From 4df75ac7460880011c627ebb58d48ddb2f15dd52 Mon Sep 17 00:00:00 2001 From: Matt Connew Date: Mon, 3 Jun 2019 12:42:27 -0700 Subject: [PATCH] Initial code for release (#5) * Initial prototype of WCF Server built on top of ASP.NET Core * Namespace rename of Microsoft.ServiceModel -> CoreWCF * Added the new code samples. * Added Readme, contributing, and license files * Fixed the warnings related to unused local variables and wrong pragma directives * Fixinf the coments * Fixed last comment --- .gitattributes | 67 + .gitignore | 225 +- CONTRIBUTING.md | 69 + Directory.Build.props | 44 + Directory.Build.targets | 12 + LICENSE | 9 + README.md | 25 +- resources.props | 24 + resources.targets | 94 + .../ChannelBindingMessagePropertyHelper.cs | 56 + .../CoreWCF/Channels/ChannelBindingUtility.cs | 61 + .../src/CoreWCF/Channels/DelegatingStream.cs | 129 + src/Common/src/CoreWCF/Channels/DnsCache.cs | 130 + .../CoreWCF/Channels/MaxMessageSizeStream.cs | 111 + .../CoreWCF/Channels/RequestContextBase.cs | 311 + .../src/CoreWCF/Channels/UriPrefixTable.cs | 529 ++ .../CoreWCF/Collections/Generic/MruCache.cs | 170 + .../CoreWCF/HostNameComparisonModeHelper.cs | 25 + .../src/CoreWCF/ProtocolExceptionHelper.cs | 34 + src/Common/src/CoreWCF/Runtime/AsyncLock.cs | 130 + .../Runtime/Collections/HopperCache.cs | 245 + .../src/CoreWCF/Runtime/ExceptionTrace.cs | 164 + .../src/CoreWCF/Runtime/FatalException.cs | 27 + src/Common/src/CoreWCF/Runtime/Fx.cs | 455 + .../src/CoreWCF/Runtime/IOThreadScheduler.cs | 624 ++ .../src/CoreWCF/Runtime/IOThreadTimer.cs | 641 ++ ...coverableTimeoutCancellationTokenSource.cs | 131 + .../ServiceModelSynchronizationContext.cs | 16 + src/Common/src/CoreWCF/Runtime/TaskHelpers.cs | 515 ++ src/Common/src/CoreWCF/Runtime/Ticks.cs | 57 + .../src/CoreWCF/Runtime/TimeoutHelper.cs | 332 + src/Common/src/CoreWCF/Runtime/TraceSR.cs | 21 + src/Common/src/CoreWCF/SR.cs | 100 + src/Common/src/CoreWCF/TransferModeHelper.cs | 34 + src/CoreWCF.Http/src/CoreWCF.Http.csproj | 14 + .../ActionMismatchAddressingException.cs | 51 + .../src/CoreWCF/BasicHttpBinding.cs | 55 + .../src/CoreWCF/CallbackException.cs | 23 + .../Channels/AddressingVersionExtensions.cs | 64 + .../Channels/AspNetCoreReplyChannel.cs | 82 + .../src/CoreWCF/Channels/BinaryVersion.cs | 24 + .../CoreWCF/Channels/ContentOnlyMessage.cs | 106 + .../src/CoreWCF/Channels/DetectEofStream.cs | 72 + .../src/CoreWCF/Channels/FramingFormat.cs | 16 + .../Channels/HttpAnonymousUriPrefixMatcher.cs | 61 + .../CoreWCF/Channels/HttpChannelHelpers.cs | 2006 +++++ .../src/CoreWCF/Channels/HttpReplyChannel.cs | 76 + .../CoreWCF/Channels/HttpRequestContext.cs | 573 ++ .../Channels/HttpRequestMessageProperty.cs | 48 + .../Channels/HttpResponseMessageProperty.cs | 85 + .../Channels/HttpTransportBindingElement.cs | 123 + .../CoreWCF/Channels/HttpTransportSettings.cs | 21 + .../Channels/ITransportFactorySettings.cs | 10 + .../MessageEncoderCompressionHandler.cs | 8 + .../Channels/RequestDelegateHandler.cs | 79 + .../Channels/ServiceModelHttpHeaders.cs | 10 + .../Channels/ServiceModelHttpMiddleware.cs | 89 + .../src/CoreWCF/Channels/TransportDefaults.cs | 26 + .../src/CoreWCF/DiagnosticUtility.cs | 188 + .../src/CoreWCF/Diagnostics/TraceUtility.cs | 62 + .../CoreWCF/DummyTransportBindingElement.cs | 18 + .../src/CoreWCF/HttpBindingBase.cs | 99 + .../Runtime/Diagnostics/EtwDiagnosticTrace.cs | 74 + .../src/CoreWCF/Runtime/UrlUtility.cs | 175 + .../src/CoreWCF/ServiceModelDictionary.cs | 111 + .../src/CoreWCF/ServiceModelStrings.cs | 12 + .../CoreWCF/ServiceModelStringsVersion1.cs | 998 ++ .../WSAddressing10ProblemHeaderQNameFault.cs | 149 + .../src/CoreWCF/WSMessageEncoding.cs | 7 + src/CoreWCF.Http/src/CoreWCF/XD.cs | 64 + src/CoreWCF.Http/src/Resources/Strings.resx | 7941 ++++++++++++++++ .../tests/ClientContract/IEchoService.cs | 34 + .../tests/CoreWCF.Http.Tests.csproj | 25 + src/CoreWCF.Http/tests/ISimpleService.cs | 11 + .../tests/Properties/launchSettings.json | 27 + .../tests/ServiceContract/IEchoService.cs | 33 + .../tests/Services/EchoService.cs | 35 + src/CoreWCF.Http/tests/SimpleService.cs | 11 + src/CoreWCF.Http/tests/SimpleTest.cs | 33 + src/CoreWCF.Http/tests/Startup.cs | 36 + src/CoreWCF.NetTcp/src/CoreWCF.NetTcp.csproj | 21 + .../Channels/AddressingVersionExtensions.cs | 65 + .../Channels/BufferedConnectionListener.cs | 301 + .../src/CoreWCF/Channels/Connection.cs | 994 ++ .../CoreWCF/Channels/ConnectionAcceptor.cs | 215 + .../CoreWCF/Channels/ConnectionBufferPool.cs | 71 + .../src/CoreWCF/Channels/ConnectionDemuxer.cs | 436 + .../CoreWCF/Channels/ConnectionModeReader.cs | 219 + ...nnectionOrientedTransportBindingElement.cs | 313 + ...nectionOrientedTransportChannelListener.cs | 396 + .../ConnectionOrientedTransportManager.cs | 224 + .../CoreWCF/Channels/DuplexRequestContext.cs | 38 + .../Channels/ExclusiveTcpTransportManager.cs | 266 + .../Framing/ConnectionContextExtensions.cs | 40 + .../Framing/DuplexFramingMiddleware.cs | 71 + .../Channels/Framing/FramingConnection.cs | 42 + .../Framing/FramingConnectionContext.cs | 45 + .../Channels/Framing/FramingDecoder.cs | 1682 ++++ .../Framing/FramingModeHandshakeMiddleware.cs | 25 + .../NetMessageFramingConnectionHandler.cs | 145 + .../ServerFramingDuplexSessionChannel.cs | 803 ++ .../ServerFramingDuplexSessionMiddleware.cs | 189 + ...ServerSessionConnectionReaderMiddleware.cs | 220 + .../SingletonFramingFramingMiddleware.cs | 20 + .../Channels/Framing/StreamDuplexPipe.cs | 156 + .../src/CoreWCF/Channels/FramingChannel.cs | 256 + .../src/CoreWCF/Channels/FramingDecoder.cs | 1631 ++++ .../src/CoreWCF/Channels/FramingEncoder.cs | 366 + .../src/CoreWCF/Channels/FramingFormat.cs | 179 + ...nectionOrientedTransportFactorySettings.cs | 30 + .../src/CoreWCF/Channels/IMessageSource.cs | 20 + .../IStreamUpgradeChannelBindingProvider.cs | 12 + .../ImmutableCommunicationTimeouts.cs | 43 + .../Channels/InitialServerConnectionReader.cs | 282 + .../Channels/NetFramingTransportSettings.cs | 20 + .../src/CoreWCF/Channels/OutputChannel.cs | 67 + .../src/CoreWCF/Channels/QueuedObjectPool.cs | 115 + .../src/CoreWCF/Channels/ReplyChannel.cs | 115 + .../CoreWCF/Channels/ReplyChannelAcceptor.cs | 23 + .../Channels/SessionConnectionReader.cs | 818 ++ .../Channels/SingletonChannelAcceptor.cs | 224 + .../Channels/SingletonConnectionReader.cs | 1216 +++ .../Channels/SocketAsyncEventArgsPool.cs | 84 + .../src/CoreWCF/Channels/SocketConnection.cs | 1655 ++++ .../Channels/SynchronizedMessageSource.cs | 66 + .../CoreWCF/Channels/TcpChannelListener.cs | 343 + .../Channels/TcpTransportBindingElement.cs | 132 + .../CoreWCF/Channels/TcpTransportManager.cs | 23 + .../src/CoreWCF/Channels/TimeoutStream.cs | 100 + .../Channels/TransportChannelListener.cs | 611 ++ .../src/CoreWCF/Channels/TransportDefaults.cs | 69 + .../Channels/TransportDuplexSessionChannel.cs | 568 ++ .../src/CoreWCF/Channels/TransportManager.cs | 256 + .../Channels/TransportOutputChannel.cs | 163 + .../Channels/TransportReplyChannelAcceptor.cs | 107 + .../Channels/TransportSecurityHelpers.cs | 30 + .../src/CoreWCF/Channels/UriGenerator.cs | 39 + .../src/CoreWCF/Channels/UriHelper.cs | 37 + .../FramingConnectionHandshakeBuilder.cs | 77 + .../IFramingConnectionHandshakeBuilder.cs | 47 + .../CoreWCF/Configuration/MapExtensions.cs | 52 + .../CoreWCF/Configuration/MapMiddleware.cs | 51 + .../src/CoreWCF/Configuration/MapOptions.cs | 24 + .../ServiceModelWebHostBuilderExtensions.cs | 75 + ...areFramingConnectionHandshakeExtensions.cs | 184 + .../src/CoreWCF/DiagnosticUtility.cs | 188 + .../CoreWCF/Dispatcher/ErrorBehaviorHelper.cs | 53 + .../Dispatcher/ExceptionHandlerHelper.cs | 46 + .../src/CoreWCF/NetTcpBinding.cs | 175 + .../src/CoreWCF/NetTcpSecurity.cs | 65 + .../CoreWCF/Runtime/AsyncCompletionResult.cs | 19 + .../CoreWCF/Runtime/BackoffTimeoutHelper.cs | 134 + .../src/CoreWCF/Runtime/CallbackException.cs | 23 + .../Runtime/Diagnostics/EtwDiagnosticTrace.cs | 74 + .../CoreWCF/Security/ProtectionLevelHelper.cs | 66 + .../CoreWCF/Security/SslProtocolsHelper.cs | 30 + .../src/CoreWCF/SecurityModeHelper.cs | 17 + .../src/CoreWCF/ServiceDefaults.cs | 27 + .../src/CoreWCF/TcpClientCredentialType.cs | 19 + .../src/CoreWCF/TcpTransportSecurity.cs | 119 + .../src/CoreWCF/TraceUtility.cs | 27 + src/CoreWCF.NetTcp/src/Resources/Strings.resx | 7944 ++++++++++++++++ src/CoreWCF.NetTcp/tests/BaseStartup.cs | 38 + src/CoreWCF.NetTcp/tests/BasicServiceTest.cs | 123 + .../tests/ClientContract/IEchoService.cs | 34 + .../tests/ConnectionHandlerTests.cs | 61 + .../tests/CoreWCF.NetTcp.Tests.csproj | 29 + .../tests/ServiceContract/IEchoService.cs | 33 + src/CoreWCF.NetTcp/tests/ServiceHelper.cs | 17 + .../tests/Services/EchoService.cs | 35 + .../tests/XunitTestAssemblyAttribute.cs | 4 + .../src/CoreWCF.Primitives.csproj | 16 + .../ActionMismatchAddressingException.cs | 50 + .../CoreWCF/ActionNotSupportedException.cs | 25 + .../CoreWCF/AddressAlreadyInUseException.cs | 14 + .../src/CoreWCF/AddressFilterMode.cs | 20 + .../src/CoreWCF/CallbackBehaviorAttribute.cs | 13 + .../src/CoreWCF/ChannelTerminatedException.cs | 13 + .../src/CoreWCF/Channels/AddressHeader.cs | 379 + .../Channels/AddressHeaderCollection.cs | 188 + .../src/CoreWCF/Channels/Addressing.cs | 917 ++ .../src/CoreWCF/Channels/AddressingVersion.cs | 69 + .../Channels/AuthenticationSchemesHelper.cs | 71 + .../CoreWCF/Channels/BaseUriWithWildcard.cs | 324 + .../Channels/BinaryMessageEncoderFactory.cs | 1815 ++++ .../BinaryMessageEncodingBindingElement.cs | 281 + .../src/CoreWCF/Channels/BinaryVersion.cs | 27 + .../src/CoreWCF/Channels/Binding.cs | 258 + .../src/CoreWCF/Channels/BindingContext.cs | 152 + .../src/CoreWCF/Channels/BindingElement.cs | 47 + .../Channels/BindingElementCollection.cs | 145 + .../Channels/BindingParameterCollection.cs | 38 + .../src/CoreWCF/Channels/BodyWriter.cs | 141 + .../src/CoreWCF/Channels/BufferManager.cs | 107 + .../Channels/BufferManagerOutputStream.cs | 48 + .../CoreWCF/Channels/BufferedMessageData.cs | 202 + .../CoreWCF/Channels/BufferedMessageWriter.cs | 94 + .../src/CoreWCF/Channels/ChannelAcceptor.cs | 51 + .../src/CoreWCF/Channels/ChannelBase.cs | 91 + .../Channels/ChannelBindingMessageProperty.cs | 78 + .../CoreWCF/Channels/ChannelListenerBase.cs | 158 + .../CoreWCF/Channels/ChannelManagerBase.cs | 50 + .../CoreWCF/Channels/ChannelRequirements.cs | 379 + .../CoreWCF/Channels/CommunicationObject.cs | 799 ++ .../Channels/CommunicationObjectManager.cs | 92 + .../src/CoreWCF/Channels/CompressionFormat.cs | 20 + .../CoreWCF/Channels/ContentOnlyMessage.cs | 100 + .../CorrelationDataMessageProperty.cs | 165 + .../src/CoreWCF/Channels/CustomBinding.cs | 118 + .../src/CoreWCF/Channels/EncoderHelpers.cs | 58 + .../src/CoreWCF/Channels/FaultConverter.cs | 239 + .../src/CoreWCF/Channels/FramingDecoder.cs | 921 ++ .../src/CoreWCF/Channels/FramingFormat.cs | 178 + .../Channels/IAnonymousUriPrefixMatcher.cs | 9 + .../src/CoreWCF/Channels/IChannel.cs | 7 + .../src/CoreWCF/Channels/IChannelAcceptor.cs | 13 + .../Channels/IChannelBindingProvider.cs | 13 + .../src/CoreWCF/Channels/IChannelListener.cs | 21 + .../Channels/ICompressedMessageEncoder.cs | 14 + .../src/CoreWCF/Channels/ICorrelatorKey.cs | 10 + .../src/CoreWCF/Channels/IDuplexChannel.cs | 6 + .../src/CoreWCF/Channels/IDuplexSession.cs | 8 + .../CoreWCF/Channels/IDuplexSessionChannel.cs | 6 + .../src/CoreWCF/Channels/IInputChannel.cs | 11 + .../src/CoreWCF/Channels/IInputSession.cs | 6 + .../CoreWCF/Channels/IInputSessionChannel.cs | 6 + .../src/CoreWCF/Channels/IMessageProperty.cs | 7 + .../src/CoreWCF/Channels/IOutputChannel.cs | 10 + .../src/CoreWCF/Channels/IOutputSession.cs | 6 + .../CoreWCF/Channels/IOutputSessionChannel.cs | 6 + .../src/CoreWCF/Channels/IReplyChannel.cs | 14 + .../CoreWCF/Channels/IReplySessionChannel.cs | 6 + .../src/CoreWCF/Channels/IRequestChannel.cs | 10 + .../Channels/IRequestReplyCorrelator.cs | 15 + .../Channels/IRequestSessionChannel.cs | 6 + .../CoreWCF/Channels/ISecurityCapabilities.cs | 13 + .../src/CoreWCF/Channels/ISecuritySession.cs | 11 + .../src/CoreWCF/Channels/ISession.cs | 7 + .../src/CoreWCF/Channels/ISessionChannel.cs | 7 + .../IStreamUpgradeChannelBindingProvider.cs | 12 + .../Channels/ITransportFactorySettings.cs | 16 + .../ITransportTokenAssertionProvider.cs | 9 + .../src/CoreWCF/Channels/InputChannel.cs | 97 + .../src/CoreWCF/Channels/InputQueueChannel.cs | 142 + .../Channels/InputQueueChannelAcceptor.cs | 99 + .../src/CoreWCF/Channels/IntEncoder.cs | 41 + .../Channels/LayeredChannelListener.cs | 222 + .../src/CoreWCF/Channels/LifetimeManager.cs | 329 + .../src/CoreWCF/Channels/Message.cs | 2039 +++++ .../src/CoreWCF/Channels/MessageBuffer.cs | 336 + .../src/CoreWCF/Channels/MessageEncoder.cs | 204 + .../MessageEncoderCompressionHandler.cs | 89 + .../CoreWCF/Channels/MessageEncoderFactory.cs | 24 + .../Channels/MessageEncodingBindingElement.cs | 99 + .../src/CoreWCF/Channels/MessageFault.cs | 650 ++ .../src/CoreWCF/Channels/MessageHeader.cs | 595 ++ .../src/CoreWCF/Channels/MessageHeaderInfo.cs | 13 + .../src/CoreWCF/Channels/MessageHeaders.cs | 1776 ++++ .../src/CoreWCF/Channels/MessageProperties.cs | 889 ++ .../src/CoreWCF/Channels/MessageState.cs | 11 + .../src/CoreWCF/Channels/MessageVersion.cs | 156 + .../src/CoreWCF/Channels/MethodCall.cs | 25 + .../src/CoreWCF/Channels/ReceiveContext.cs | 487 + .../CoreWCF/Channels/ReceiveContextState.cs | 12 + .../Channels/RemoteEndpointMessageProperty.cs | 130 + .../src/CoreWCF/Channels/RequestContext.cs | 22 + .../Channels/RequestReplyCorrelator.cs | 242 + .../CoreWCF/Channels/SecurityCapabilities.cs | 74 + .../src/CoreWCF/Channels/ServiceChannel.cs | 1780 ++++ .../CoreWCF/Channels/ServiceChannelFactory.cs | 54 + .../CoreWCF/Channels/ServiceChannelProxy.cs | 706 ++ .../Channels/SessionOpenNotification.cs | 12 + .../Channels/SingletonChannelAcceptor.cs | 222 + .../SslStreamSecurityBindingElement.cs | 148 + .../SslStreamSecurityUpgradeProvider.cs | 367 + .../Channels/StreamSecurityUpgradeAcceptor.cs | 16 + .../StreamSecurityUpgradeAcceptorBase.cs | 51 + .../Channels/StreamSecurityUpgradeProvider.cs | 24 + .../CoreWCF/Channels/StreamUpgradeAcceptor.cs | 19 + .../Channels/StreamUpgradeBindingElement.cs | 20 + .../CoreWCF/Channels/StreamUpgradeProvider.cs | 48 + .../Channels/TextMessageEncoderFactory.cs | 778 ++ .../TextMessageEncodingBindingElement.cs | 206 + .../Channels/TransportBindingElement.cs | 143 + .../src/CoreWCF/Channels/TransportDefaults.cs | 163 + .../Channels/TransportSecurityHelpers.cs | 153 + .../src/CoreWCF/Channels/UnderstoodHeaders.cs | 50 + .../src/CoreWCF/Channels/UriEx.cs | 10 + .../WindowsStreamSecurityBindingElement.cs | 69 + .../WindowsStreamSecurityUpgradeProvider.cs | 192 + .../Generic/KeyedByTypeCollection.cs | 122 + .../Generic/SynchronizedCollection.cs | 313 + .../Generic/SynchronizedKeyedCollection.cs | 268 + .../src/CoreWCF/CommunicationException.cs | 14 + .../CommunicationObjectAbortedException.cs | 12 + .../CommunicationObjectFaultedException.cs | 12 + .../src/CoreWCF/CommunicationState.cs | 12 + .../src/CoreWCF/ConcurrencyMode.cs | 20 + .../Configuration/DispatcherBuilderImpl.cs | 24 + .../Configuration/IDispatcherBuilder.cs | 11 + .../src/CoreWCF/Configuration/IService.cs | 10 + .../CoreWCF/Configuration/IServiceBuilder.cs | 22 + .../Configuration/IServiceConfiguration.cs | 19 + .../Configuration/IServiceDispatcher.cs | 16 + .../CoreWCF/Configuration/ServiceBuilder.cs | 100 + .../Configuration/ServiceConfiguration.cs | 36 + .../ServiceEndpointConfiguration.cs | 15 + ...erviceModelApplicationBuilderExtensions.cs | 57 + ...ServiceModelServiceCollectionExtensions.cs | 31 + src/CoreWCF.Primitives/src/CoreWCF/DBNull.cs | 14 + .../CoreWCF/DataContractFormatAttribute.cs | 19 + .../src/CoreWCF/Description/ConfigLoader.cs | 41 + .../Description/ContractDescription.cs | 209 + .../Description/CustomAttributeProvider.cs | 138 + ...DataContractSerializerOperationBehavior.cs | 164 + .../CoreWCF/Description/DispatcherBuilder.cs | 1079 +++ .../CoreWCF/Description/FaultDescription.cs | 65 + .../Description/FaultDescriptionCollection.cs | 7 + .../CoreWCF/Description/IContractBehavior.cs | 10 + .../Description/IContractBehaviorAttribute.cs | 12 + .../CoreWCF/Description/IContractResolver.cs | 7 + .../CoreWCF/Description/IEndpointBehavior.cs | 10 + .../CoreWCF/Description/IOperationBehavior.cs | 13 + .../CoreWCF/Description/IServiceBehavior.cs | 9 + .../src/CoreWCF/Description/ListenUriMode.cs | 17 + .../Description/MessageBodyDescription.cs | 43 + .../CoreWCF/Description/MessageDescription.cs | 158 + .../MessageDescriptionCollection.cs | 9 + .../CoreWCF/Description/MessageDirection.cs | 21 + .../Description/MessageHeaderDescription.cs | 71 + .../MessageHeaderDescriptionCollection.cs | 8 + .../Description/MessagePartDescription.cs | 120 + .../MessagePartDescriptionCollection.cs | 8 + .../Description/MessagePropertyAttribute.cs | 36 + .../Description/MessagePropertyDescription.cs | 7 + .../MessagePropertyDescriptionCollection.cs | 8 + .../Description/OperationDescription.cs | 213 + .../OperationDescriptionCollection.cs | 50 + .../CoreWCF/Description/ServiceCredentials.cs | 204 + .../CoreWCF/Description/ServiceDescription.cs | 288 + .../CoreWCF/Description/ServiceEndpoint.cs | 252 + .../Description/ServiceEndpointCollection.cs | 175 + .../CoreWCF/Description/ServiceReflector.cs | 1003 +++ .../TaskOperationDescriptionValidator.cs | 71 + .../src/CoreWCF/Description/TypeLoader.cs | 2064 +++++ .../XmlSerializerOperationBehavior.cs | 1049 +++ .../src/CoreWCF/DiagnosticUtility.cs | 193 + .../src/CoreWCF/Diagnostics/TraceUtility.cs | 62 + .../CoreWCF/Dispatcher/ActionMessageFilter.cs | 115 + .../Dispatcher/ActionMessageFilterTable.cs | 496 + .../CoreWCF/Dispatcher/AndMessageFilter.cs | 82 + .../Dispatcher/AndMessageFilterTable.cs | 481 + .../CoreWCF/Dispatcher/AsyncMethodInvoker.cs | 161 + .../Dispatcher/AuthorizationBehavior.cs | 23 + .../Dispatcher/BufferedReceiveBinder.cs | 167 + .../CoreWCF/Dispatcher/ChannelDispatcher.cs | 555 ++ .../Dispatcher/ChannelDispatcherBase.cs | 30 + .../Dispatcher/ChannelDispatcherCollection.cs | 56 + .../src/CoreWCF/Dispatcher/ChannelHandler.cs | 1511 ++++ .../src/CoreWCF/Dispatcher/ClientOperation.cs | 283 + .../src/CoreWCF/Dispatcher/ClientRuntime.cs | 561 ++ .../CoreWCF/Dispatcher/ConcurrencyBehavior.cs | 298 + .../DataContractSerializerFaultFormatter.cs | 19 + ...ataContractSerializerOperationFormatter.cs | 587 ++ .../DataContractSerializerServiceBehavior.cs | 96 + .../CoreWCF/Dispatcher/DispatchOperation.cs | 238 + .../Dispatcher/DispatchOperationRuntime.cs | 672 ++ .../src/CoreWCF/Dispatcher/DispatchRuntime.cs | 976 ++ .../CoreWCF/Dispatcher/DuplexChannelBinder.cs | 960 ++ .../EndpointAddressMessageFilter.cs | 322 + .../EndpointAddressMessageFilterTable.cs | 813 ++ .../Dispatcher/EndpointAddressProcessor.cs | 353 + .../CoreWCF/Dispatcher/EndpointDispatcher.cs | 331 + .../Dispatcher/EndpointDispatcherTable.cs | 167 + .../Dispatcher/EndpointFilterProvider.cs | 49 + .../src/CoreWCF/Dispatcher/ErrorBehavior.cs | 289 + .../Dispatcher/ErrorHandlerFaultInfo.cs | 36 + .../Dispatcher/ErrorHandlingAcceptor.cs | 126 + .../Dispatcher/ErrorHandlingReceiver.cs | 112 + .../CoreWCF/Dispatcher/ExceptionHandler.cs | 85 + .../CoreWCF/Dispatcher/FaultContractInfo.cs | 70 + .../src/CoreWCF/Dispatcher/FaultFormatter.cs | 207 + .../Dispatcher/ICallContextInitializer.cs | 10 + .../src/CoreWCF/Dispatcher/IChannelBinder.cs | 23 + .../CoreWCF/Dispatcher/IChannelInitializer.cs | 7 + .../Dispatcher/IClientFaultFormatter.cs | 9 + .../Dispatcher/IClientMessageFormatter.cs | 10 + .../Dispatcher/IClientMessageInspector.cs | 10 + .../Dispatcher/IClientOperationSelector.cs | 10 + .../Dispatcher/IDispatchFaultFormatter.cs | 19 + .../Dispatcher/IDispatchMessageFormatter.cs | 10 + .../Dispatcher/IDispatchMessageInspector.cs | 10 + .../Dispatcher/IDispatchOperationSelector.cs | 9 + .../src/CoreWCF/Dispatcher/IErrorHandler.cs | 11 + .../Dispatcher/IInputSessionShutdown.cs | 8 + .../Dispatcher/IInstanceContextInitializer.cs | 10 + .../Dispatcher/IInstanceContextProvider.cs | 98 + .../CoreWCF/Dispatcher/IInstanceProvider.cs | 11 + .../Dispatcher/IInvokeReceivedNotification.cs | 10 + .../src/CoreWCF/Dispatcher/IListenerBinder.cs | 14 + .../CoreWCF/Dispatcher/IMessageFilterTable.cs | 23 + .../CoreWCF/Dispatcher/IOperationInvoker.cs | 18 + .../CoreWCF/Dispatcher/IParameterInspector.cs | 8 + .../CoreWCF/Dispatcher/IResumeMessageRpc.cs | 16 + .../Dispatcher/ImmutableClientRuntime.cs | 232 + .../ImmutableCommunicationTimeouts.cs | 55 + .../Dispatcher/ImmutableDispatchRuntime.cs | 1299 +++ .../CoreWCF/Dispatcher/InputChannelBinder.cs | 136 + .../CoreWCF/Dispatcher/InstanceBehavior.cs | 292 + .../Dispatcher/InstanceContextManager.cs | 245 + .../src/CoreWCF/Dispatcher/InvokerUtil.cs | 158 + .../src/CoreWCF/Dispatcher/ListenerBinder.cs | 233 + .../src/CoreWCF/Dispatcher/ListenerHandler.cs | 448 + .../Dispatcher/MatchAllMessageFilter.cs | 33 + .../Dispatcher/MatchNoneMessageFilter.cs | 32 + .../src/CoreWCF/Dispatcher/MessageFilter.cs | 311 + .../CoreWCF/Dispatcher/MessageFilterTable.cs | 657 ++ .../Dispatcher/MessageOperationFormatter.cs | 75 + .../src/CoreWCF/Dispatcher/MessageRpc.cs | 576 ++ .../MultipleFilterMatchesException.cs | 53 + .../Dispatcher/MultipleReceiveBinder.cs | 135 + .../Dispatcher/NetDispatcherFaultException.cs | 16 + .../CoreWCF/Dispatcher/OperationFormatter.cs | 821 ++ .../Dispatcher/OperationInvokerBehavior.cs | 72 + .../Dispatcher/OperationSelectorBehavior.cs | 83 + .../PerCallInstanceContextProvider.cs | 40 + .../PerSessionInstanceContextProvider.cs | 52 + .../PrefixEndpointAddressMessageFilter.cs | 96 + ...PrefixEndpointAddressMessageFilterTable.cs | 156 + .../Dispatcher/PrimitiveOperationFormatter.cs | 974 ++ .../Dispatcher/ProxyOperationRuntime.cs | 418 + .../src/CoreWCF/Dispatcher/ProxyRpc.cs | 62 + .../src/CoreWCF/Dispatcher/QueryUtil.cs | 599 ++ .../ReceiveContextAcknowledgementMode.cs | 9 + .../CoreWCF/Dispatcher/ReplyChannelBinder.cs | 102 + .../CoreWCF/Dispatcher/ServiceDispatcher.cs | 107 + .../CoreWCF/Dispatcher/SharedRuntimeState.cs | 41 + .../SingletonInstanceContextProvider.cs | 92 + .../src/CoreWCF/Dispatcher/StreamFormatter.cs | 423 + .../CoreWCF/Dispatcher/SyncMethodInvoker.cs | 212 + .../SynchronizedChannelCollection.cs | 81 + .../CoreWCF/Dispatcher/TaskMethodInvoker.cs | 335 + .../TerminatingOperationBehavior.cs | 59 + .../src/CoreWCF/Dispatcher/ThreadBehavior.cs | 33 + .../ThreadSafeMessageFilterTable.cs | 275 + .../UniqueContractNameValidationBehavior.cs | 49 + .../Dispatcher/XmlSerializerFaultFormatter.cs | 110 + .../XmlSerializerObjectSerializer.cs | 121 + .../XmlSerializerOperationFormatter.cs | 528 ++ .../src/CoreWCF/DnsEndpointIdentity.cs | 36 + .../src/CoreWCF/EmptyArray.cs | 52 + .../src/CoreWCF/EndpointAddress.cs | 1079 +++ .../src/CoreWCF/EndpointIdentity.cs | 220 + .../src/CoreWCF/EndpointNotFoundException.cs | 13 + .../src/CoreWCF/EnvelopeVersion.cs | 109 + .../src/CoreWCF/ExceptionDetail.cs | 69 + .../src/CoreWCF/ExceptionMapper.cs | 76 + .../src/CoreWCF/ExtensionCollection.cs | 114 + .../src/CoreWCF/FaultCode.cs | 124 + .../src/CoreWCF/FaultCodeConstants.cs | 32 + .../src/CoreWCF/FaultContractAttribute.cs | 63 + .../src/CoreWCF/FaultException.cs | 276 + .../src/CoreWCF/FaultReason.cs | 127 + .../src/CoreWCF/FaultReasonText.cs | 53 + .../src/CoreWCF/GeneralEndpointIdentity.cs | 15 + .../src/CoreWCF/HostNameComparisonMode.cs | 11 + .../src/CoreWCF/IClientChannel.cs | 10 + .../src/CoreWCF/ICommunicationObject.cs | 28 + .../src/CoreWCF/IContextChannel.cs | 16 + .../CoreWCF/IDefaultCommunicationTimeouts.cs | 12 + .../src/CoreWCF/IDuplexContextChannel.cs | 13 + .../src/CoreWCF/IExtensibleObject.cs | 7 + .../src/CoreWCF/IExtension.cs | 8 + .../src/CoreWCF/IExtensionCollection.cs | 8 + .../IOperationContractAttributeProvider.cs | 7 + .../src/CoreWCF/IServiceChannel.cs | 10 + .../src/CoreWCF/IdentityModel/Claims/Claim.cs | 201 + .../IdentityModel/Claims/ClaimComparer.cs | 348 + .../CoreWCF/IdentityModel/Claims/ClaimSet.cs | 112 + .../IdentityModel/Claims/ClaimTypes.cs | 75 + .../IdentityModel/Claims/DefaultClaimSet.cs | 104 + .../CoreWCF/IdentityModel/Claims/Rights.cs | 14 + .../IdentityModel/Claims/WindowsClaimSet.cs | 247 + .../Claims/X509CertificateClaimSet.cs | 525 ++ .../IdentityModel/Claims/XsiConstants.cs | 8 + .../IdentityModel/IdentityModelStrings.cs | 12 + .../IdentityModelStringsVersion1.cs | 583 ++ .../Policy/AuthorizationContext.cs | 22 + .../IdentityModel/Policy/EvaluationContext.cs | 17 + .../Policy/IAuthorizationComponent.cs | 7 + .../Policy/IAuthorizationPolicy.cs | 15 + .../Policy/UnconditionalPolicy.cs | 252 + .../SecurityKeyIdentifierClause.cs | 71 + .../CoreWCF/IdentityModel/SecurityUniqueId.cs | 47 + .../CoreWCF/IdentityModel/SecurityUtils.cs | 253 + ...ustomUserNameSecurityTokenAuthenticator.cs | 52 + .../RsaSecurityTokenAuthenticator.cs | 35 + .../Selectors/SecurityTokenAuthenticator.cs | 49 + .../Selectors/SecurityTokenManager.cs | 13 + .../Selectors/SecurityTokenProvider.cs | 118 + .../Selectors/SecurityTokenRequirement.cs | 170 + .../Selectors/SecurityTokenResolver.cs | 28 + .../Selectors/SecurityTokenSerializer.cs | 248 + .../Selectors/SecurityTokenVersion.cs | 12 + .../Selectors/UserNamePasswordValidator.cs | 32 + .../UserNameSecurityTokenAuthenticator.cs | 29 + .../WindowsSecurityTokenAuthenticator.cs | 36 + .../Selectors/X509CertificateChain.cs | 11 + .../Selectors/X509CertificateValidator.cs | 257 + .../X509SecurityTokenAuthenticator.cs | 99 + .../Selectors/X509SecurityTokenProvider.cs | 71 + .../IdentityModel/Tokens/RsaSecurityToken.cs | 62 + .../Tokens/SecurityAlgorithms.cs | 50 + .../IdentityModel/Tokens/SecurityKey.cs | 16 + .../Tokens/SecurityKeyIdentifier.cs | 148 + .../IdentityModel/Tokens/SecurityKeyType.cs | 36 + .../IdentityModel/Tokens/SecurityKeyUsage.cs | 32 + .../IdentityModel/Tokens/SecurityToken.cs | 15 + .../Tokens/SecurityTokenException.cs | 30 + .../Tokens/SecurityTokenTypes.cs | 28 + .../SecurityTokenValidationException.cs | 31 + .../Tokens/USerNameSecurityToken.cs | 66 + .../Tokens/WindowsSecurityToken.cs | 95 + .../IdentityModel/Tokens/X509SecurityToken.cs | 120 + .../Tokens/X509WindowsSecurityToken.cs | 82 + .../src/CoreWCF/IdentityModel/XD.cs | 48 + .../src/CoreWCF/InstanceContext.cs | 472 + .../src/CoreWCF/InstanceContextMode.cs | 20 + .../InvalidMessageContractException.cs | 28 + .../src/CoreWCF/MessageBodyMemberAttribute.cs | 25 + .../src/CoreWCF/MessageContractAttribute.cs | 74 + .../CoreWCF/MessageContractMemberAttribute.cs | 89 + .../CoreWCF/MessageHeaderArrayAttribute.cs | 9 + .../src/CoreWCF/MessageHeaderAttribute.cs | 43 + .../src/CoreWCF/MessageHeaderException.cs | 70 + .../src/CoreWCF/MessageHeaderT.cs | 184 + .../src/CoreWCF/MessageParameterAttribute.cs | 34 + .../CoreWCF/MustUnderstandSoapException.cs | 91 + .../src/CoreWCF/OperationBehaviorAttribute.cs | 49 + .../src/CoreWCF/OperationContext.cs | 336 + .../src/CoreWCF/OperationContractAttribute.cs | 100 + .../src/CoreWCF/OperationFormatStyle.cs | 19 + .../src/CoreWCF/OperationFormatUse.cs | 19 + src/CoreWCF.Primitives/src/CoreWCF/Pool.cs | 52 + .../src/CoreWCF/ProtocolException.cs | 40 + .../src/CoreWCF/QuotaExceededException.cs | 23 + .../src/CoreWCF/Runtime/ActionItem.cs | 331 + .../CoreWCF/Runtime/AsyncManualResetEvent.cs | 86 + .../CoreWCF/Runtime/BackoffTimeoutHelper.cs | 134 + .../CoreWCF/Runtime/BufferedOutputStream.cs | 324 + .../src/CoreWCF/Runtime/CallbackException.cs | 23 + .../Runtime/Diagnostics/EtwDiagnosticTrace.cs | 74 + .../Diagnostics/SecurityTraceRecordHelper.cs | 29 + .../src/CoreWCF/Runtime/InputQueue.cs | 863 ++ .../CoreWCF/Runtime/InternalBufferManager.cs | 519 ++ .../CoreWCF/Runtime/ReflectionExtensions.cs | 64 + .../Runtime/Serialization/XmlMappingTypes.cs | 446 + .../src/CoreWCF/Runtime/SignalGate.cs | 119 + .../src/CoreWCF/Runtime/SynchronizedPool.cs | 429 + .../Security/ChannelProtectionRequirements.cs | 328 + .../Security/IEndpointIdentityProvider.cs | 12 + .../src/CoreWCF/Security/IdentityVerifier.cs | 328 + .../Security/MessagePartSpecification.cs | 227 + .../Security/MessageSecurityException.cs | 58 + .../ScopedMessagePartSpecification.cs | 204 + .../Security/SecurityAlgorithmSuite.cs | 804 ++ .../Security/SecurityCredentialsManager.cs | 15 + .../Security/SecurityMessageProperty.cs | 318 + .../Security/SecurityNegotiationException.cs | 31 + .../Security/SecurityTokenSpecification.cs | 35 + .../src/CoreWCF/Security/SecurityUtils.cs | 632 ++ .../ServiceCredentialsSecurityTokenManager.cs | 526 ++ .../Security/SspiSecurityTokenProvider.cs | 36 + ...torServiceModelSecurityTokenRequirement.cs | 83 + ...entServiceModelSecurityTokenRequirement.cs | 69 + .../ServiceModelSecurityTokenRequirement.cs | 239 + .../Tokens/ServiceModelSecurityTokenTypes.cs | 24 + .../ServiceX509SecurityTokenProvider.cs | 27 + .../Security/Tokens/SspiSecurityToken.cs | 62 + .../Security/Tokens/WindowsSidIdentity.cs | 61 + .../UserNamePasswordServiceCredential.cs | 175 + .../UserNamePasswordValidationMode.cs | 34 + .../Security/WindowsServiceCredential.cs | 65 + ...09CertificateInitiatorServiceCredential.cs | 84 + ...09CertificateRecipientServiceCredential.cs | 75 + .../Security/X509CertificateValidationMode.cs | 59 + .../X509ClientCertificateAuthentication.cs | 182 + .../src/CoreWCF/SecurityMode.cs | 10 + .../src/CoreWCF/ServerTooBusyException.cs | 14 + .../src/CoreWCF/ServiceActivationException.cs | 14 + .../src/CoreWCF/ServiceBehaviorAttribute.cs | 254 + .../src/CoreWCF/ServiceChannelManager.cs | 454 + .../src/CoreWCF/ServiceContractAttribute.cs | 83 + .../src/CoreWCF/ServiceDefaults.cs | 25 + .../src/CoreWCF/ServiceHostBase.cs | 370 + .../src/CoreWCF/ServiceHostObjectModel.cs | 271 + .../src/CoreWCF/ServiceKnownTypeAttribute.cs | 40 + .../CoreWCF/ServiceModelAttributeTargets.cs | 19 + .../src/CoreWCF/ServiceModelDictionary.cs | 120 + .../src/CoreWCF/ServiceModelStrings.cs | 8 + .../CoreWCF/ServiceModelStringsVersion1.cs | 995 ++ .../src/CoreWCF/ServiceRequestDelegate.cs | 20 + .../src/CoreWCF/ServiceSecurityContext.cs | 198 + .../src/CoreWCF/SessionMode.cs | 21 + .../src/CoreWCF/SpnEndpointIdentity.cs | 156 + .../src/CoreWCF/TransferMode.cs | 10 + .../src/CoreWCF/TryAsyncResult.cs | 29 + .../UnknownMessageReceivedEventArgs.cs | 20 + .../src/CoreWCF/UpnEndpointIdentity.cs | 147 + .../src/CoreWCF/UriSchemeKeyedCollection.cs | 77 + .../WSAddressing10ProblemHeaderQNameFault.cs | 147 + .../X509CertificateEndpointIdentity.cs | 92 + src/CoreWCF.Primitives/src/CoreWCF/XD.cs | 2493 +++++ .../src/CoreWCF/Xml/XmlBinaryNodeType.cs | 272 + .../src/CoreWCF/XmlBuffer.cs | 161 + .../src/CoreWCF/XmlReaderExtensions.cs | 31 + .../CoreWCF/XmlSerializerFormatAttribute.cs | 64 + src/CoreWCF.Primitives/src/CoreWCF/XmlUtil.cs | 141 + .../src/Resources/Strings.resx | 7987 +++++++++++++++++ .../tests/CoreWCF.Primitives.Tests.csproj | 22 + .../tests/DispatchBuilderTests.cs | 101 + .../tests/Helpers/MockReplyChannel.cs | 80 + .../tests/Helpers/MockServer.cs | 29 + .../Helpers/MockTransportBindingElement.cs | 14 + .../tests/Helpers/TestRequestContext.cs | 91 + .../tests/ISimpleService.cs | 13 + .../tests/Properties/launchSettings.json | 27 + src/CoreWCF.Primitives/tests/SimpleService.cs | 23 + src/NuGet.config | 13 + src/Samples/DesktopClient/App.config | 6 + .../DesktopClient/DesktopClient.csproj | 55 + src/Samples/DesktopClient/IEchoService.cs | 11 + src/Samples/DesktopClient/Program.cs | 30 + .../DesktopClient/Properties/AssemblyInfo.cs | 36 + src/Samples/DesktopServer/App.config | 6 + .../DesktopServer/DesktopServer.csproj | 56 + src/Samples/DesktopServer/EchoService.cs | 11 + src/Samples/DesktopServer/IEchoService.cs | 11 + src/Samples/DesktopServer/Program.cs | 29 + .../DesktopServer/Properties/AssemblyInfo.cs | 36 + src/Samples/NetCoreClient/IEchoService.cs | 11 + .../NetCoreClient/NetCoreClient.csproj | 14 + src/Samples/NetCoreClient/Program.cs | 30 + src/Samples/NetCoreServer/EchoService.cs | 11 + src/Samples/NetCoreServer/IEchoService.cs | 11 + .../NetCoreServer/NetCoreServer.csproj | 18 + src/Samples/NetCoreServer/Program.cs | 23 + .../Properties/launchSettings.json | 24 + src/Samples/NetCoreServer/Startup.cs | 26 + src/WcfCore.sln | 180 + 650 files changed, 134294 insertions(+), 145 deletions(-) create mode 100644 .gitattributes create mode 100644 CONTRIBUTING.md create mode 100644 Directory.Build.props create mode 100644 Directory.Build.targets create mode 100644 LICENSE create mode 100644 resources.props create mode 100644 resources.targets create mode 100644 src/Common/src/CoreWCF/Channels/ChannelBindingMessagePropertyHelper.cs create mode 100644 src/Common/src/CoreWCF/Channels/ChannelBindingUtility.cs create mode 100644 src/Common/src/CoreWCF/Channels/DelegatingStream.cs create mode 100644 src/Common/src/CoreWCF/Channels/DnsCache.cs create mode 100644 src/Common/src/CoreWCF/Channels/MaxMessageSizeStream.cs create mode 100644 src/Common/src/CoreWCF/Channels/RequestContextBase.cs create mode 100644 src/Common/src/CoreWCF/Channels/UriPrefixTable.cs create mode 100644 src/Common/src/CoreWCF/Collections/Generic/MruCache.cs create mode 100644 src/Common/src/CoreWCF/HostNameComparisonModeHelper.cs create mode 100644 src/Common/src/CoreWCF/ProtocolExceptionHelper.cs create mode 100644 src/Common/src/CoreWCF/Runtime/AsyncLock.cs create mode 100644 src/Common/src/CoreWCF/Runtime/Collections/HopperCache.cs create mode 100644 src/Common/src/CoreWCF/Runtime/ExceptionTrace.cs create mode 100644 src/Common/src/CoreWCF/Runtime/FatalException.cs create mode 100644 src/Common/src/CoreWCF/Runtime/Fx.cs create mode 100644 src/Common/src/CoreWCF/Runtime/IOThreadScheduler.cs create mode 100644 src/Common/src/CoreWCF/Runtime/IOThreadTimer.cs create mode 100644 src/Common/src/CoreWCF/Runtime/RecoverableTimeoutCancellationTokenSource.cs create mode 100644 src/Common/src/CoreWCF/Runtime/ServiceModelSynchronizationContext.cs create mode 100644 src/Common/src/CoreWCF/Runtime/TaskHelpers.cs create mode 100644 src/Common/src/CoreWCF/Runtime/Ticks.cs create mode 100644 src/Common/src/CoreWCF/Runtime/TimeoutHelper.cs create mode 100644 src/Common/src/CoreWCF/Runtime/TraceSR.cs create mode 100644 src/Common/src/CoreWCF/SR.cs create mode 100644 src/Common/src/CoreWCF/TransferModeHelper.cs create mode 100644 src/CoreWCF.Http/src/CoreWCF.Http.csproj create mode 100644 src/CoreWCF.Http/src/CoreWCF/ActionMismatchAddressingException.cs create mode 100644 src/CoreWCF.Http/src/CoreWCF/BasicHttpBinding.cs create mode 100644 src/CoreWCF.Http/src/CoreWCF/CallbackException.cs create mode 100644 src/CoreWCF.Http/src/CoreWCF/Channels/AddressingVersionExtensions.cs create mode 100644 src/CoreWCF.Http/src/CoreWCF/Channels/AspNetCoreReplyChannel.cs create mode 100644 src/CoreWCF.Http/src/CoreWCF/Channels/BinaryVersion.cs create mode 100644 src/CoreWCF.Http/src/CoreWCF/Channels/ContentOnlyMessage.cs create mode 100644 src/CoreWCF.Http/src/CoreWCF/Channels/DetectEofStream.cs create mode 100644 src/CoreWCF.Http/src/CoreWCF/Channels/FramingFormat.cs create mode 100644 src/CoreWCF.Http/src/CoreWCF/Channels/HttpAnonymousUriPrefixMatcher.cs create mode 100644 src/CoreWCF.Http/src/CoreWCF/Channels/HttpChannelHelpers.cs create mode 100644 src/CoreWCF.Http/src/CoreWCF/Channels/HttpReplyChannel.cs create mode 100644 src/CoreWCF.Http/src/CoreWCF/Channels/HttpRequestContext.cs create mode 100644 src/CoreWCF.Http/src/CoreWCF/Channels/HttpRequestMessageProperty.cs create mode 100644 src/CoreWCF.Http/src/CoreWCF/Channels/HttpResponseMessageProperty.cs create mode 100644 src/CoreWCF.Http/src/CoreWCF/Channels/HttpTransportBindingElement.cs create mode 100644 src/CoreWCF.Http/src/CoreWCF/Channels/HttpTransportSettings.cs create mode 100644 src/CoreWCF.Http/src/CoreWCF/Channels/ITransportFactorySettings.cs create mode 100644 src/CoreWCF.Http/src/CoreWCF/Channels/MessageEncoderCompressionHandler.cs create mode 100644 src/CoreWCF.Http/src/CoreWCF/Channels/RequestDelegateHandler.cs create mode 100644 src/CoreWCF.Http/src/CoreWCF/Channels/ServiceModelHttpHeaders.cs create mode 100644 src/CoreWCF.Http/src/CoreWCF/Channels/ServiceModelHttpMiddleware.cs create mode 100644 src/CoreWCF.Http/src/CoreWCF/Channels/TransportDefaults.cs create mode 100644 src/CoreWCF.Http/src/CoreWCF/DiagnosticUtility.cs create mode 100644 src/CoreWCF.Http/src/CoreWCF/Diagnostics/TraceUtility.cs create mode 100644 src/CoreWCF.Http/src/CoreWCF/DummyTransportBindingElement.cs create mode 100644 src/CoreWCF.Http/src/CoreWCF/HttpBindingBase.cs create mode 100644 src/CoreWCF.Http/src/CoreWCF/Runtime/Diagnostics/EtwDiagnosticTrace.cs create mode 100644 src/CoreWCF.Http/src/CoreWCF/Runtime/UrlUtility.cs create mode 100644 src/CoreWCF.Http/src/CoreWCF/ServiceModelDictionary.cs create mode 100644 src/CoreWCF.Http/src/CoreWCF/ServiceModelStrings.cs create mode 100644 src/CoreWCF.Http/src/CoreWCF/ServiceModelStringsVersion1.cs create mode 100644 src/CoreWCF.Http/src/CoreWCF/WSAddressing10ProblemHeaderQNameFault.cs create mode 100644 src/CoreWCF.Http/src/CoreWCF/WSMessageEncoding.cs create mode 100644 src/CoreWCF.Http/src/CoreWCF/XD.cs create mode 100644 src/CoreWCF.Http/src/Resources/Strings.resx create mode 100644 src/CoreWCF.Http/tests/ClientContract/IEchoService.cs create mode 100644 src/CoreWCF.Http/tests/CoreWCF.Http.Tests.csproj create mode 100644 src/CoreWCF.Http/tests/ISimpleService.cs create mode 100644 src/CoreWCF.Http/tests/Properties/launchSettings.json create mode 100644 src/CoreWCF.Http/tests/ServiceContract/IEchoService.cs create mode 100644 src/CoreWCF.Http/tests/Services/EchoService.cs create mode 100644 src/CoreWCF.Http/tests/SimpleService.cs create mode 100644 src/CoreWCF.Http/tests/SimpleTest.cs create mode 100644 src/CoreWCF.Http/tests/Startup.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF.NetTcp.csproj create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Channels/AddressingVersionExtensions.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Channels/BufferedConnectionListener.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Channels/Connection.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Channels/ConnectionAcceptor.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Channels/ConnectionBufferPool.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Channels/ConnectionDemuxer.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Channels/ConnectionModeReader.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Channels/ConnectionOrientedTransportBindingElement.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Channels/ConnectionOrientedTransportChannelListener.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Channels/ConnectionOrientedTransportManager.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Channels/DuplexRequestContext.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Channels/ExclusiveTcpTransportManager.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Channels/Framing/ConnectionContextExtensions.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Channels/Framing/DuplexFramingMiddleware.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Channels/Framing/FramingConnection.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Channels/Framing/FramingConnectionContext.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Channels/Framing/FramingDecoder.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Channels/Framing/FramingModeHandshakeMiddleware.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Channels/Framing/NetMessageFramingConnectionHandler.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Channels/Framing/ServerFramingDuplexSessionChannel.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Channels/Framing/ServerFramingDuplexSessionMiddleware.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Channels/Framing/ServerSessionConnectionReaderMiddleware.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Channels/Framing/SingletonFramingFramingMiddleware.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Channels/Framing/StreamDuplexPipe.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Channels/FramingChannel.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Channels/FramingDecoder.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Channels/FramingEncoder.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Channels/FramingFormat.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Channels/IConnectionOrientedTransportFactorySettings.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Channels/IMessageSource.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Channels/IStreamUpgradeChannelBindingProvider.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Channels/ImmutableCommunicationTimeouts.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Channels/InitialServerConnectionReader.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Channels/NetFramingTransportSettings.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Channels/OutputChannel.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Channels/QueuedObjectPool.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Channels/ReplyChannel.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Channels/ReplyChannelAcceptor.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Channels/SessionConnectionReader.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Channels/SingletonChannelAcceptor.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Channels/SingletonConnectionReader.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Channels/SocketAsyncEventArgsPool.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Channels/SocketConnection.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Channels/SynchronizedMessageSource.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Channels/TcpChannelListener.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Channels/TcpTransportBindingElement.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Channels/TcpTransportManager.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Channels/TimeoutStream.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Channels/TransportChannelListener.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Channels/TransportDefaults.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Channels/TransportDuplexSessionChannel.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Channels/TransportManager.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Channels/TransportOutputChannel.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Channels/TransportReplyChannelAcceptor.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Channels/TransportSecurityHelpers.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Channels/UriGenerator.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Channels/UriHelper.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Configuration/FramingConnectionHandshakeBuilder.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Configuration/IFramingConnectionHandshakeBuilder.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Configuration/MapExtensions.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Configuration/MapMiddleware.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Configuration/MapOptions.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Configuration/ServiceModelWebHostBuilderExtensions.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Configuration/UseMiddlewareFramingConnectionHandshakeExtensions.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/DiagnosticUtility.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Dispatcher/ErrorBehaviorHelper.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Dispatcher/ExceptionHandlerHelper.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/NetTcpBinding.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/NetTcpSecurity.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Runtime/AsyncCompletionResult.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Runtime/BackoffTimeoutHelper.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Runtime/CallbackException.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Runtime/Diagnostics/EtwDiagnosticTrace.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Security/ProtectionLevelHelper.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/Security/SslProtocolsHelper.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/SecurityModeHelper.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/ServiceDefaults.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/TcpClientCredentialType.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/TcpTransportSecurity.cs create mode 100644 src/CoreWCF.NetTcp/src/CoreWCF/TraceUtility.cs create mode 100644 src/CoreWCF.NetTcp/src/Resources/Strings.resx create mode 100644 src/CoreWCF.NetTcp/tests/BaseStartup.cs create mode 100644 src/CoreWCF.NetTcp/tests/BasicServiceTest.cs create mode 100644 src/CoreWCF.NetTcp/tests/ClientContract/IEchoService.cs create mode 100644 src/CoreWCF.NetTcp/tests/ConnectionHandlerTests.cs create mode 100644 src/CoreWCF.NetTcp/tests/CoreWCF.NetTcp.Tests.csproj create mode 100644 src/CoreWCF.NetTcp/tests/ServiceContract/IEchoService.cs create mode 100644 src/CoreWCF.NetTcp/tests/ServiceHelper.cs create mode 100644 src/CoreWCF.NetTcp/tests/Services/EchoService.cs create mode 100644 src/CoreWCF.NetTcp/tests/XunitTestAssemblyAttribute.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF.Primitives.csproj create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/ActionMismatchAddressingException.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/ActionNotSupportedException.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/AddressAlreadyInUseException.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/AddressFilterMode.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/CallbackBehaviorAttribute.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/ChannelTerminatedException.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/AddressHeader.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/AddressHeaderCollection.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/Addressing.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/AddressingVersion.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/AuthenticationSchemesHelper.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/BaseUriWithWildcard.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/BinaryMessageEncoderFactory.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/BinaryMessageEncodingBindingElement.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/BinaryVersion.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/Binding.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/BindingContext.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/BindingElement.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/BindingElementCollection.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/BindingParameterCollection.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/BodyWriter.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/BufferManager.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/BufferManagerOutputStream.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/BufferedMessageData.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/BufferedMessageWriter.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/ChannelAcceptor.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/ChannelBase.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/ChannelBindingMessageProperty.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/ChannelListenerBase.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/ChannelManagerBase.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/ChannelRequirements.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/CommunicationObject.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/CommunicationObjectManager.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/CompressionFormat.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/ContentOnlyMessage.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/CorrelationDataMessageProperty.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/CustomBinding.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/EncoderHelpers.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/FaultConverter.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/FramingDecoder.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/FramingFormat.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/IAnonymousUriPrefixMatcher.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/IChannel.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/IChannelAcceptor.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/IChannelBindingProvider.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/IChannelListener.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/ICompressedMessageEncoder.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/ICorrelatorKey.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/IDuplexChannel.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/IDuplexSession.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/IDuplexSessionChannel.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/IInputChannel.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/IInputSession.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/IInputSessionChannel.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/IMessageProperty.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/IOutputChannel.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/IOutputSession.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/IOutputSessionChannel.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/IReplyChannel.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/IReplySessionChannel.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/IRequestChannel.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/IRequestReplyCorrelator.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/IRequestSessionChannel.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/ISecurityCapabilities.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/ISecuritySession.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/ISession.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/ISessionChannel.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/IStreamUpgradeChannelBindingProvider.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/ITransportFactorySettings.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/ITransportTokenAssertionProvider.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/InputChannel.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/InputQueueChannel.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/InputQueueChannelAcceptor.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/IntEncoder.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/LayeredChannelListener.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/LifetimeManager.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/Message.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/MessageBuffer.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/MessageEncoder.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/MessageEncoderCompressionHandler.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/MessageEncoderFactory.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/MessageEncodingBindingElement.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/MessageFault.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/MessageHeader.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/MessageHeaderInfo.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/MessageHeaders.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/MessageProperties.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/MessageState.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/MessageVersion.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/MethodCall.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/ReceiveContext.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/ReceiveContextState.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/RemoteEndpointMessageProperty.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/RequestContext.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/RequestReplyCorrelator.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/SecurityCapabilities.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/ServiceChannel.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/ServiceChannelFactory.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/ServiceChannelProxy.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/SessionOpenNotification.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/SingletonChannelAcceptor.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/SslStreamSecurityBindingElement.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/SslStreamSecurityUpgradeProvider.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/StreamSecurityUpgradeAcceptor.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/StreamSecurityUpgradeAcceptorBase.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/StreamSecurityUpgradeProvider.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/StreamUpgradeAcceptor.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/StreamUpgradeBindingElement.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/StreamUpgradeProvider.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/TextMessageEncoderFactory.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/TextMessageEncodingBindingElement.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/TransportBindingElement.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/TransportDefaults.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/TransportSecurityHelpers.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/UnderstoodHeaders.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/UriEx.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/WindowsStreamSecurityBindingElement.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Channels/WindowsStreamSecurityUpgradeProvider.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Collections/Generic/KeyedByTypeCollection.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Collections/Generic/SynchronizedCollection.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Collections/Generic/SynchronizedKeyedCollection.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/CommunicationException.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/CommunicationObjectAbortedException.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/CommunicationObjectFaultedException.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/CommunicationState.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/ConcurrencyMode.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Configuration/DispatcherBuilderImpl.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Configuration/IDispatcherBuilder.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Configuration/IService.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Configuration/IServiceBuilder.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Configuration/IServiceConfiguration.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Configuration/IServiceDispatcher.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Configuration/ServiceBuilder.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Configuration/ServiceConfiguration.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Configuration/ServiceEndpointConfiguration.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Configuration/ServiceModelApplicationBuilderExtensions.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Configuration/ServiceModelServiceCollectionExtensions.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/DBNull.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/DataContractFormatAttribute.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Description/ConfigLoader.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Description/ContractDescription.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Description/CustomAttributeProvider.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Description/DataContractSerializerOperationBehavior.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Description/DispatcherBuilder.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Description/FaultDescription.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Description/FaultDescriptionCollection.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Description/IContractBehavior.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Description/IContractBehaviorAttribute.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Description/IContractResolver.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Description/IEndpointBehavior.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Description/IOperationBehavior.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Description/IServiceBehavior.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Description/ListenUriMode.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Description/MessageBodyDescription.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Description/MessageDescription.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Description/MessageDescriptionCollection.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Description/MessageDirection.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Description/MessageHeaderDescription.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Description/MessageHeaderDescriptionCollection.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Description/MessagePartDescription.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Description/MessagePartDescriptionCollection.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Description/MessagePropertyAttribute.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Description/MessagePropertyDescription.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Description/MessagePropertyDescriptionCollection.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Description/OperationDescription.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Description/OperationDescriptionCollection.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Description/ServiceCredentials.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Description/ServiceDescription.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Description/ServiceEndpoint.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Description/ServiceEndpointCollection.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Description/ServiceReflector.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Description/TaskOperationDescriptionValidator.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Description/TypeLoader.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Description/XmlSerializerOperationBehavior.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/DiagnosticUtility.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Diagnostics/TraceUtility.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ActionMessageFilter.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ActionMessageFilterTable.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/AndMessageFilter.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/AndMessageFilterTable.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/AsyncMethodInvoker.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/AuthorizationBehavior.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/BufferedReceiveBinder.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ChannelDispatcher.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ChannelDispatcherBase.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ChannelDispatcherCollection.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ChannelHandler.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ClientOperation.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ClientRuntime.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ConcurrencyBehavior.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/DataContractSerializerFaultFormatter.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/DataContractSerializerOperationFormatter.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/DataContractSerializerServiceBehavior.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/DispatchOperation.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/DispatchOperationRuntime.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/DispatchRuntime.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/DuplexChannelBinder.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/EndpointAddressMessageFilter.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/EndpointAddressMessageFilterTable.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/EndpointAddressProcessor.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/EndpointDispatcher.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/EndpointDispatcherTable.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/EndpointFilterProvider.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ErrorBehavior.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ErrorHandlerFaultInfo.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ErrorHandlingAcceptor.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ErrorHandlingReceiver.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ExceptionHandler.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/FaultContractInfo.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/FaultFormatter.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ICallContextInitializer.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IChannelBinder.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IChannelInitializer.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IClientFaultFormatter.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IClientMessageFormatter.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IClientMessageInspector.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IClientOperationSelector.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IDispatchFaultFormatter.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IDispatchMessageFormatter.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IDispatchMessageInspector.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IDispatchOperationSelector.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IErrorHandler.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IInputSessionShutdown.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IInstanceContextInitializer.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IInstanceContextProvider.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IInstanceProvider.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IInvokeReceivedNotification.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IListenerBinder.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IMessageFilterTable.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IOperationInvoker.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IParameterInspector.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IResumeMessageRpc.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ImmutableClientRuntime.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ImmutableCommunicationTimeouts.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ImmutableDispatchRuntime.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/InputChannelBinder.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/InstanceBehavior.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/InstanceContextManager.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/InvokerUtil.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ListenerBinder.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ListenerHandler.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/MatchAllMessageFilter.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/MatchNoneMessageFilter.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/MessageFilter.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/MessageFilterTable.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/MessageOperationFormatter.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/MessageRpc.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/MultipleFilterMatchesException.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/MultipleReceiveBinder.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/NetDispatcherFaultException.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/OperationFormatter.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/OperationInvokerBehavior.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/OperationSelectorBehavior.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/PerCallInstanceContextProvider.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/PerSessionInstanceContextProvider.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/PrefixEndpointAddressMessageFilter.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/PrefixEndpointAddressMessageFilterTable.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/PrimitiveOperationFormatter.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ProxyOperationRuntime.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ProxyRpc.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/QueryUtil.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ReceiveContextAcknowledgementMode.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ReplyChannelBinder.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ServiceDispatcher.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/SharedRuntimeState.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/SingletonInstanceContextProvider.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/StreamFormatter.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/SyncMethodInvoker.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/SynchronizedChannelCollection.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/TaskMethodInvoker.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/TerminatingOperationBehavior.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ThreadBehavior.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ThreadSafeMessageFilterTable.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/UniqueContractNameValidationBehavior.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/XmlSerializerFaultFormatter.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/XmlSerializerObjectSerializer.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/XmlSerializerOperationFormatter.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/DnsEndpointIdentity.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/EmptyArray.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/EndpointAddress.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/EndpointIdentity.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/EndpointNotFoundException.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/EnvelopeVersion.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/ExceptionDetail.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/ExceptionMapper.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/ExtensionCollection.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/FaultCode.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/FaultCodeConstants.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/FaultContractAttribute.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/FaultException.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/FaultReason.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/FaultReasonText.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/GeneralEndpointIdentity.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/HostNameComparisonMode.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/IClientChannel.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/ICommunicationObject.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/IContextChannel.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/IDefaultCommunicationTimeouts.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/IDuplexContextChannel.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/IExtensibleObject.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/IExtension.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/IExtensionCollection.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/IOperationContractAttributeProvider.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/IServiceChannel.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Claims/Claim.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Claims/ClaimComparer.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Claims/ClaimSet.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Claims/ClaimTypes.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Claims/DefaultClaimSet.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Claims/Rights.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Claims/WindowsClaimSet.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Claims/X509CertificateClaimSet.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Claims/XsiConstants.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/IdentityModelStrings.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/IdentityModelStringsVersion1.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Policy/AuthorizationContext.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Policy/EvaluationContext.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Policy/IAuthorizationComponent.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Policy/IAuthorizationPolicy.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Policy/UnconditionalPolicy.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/SecurityKeyIdentifierClause.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/SecurityUniqueId.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/SecurityUtils.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/CustomUserNameSecurityTokenAuthenticator.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/RsaSecurityTokenAuthenticator.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/SecurityTokenAuthenticator.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/SecurityTokenManager.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/SecurityTokenProvider.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/SecurityTokenRequirement.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/SecurityTokenResolver.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/SecurityTokenSerializer.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/SecurityTokenVersion.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/UserNamePasswordValidator.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/UserNameSecurityTokenAuthenticator.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/WindowsSecurityTokenAuthenticator.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/X509CertificateChain.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/X509CertificateValidator.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/X509SecurityTokenAuthenticator.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/X509SecurityTokenProvider.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/RsaSecurityToken.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/SecurityAlgorithms.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/SecurityKey.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/SecurityKeyIdentifier.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/SecurityKeyType.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/SecurityKeyUsage.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/SecurityToken.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/SecurityTokenException.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/SecurityTokenTypes.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/SecurityTokenValidationException.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/USerNameSecurityToken.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/WindowsSecurityToken.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/X509SecurityToken.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/X509WindowsSecurityToken.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/XD.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/InstanceContext.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/InstanceContextMode.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/InvalidMessageContractException.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/MessageBodyMemberAttribute.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/MessageContractAttribute.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/MessageContractMemberAttribute.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/MessageHeaderArrayAttribute.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/MessageHeaderAttribute.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/MessageHeaderException.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/MessageHeaderT.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/MessageParameterAttribute.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/MustUnderstandSoapException.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/OperationBehaviorAttribute.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/OperationContext.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/OperationContractAttribute.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/OperationFormatStyle.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/OperationFormatUse.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Pool.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/ProtocolException.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/QuotaExceededException.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Runtime/ActionItem.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Runtime/AsyncManualResetEvent.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Runtime/BackoffTimeoutHelper.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Runtime/BufferedOutputStream.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Runtime/CallbackException.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Runtime/Diagnostics/EtwDiagnosticTrace.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Runtime/Diagnostics/SecurityTraceRecordHelper.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Runtime/InputQueue.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Runtime/InternalBufferManager.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Runtime/ReflectionExtensions.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Runtime/Serialization/XmlMappingTypes.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Runtime/SignalGate.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Runtime/SynchronizedPool.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Security/ChannelProtectionRequirements.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Security/IEndpointIdentityProvider.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Security/IdentityVerifier.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Security/MessagePartSpecification.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Security/MessageSecurityException.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Security/ScopedMessagePartSpecification.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Security/SecurityAlgorithmSuite.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Security/SecurityCredentialsManager.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Security/SecurityMessageProperty.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Security/SecurityNegotiationException.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Security/SecurityTokenSpecification.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Security/SecurityUtils.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Security/ServiceCredentialsSecurityTokenManager.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Security/SspiSecurityTokenProvider.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Security/Tokens/InitiatorServiceModelSecurityTokenRequirement.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Security/Tokens/RecipientServiceModelSecurityTokenRequirement.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Security/Tokens/ServiceModelSecurityTokenRequirement.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Security/Tokens/ServiceModelSecurityTokenTypes.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Security/Tokens/ServiceX509SecurityTokenProvider.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Security/Tokens/SspiSecurityToken.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Security/Tokens/WindowsSidIdentity.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Security/UserNamePasswordServiceCredential.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Security/UserNamePasswordValidationMode.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Security/WindowsServiceCredential.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Security/X509CertificateInitiatorServiceCredential.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Security/X509CertificateRecipientServiceCredential.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Security/X509CertificateValidationMode.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Security/X509ClientCertificateAuthentication.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/SecurityMode.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/ServerTooBusyException.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/ServiceActivationException.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/ServiceBehaviorAttribute.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/ServiceChannelManager.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/ServiceContractAttribute.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/ServiceDefaults.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/ServiceHostBase.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/ServiceHostObjectModel.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/ServiceKnownTypeAttribute.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/ServiceModelAttributeTargets.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/ServiceModelDictionary.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/ServiceModelStrings.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/ServiceModelStringsVersion1.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/ServiceRequestDelegate.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/ServiceSecurityContext.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/SessionMode.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/SpnEndpointIdentity.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/TransferMode.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/TryAsyncResult.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/UnknownMessageReceivedEventArgs.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/UpnEndpointIdentity.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/UriSchemeKeyedCollection.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/WSAddressing10ProblemHeaderQNameFault.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/X509CertificateEndpointIdentity.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/XD.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/Xml/XmlBinaryNodeType.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/XmlBuffer.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/XmlReaderExtensions.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/XmlSerializerFormatAttribute.cs create mode 100644 src/CoreWCF.Primitives/src/CoreWCF/XmlUtil.cs create mode 100644 src/CoreWCF.Primitives/src/Resources/Strings.resx create mode 100644 src/CoreWCF.Primitives/tests/CoreWCF.Primitives.Tests.csproj create mode 100644 src/CoreWCF.Primitives/tests/DispatchBuilderTests.cs create mode 100644 src/CoreWCF.Primitives/tests/Helpers/MockReplyChannel.cs create mode 100644 src/CoreWCF.Primitives/tests/Helpers/MockServer.cs create mode 100644 src/CoreWCF.Primitives/tests/Helpers/MockTransportBindingElement.cs create mode 100644 src/CoreWCF.Primitives/tests/Helpers/TestRequestContext.cs create mode 100644 src/CoreWCF.Primitives/tests/ISimpleService.cs create mode 100644 src/CoreWCF.Primitives/tests/Properties/launchSettings.json create mode 100644 src/CoreWCF.Primitives/tests/SimpleService.cs create mode 100644 src/NuGet.config create mode 100644 src/Samples/DesktopClient/App.config create mode 100644 src/Samples/DesktopClient/DesktopClient.csproj create mode 100644 src/Samples/DesktopClient/IEchoService.cs create mode 100644 src/Samples/DesktopClient/Program.cs create mode 100644 src/Samples/DesktopClient/Properties/AssemblyInfo.cs create mode 100644 src/Samples/DesktopServer/App.config create mode 100644 src/Samples/DesktopServer/DesktopServer.csproj create mode 100644 src/Samples/DesktopServer/EchoService.cs create mode 100644 src/Samples/DesktopServer/IEchoService.cs create mode 100644 src/Samples/DesktopServer/Program.cs create mode 100644 src/Samples/DesktopServer/Properties/AssemblyInfo.cs create mode 100644 src/Samples/NetCoreClient/IEchoService.cs create mode 100644 src/Samples/NetCoreClient/NetCoreClient.csproj create mode 100644 src/Samples/NetCoreClient/Program.cs create mode 100644 src/Samples/NetCoreServer/EchoService.cs create mode 100644 src/Samples/NetCoreServer/IEchoService.cs create mode 100644 src/Samples/NetCoreServer/NetCoreServer.csproj create mode 100644 src/Samples/NetCoreServer/Program.cs create mode 100644 src/Samples/NetCoreServer/Properties/launchSettings.json create mode 100644 src/Samples/NetCoreServer/Startup.cs create mode 100644 src/WcfCore.sln diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..87a3690c0 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,67 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain + +# Force bash scripts to always use lf line endings so that if a repo is accessed +# in Unix via a file share from Windows, the scripts will work. +*.sh text eol=lf diff --git a/.gitignore b/.gitignore index 3e759b75b..544bb1466 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,9 @@ -## Ignore Visual Studio temporary files, build results, and -## files generated by popular Visual Studio add-ons. -## -## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore +syntax: glob + +### VisualStudio ### + +# Tool Runtime Dir +/[Tt]ools/ # User-specific files *.suo @@ -9,9 +11,6 @@ *.userosscache *.sln.docstates -# User-specific files (MonoDevelop/Xamarin Studio) -*.userprefs - # Build results [Dd]ebug/ [Dd]ebugPublic/ @@ -19,24 +18,29 @@ [Rr]eleases/ x64/ x86/ +build/ bld/ [Bb]in/ [Oo]bj/ -[Ll]og/ +msbuild.log +msbuild.err +msbuild.wrn -# Visual Studio 2015/2017 cache/options directory +# Cross building rootfs +cross/rootfs/ + +# Visual Studio 2015 .vs/ -# Uncomment if you have tasks that create the project's static files in wwwroot -#wwwroot/ -# Visual Studio 2017 auto generated files -Generated\ Files/ +# Visual Studio 2015 Pre-CTP6 +*.sln.ide +*.ide/ # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* -# NUNIT +#NUNIT *.VisualState.xml TestResult.xml @@ -45,29 +49,14 @@ TestResult.xml [Rr]eleasePS/ dlldata.c -# Benchmark Results -BenchmarkDotNet.Artifacts/ - -# .NET Core -project.lock.json -project.fragment.lock.json -artifacts/ -**/Properties/launchSettings.json - -# StyleCop -StyleCopReport.xml - -# Files built by Visual Studio *_i.c *_p.c *_i.h *.ilk *.meta *.obj -*.iobj *.pch *.pdb -*.ipdb *.pgc *.pgd *.rsp @@ -97,16 +86,11 @@ ipch/ *.sdf *.cachefile *.VC.db -*.VC.VC.opendb # Visual Studio profiler *.psess *.vsp *.vspx -*.sap - -# Visual Studio Trace Files -*.e2e # TFS 2012 Local Workspace $tf/ @@ -119,7 +103,7 @@ _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user -# JustCode is a .NET coding add-in +# JustCode is a .NET coding addin-in .JustCode # TeamCity is a build add-in @@ -128,18 +112,9 @@ _TeamCity* # DotCover is a Code Coverage Tool *.dotCover -# AxoCover is a Code Coverage Tool -.axoCover/* -!.axoCover/settings.json - -# Visual Studio code coverage results -*.coverage -*.coveragexml - # NCrunch _NCrunch_* .*crunch*.local.xml -nCrunchTemp_* # MightyMoose *.mm.* @@ -167,67 +142,37 @@ publish/ # Publish Web Output *.[Pp]ublish.xml *.azurePubxml -# Note: Comment the next line if you want to checkin your web deploy settings, -# but database connection strings (with potential passwords) will be unencrypted *.pubxml *.publishproj -# Microsoft Azure Web App publish settings. Comment the next line if you want to -# checkin your Azure Web App publish settings, but sensitive information contained -# in these scripts will be unencrypted -PublishScripts/ - # NuGet Packages -*.nupkg -# The packages folder can be ignored because of Package Restore -**/[Pp]ackages/* -# except build/, which is used as an MSBuild target. -!**/[Pp]ackages/build/ -# Uncomment if necessary however generally it will be regenerated when needed -#!**/[Pp]ackages/repositories.config -# NuGet v3's project.json files produces more ignorable files *.nuget.props *.nuget.targets +*.nupkg +**/packages/* + +# NuGet package restore lockfiles +project.lock.json -# Microsoft Azure Build Output +# Windows Azure Build Output csx/ *.build.csdef -# Microsoft Azure Emulator -ecf/ -rcf/ - -# Windows Store app package directories and files +# Windows Store app package directory AppPackages/ -BundleArtifacts/ -Package.StoreAssociation.xml -_pkginfo.txt -*.appx - -# Visual Studio cache files -# files ending in .cache can be ignored -*.[Cc]ache -# but keep track of directories ending in .cache -!*.[Cc]ache/ # Others +*.Cache ClientBin/ +[Ss]tyle[Cc]op.* ~$* -*~ *.dbmdl *.dbproj.schemaview -*.jfm *.pfx *.publishsettings -orleans.codegen.cs - -# Including strong name files can present a security risk -# (https://github.com/github/gitignore/pull/2483#issue-259490424) -#*.snk - -# Since there are multiple workflows, uncomment next line to ignore bower_components -# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) -#bower_components/ +node_modules/ +*.metaproj +*.metaproj.tmp # RIA/Silverlight projects Generated_Code/ @@ -239,92 +184,86 @@ _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm -ServiceFabricBackup/ -*.rptproj.bak # SQL Server files *.mdf *.ldf -*.ndf # Business Intelligence projects *.rdl.data *.bim.layout *.bim_*.settings -*.rptproj.rsuser # Microsoft Fakes FakesAssemblies/ -# GhostDoc plugin setting file -*.GhostDoc.xml +### MonoDevelop ### -# Node.js Tools for Visual Studio -.ntvs_analysis.dat -node_modules/ +*.pidb +*.userprefs -# Visual Studio 6 build log -*.plg +### Windows ### -# Visual Studio 6 workspace options file -*.opt +# Windows image file caches +Thumbs.db +ehthumbs.db -# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) -*.vbw +# Folder config file +Desktop.ini -# Visual Studio LightSwitch build output -**/*.HTMLClient/GeneratedArtifacts -**/*.DesktopClient/GeneratedArtifacts -**/*.DesktopClient/ModelManifest.xml -**/*.Server/GeneratedArtifacts -**/*.Server/ModelManifest.xml -_Pvt_Extensions +# Recycle Bin used on file shares +$RECYCLE.BIN/ -# Paket dependency manager -.paket/paket.exe -paket-files/ +# Windows Installer files +*.cab +*.msi +*.msm +*.msp -# FAKE - F# Make -.fake/ +# Windows shortcuts +*.lnk -# JetBrains Rider -.idea/ -*.sln.iml +### Linux ### -# CodeRush -.cr/ +*~ -# Python Tools for Visual Studio (PTVS) -__pycache__/ -*.pyc +# KDE directory preferences +.directory -# Cake - Uncomment if you are using it -# tools/** -# !tools/packages.config +### OSX ### -# Tabs Studio -*.tss +.DS_Store +.AppleDouble +.LSOverride -# Telerik's JustMock configuration file -*.jmconfig +# Icon must end with two \r +Icon -# BizTalk build output -*.btp.cs -*.btm.cs -*.odx.cs -*.xsd.cs +# Thumbnails +._* -# OpenCover UI analysis results -OpenCover/ +# Files that might appear on external disk +.Spotlight-V100 +.Trashes -# Azure Stream Analytics local run output -ASALocalRun/ +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk -# MSBuild Binary and Structured Log -*.binlog +# vim temporary files +[._]*.s[a-w][a-z] +[._]s[a-w][a-z] +*.un~ +Session.vim +.netrwhist +*~ -# NVidia Nsight GPU debugger configuration file -*.nvuser +# Visual Studio Code +.vscode/ -# MFractors (Xamarin productivity tool) working folder -.mfractor/ +# Private test configuration and binaries. +config.ps1 +**/IISApplications diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..5be9e6030 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,69 @@ +# How to contribute + +The easiest way to contribute is to open an issue and start a discussion. +Then we can decide if and how a feature or a change could be implemented and if you should submit a pull requests with code changes. We encourage all contributors to write tests for the features they are contributing. + +Also read this first: [Being a good open source citizen](https://hackernoon.com/being-a-good-open-source-citizen-9060d0ab9732#.x3hocgw85) + +## General feedback and discussions + +Please start a discussion on the [core repo issue tracker](https://github.com/CoreWCF/CoreWCF/issues). + +## Platform + +Core WCF is built on top of .NET Core 2.2. + +## Building + +Run `dotnet restore` and `dotnet build` from the command line inside the src folder. This builds Core WCF. + +## Other discussions + +https://gitter.im/CoreWCF/CoreWCF + +## Filing issues + +The best way to get your bug fixed is to be as detailed as you can be about the problem. +Providing a minimal project with steps to reproduce the problem is ideal. +Here are questions you can answer before you file a bug to make sure you're not missing any important information. + +1. Did you read the [documentation](https://corewcf.readthedocs.io/en/latest/)? +2. Did you include the snippet of broken code in the issue? +3. What are the *EXACT* steps to reproduce this problem (including source/destination types, mapping configuration and execution)? + +GitHub supports [markdown](https://github.github.com/github-flavored-markdown/), so when filing bugs make sure you check the formatting before clicking submit. + +## Contributing code and content + +You will need to sign a [Contributor License Agreement](https://cla.dotnetfoundation.org/) before submitting your pull request. + +Make sure you can build the code. Familiarize yourself with the project workflow and our coding conventions. If you don't know what a pull request is read this article: https://help.github.com/articles/using-pull-requests. + +**We only accept PRs to the master branch.** + +Before submitting a feature or substantial code contribution please discuss it with the team and ensure it follows the product roadmap. Here's a list of blog posts that are worth reading before doing a pull request: + +* [Open Source Contribution Etiquette](http://tirania.org/blog/archive/2010/Dec-31.html) by Miguel de Icaza +* [Don't "Push" Your Pull Requests](http://www.igvita.com/2011/12/19/dont-push-your-pull-requests/) by Ilya Grigorik. +* [10 tips for better Pull Requests](http://blog.ploeh.dk/2015/01/15/10-tips-for-better-pull-requests/) by Mark Seemann +* [How to write the perfect pull request](https://github.com/blog/1943-how-to-write-the-perfect-pull-request) by GitHub + +Here's a few things you should always do when making changes to the code base: + +**Commit/Pull Request Format** + +``` +Summary of the changes (Less than 80 chars) + - Detail 1 + - Detail 2 + +#bugnumber (in this specific format) +``` + +**Tests** + +- Tests need to be provided for every bug/feature that is completed. +- Tests only need to be present for issues that need to be verified by QA (e.g. not tasks). +- If there is a scenario that is far too hard to test there does not need to be a test for it. + - "Too hard" is determined by the team as a whole. + - With WCF there are many infrastructure scenarios which are hard to automate because of that infrastructure (kerberos auth in a Windows domain for example) but the tests are simple to write, just not execute. We will clarify such scenarios which are too hard to test because of infrastructure requirements, but we will provide test code which isn't run as part of automated tests, but can be run manually in a configured environment. For example, on the WCF client we have tests which require being in a domain which are disabled but we have a flag we can set to enable running those tests so that if you are executing them in a domain, you can still test it. \ No newline at end of file diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 000000000..b598cef64 --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,44 @@ + + + + $(MSBuildThisFileDirectory) + $(ProjectDir)src/ + + + + $(ProjectDir)bin/ + $(BinDir)obj/ + + $(ObjDir)$(MSBuildProjectName)/ + $(BaseIntermediateOutputPath) + $(BaseIntermediateOutputPath)/project.assets.json + + + + + + + + $(SourceDir)Common/src + $([System.IO.Path]::GetDirectoryName($(MSBuildProjectDirectory))) + $(ParentFolder.EndsWith(`Samples`, true, null)) + $(AssemblyName.EndsWith(`Tests`)) + + + + + $(BinDir)$(MSBuildProjectName)/ + $(BaseIntermediateOutputPath) + $(IntermediateOutputRootPath)$(MSBuildProjectName)/ + + + + + false + + + + diff --git a/Directory.Build.targets b/Directory.Build.targets new file mode 100644 index 000000000..b11ab7523 --- /dev/null +++ b/Directory.Build.targets @@ -0,0 +1,12 @@ + + + + + + true + shared/%(RecursiveDir)%(Filename)%(Extension) + + + + + diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..559914551 --- /dev/null +++ b/LICENSE @@ -0,0 +1,9 @@ +The MIT License (MIT) + +Copyright (c) 2019 Tiberiu Covaci + +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. \ No newline at end of file diff --git a/README.md b/README.md index 25e7c6ff9..5f31f5533 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,23 @@ -# corewcf -Main repository for the Core WCF project +### What is Core WCF? + +Core WCF is a port of Windows Communication Framework (WCF) to .NET Core. The goal of this project is to enable existing WCF projects to move to .NET Core. Please note that right now, this port is not production ready. We suggest waiting until there are release packages available before using WCF in a production enivronment. + +### How do I get started? + + +### How do I contribute? + +Please see the [CONTRIBUTING.md](CONTRIBUTING.md) file for details. + + + +### License, etc. + +This project has adopted the code of conduct defined by the Contributor Covenant to clarify expected behavior in our community. +For more information see the [.NET Foundation Code of Conduct](https://dotnetfoundation.org/code-of-conduct). + +Core WCF is Copyright © 2019 [Tiberiu Covaci](https://tibi.me) and other contributors under the [MIT license](LICENSE.txt). + +### .NET Foundation + +This project is supported by the [.NET Foundation](https://dotnetfoundation.org). \ No newline at end of file diff --git a/resources.props b/resources.props new file mode 100644 index 000000000..8faffe9a6 --- /dev/null +++ b/resources.props @@ -0,0 +1,24 @@ + + + + $(MSBuildProjectDirectory)/Resources/ + true + $(ResourcesSourceOutputDirectory)Strings.resx + $(IntermediateOutputPath)SR.cs + $(DefineConstants);DEBUGRESOURCES + + + + + true + FxResources.$(AssemblyName).SR.resources + + + + + + true + Resources/Common/SR.cs + + + \ No newline at end of file diff --git a/resources.targets b/resources.targets new file mode 100644 index 000000000..b8bc315f8 --- /dev/null +++ b/resources.targets @@ -0,0 +1,94 @@ + + + + + + <_Indent>%20%20%20%20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $([System.String]::new('%(ResourceStringItems.Identity)').Contains(`resName`)) + + <_Indent>%20%20%20%20 + + + + + $([System.Text.RegularExpressions.Regex]::Match($([System.String]::new('%(ResourceStringItems.Identity)')),`(?<=\s*<resName>)(.*?)(?=</resName>)`)) + $([System.Text.RegularExpressions.Regex]::Match($([System.String]::new('%(ResourceStringItems.Identity)')),`(?<=\s*<resValue>)(.*?)(?=</resValue>.*)`)) + + $([System.Text.RegularExpressions.Regex]::Replace($(ResourceValue),`"`,`""`)) + + + + + + + + + + + + + $([System.IO.File]::ReadAllText($(StringResourcesPath))) + + + $([System.Text.RegularExpressions.Regex]::Replace($(ResourceFileContents), `<!--.*-->`, ``, System.Text.RegularExpressions.RegexOptions.Singleline)) + + + <data\s+name\s*=\s*"(?<name>.*?)"\s*(?:xml:space\s*=\s*"\w+")?>\s*<value>(?<value>.*?)</value>\s*</data> + $([System.Text.RegularExpressions.Regex]::Replace($(ResourceFileContents), `$(ResourceRegexExpression)`, `<resName>$1</resName><resValue>$2</resValue>`)) + + + + + + + + diff --git a/src/Common/src/CoreWCF/Channels/ChannelBindingMessagePropertyHelper.cs b/src/Common/src/CoreWCF/Channels/ChannelBindingMessagePropertyHelper.cs new file mode 100644 index 000000000..d3934f411 --- /dev/null +++ b/src/Common/src/CoreWCF/Channels/ChannelBindingMessagePropertyHelper.cs @@ -0,0 +1,56 @@ +namespace CoreWCF.Channels +{ + internal static class ChannelBindingMessagePropertyHelper + { + internal static bool TryGet(Message message, out ChannelBindingMessageProperty property) + { + if (message == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("message"); + } + + return TryGet(message.Properties, out property); + } + + internal static bool TryGet(MessageProperties properties, out ChannelBindingMessageProperty property) + { + if (properties == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("properties"); + } + + property = null; + object value; + + if (properties.TryGetValue(ChannelBindingMessageProperty.Name, out value)) + { + property = value as ChannelBindingMessageProperty; + return property != null; + } + + return false; + } + + internal static void AddTo(this ChannelBindingMessageProperty channelBindingProperty, Message message) + { + if (message == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("message"); + } + + channelBindingProperty.AddTo(message.Properties); + } + + internal static void AddTo(this ChannelBindingMessageProperty channelBindingProperty, MessageProperties properties) + { + // Throws if disposed + var dummy = channelBindingProperty.ChannelBinding; + if (properties == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("properties"); + } + + properties.Add(ChannelBindingMessageProperty.Name, channelBindingProperty); + } + } +} diff --git a/src/Common/src/CoreWCF/Channels/ChannelBindingUtility.cs b/src/Common/src/CoreWCF/Channels/ChannelBindingUtility.cs new file mode 100644 index 000000000..98f1ce9d6 --- /dev/null +++ b/src/Common/src/CoreWCF/Channels/ChannelBindingUtility.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.Security; +using System.Security.Authentication.ExtendedProtection; +using System.Text; + +namespace CoreWCF.Channels +{ + internal static class ChannelBindingUtility + { + static ExtendedProtectionPolicy disabledPolicy = new ExtendedProtectionPolicy(PolicyEnforcement.Never); + static ExtendedProtectionPolicy defaultPolicy = disabledPolicy; + + public static ExtendedProtectionPolicy DefaultPolicy + { + get + { + return defaultPolicy; + } + } + + public static ChannelBinding GetToken(SslStream stream) + { + return GetToken(stream.TransportContext); + } + + public static ChannelBinding GetToken(TransportContext context) + { + ChannelBinding token = null; + if (context != null) + { + token = context.GetChannelBinding(ChannelBindingKind.Endpoint); + } + return token; + } + + public static void TryAddToMessage(ChannelBinding channelBindingToken, Message message, bool messagePropertyOwnsCleanup) + { + if (channelBindingToken != null) + { + ChannelBindingMessageProperty property = new ChannelBindingMessageProperty(channelBindingToken, messagePropertyOwnsCleanup); + property.AddTo(message); + ((IDisposable)property).Dispose(); //message.Properties.Add() creates a copy... + } + } + + public static void Dispose(ref ChannelBinding channelBinding) + { + // Explicitly cast to IDisposable to avoid the SecurityException. + // I don't think this is relevant on .Net Core but I don't believe + // it will emit any extra code from the JIT. + IDisposable disposable = (IDisposable)channelBinding; + channelBinding = null; + if (disposable != null) + { + disposable.Dispose(); + } + } + } +} diff --git a/src/Common/src/CoreWCF/Channels/DelegatingStream.cs b/src/Common/src/CoreWCF/Channels/DelegatingStream.cs new file mode 100644 index 000000000..8ff500e5a --- /dev/null +++ b/src/Common/src/CoreWCF/Channels/DelegatingStream.cs @@ -0,0 +1,129 @@ +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace CoreWCF.Channels +{ + abstract class DelegatingStream : Stream + { + Stream _stream; + private bool _disposed; + + protected DelegatingStream(Stream stream) + { + if (stream == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(stream)); + } + + _stream = stream; + } + + protected Stream BaseStream => _stream; + + public override bool CanRead => _stream.CanRead; + + public override bool CanSeek => _stream.CanSeek; + + public override bool CanTimeout => _stream.CanTimeout; + + public override bool CanWrite => _stream.CanWrite; + + public override long Length => _stream.Length; + + public override long Position + { + get + { + return _stream.Position; + } + set + { + _stream.Position = value; + } + } + + public override int ReadTimeout + { + get + { + return _stream.ReadTimeout; + } + set + { + _stream.ReadTimeout = value; + } + } + + public override int WriteTimeout + { + get + { + return _stream.WriteTimeout; + } + set + { + _stream.WriteTimeout = value; + } + } + + protected override void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + _stream.Dispose(); + } + + _disposed = true; + } + base.Dispose(disposing); + } + + public override void Flush() + { + _stream.Flush(); + } + + public override int Read(byte[] buffer, int offset, int count) + { + return _stream.Read(buffer, offset, count); + } + + public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + return _stream.ReadAsync(buffer, offset, count, cancellationToken); + } + + public override int ReadByte() + { + return _stream.ReadByte(); + } + + public override long Seek(long offset, SeekOrigin origin) + { + return _stream.Seek(offset, origin); + } + + public override void SetLength(long value) + { + _stream.SetLength(value); + } + + public override void Write(byte[] buffer, int offset, int count) + { + _stream.Write(buffer, offset, count); + } + + public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + return _stream.WriteAsync(buffer, offset, count, cancellationToken); + } + + public override void WriteByte(byte value) + { + _stream.WriteByte(value); + } + } +} \ No newline at end of file diff --git a/src/Common/src/CoreWCF/Channels/DnsCache.cs b/src/Common/src/CoreWCF/Channels/DnsCache.cs new file mode 100644 index 000000000..00569e3ff --- /dev/null +++ b/src/Common/src/CoreWCF/Channels/DnsCache.cs @@ -0,0 +1,130 @@ +using System; +using System.Net; +using System.Net.Sockets; +using CoreWCF.Collections.Generic; + +namespace CoreWCF.Channels +{ + static class DnsCache + { + const int mruWatermark = 64; + static MruCache resolveCache = new MruCache(mruWatermark); + static readonly TimeSpan cacheTimeout = TimeSpan.FromSeconds(2); + + // Double-checked locking pattern requires volatile for read/write synchronization + static volatile string machineName; + + static object ThisLock + { + get + { + return resolveCache; + } + } + + public static string MachineName + { + get + { + if (machineName == null) + { + lock (ThisLock) + { + if (machineName == null) + { + machineName = Dns.GetHostEntryAsync(string.Empty).GetAwaiter().GetResult().HostName; + } + } + } + + return machineName; + } + } + + // TODO: Convert to Async as Dns.GetHostEntry is now async + public static IPHostEntry Resolve(Uri uri) + { + string hostName = uri.DnsSafeHost; + IPHostEntry hostEntry = null; + DateTime now = DateTime.UtcNow; + + lock (ThisLock) + { + DnsCacheEntry cacheEntry; + if (resolveCache.TryGetValue(hostName, out cacheEntry)) + { + if (now.Subtract(cacheEntry.TimeStamp) > cacheTimeout) + { + resolveCache.Remove(hostName); + } + else + { + if (cacheEntry.HostEntry == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new EndpointNotFoundException(SR.Format(SR.DnsResolveFailed, hostName))); + } + hostEntry = cacheEntry.HostEntry; + } + } + } + + if (hostEntry == null) + { + SocketException dnsException = null; + try + { + hostEntry = Dns.GetHostEntryAsync(hostName).GetAwaiter().GetResult(); + } + catch (SocketException e) + { + dnsException = e; + } + + lock (ThisLock) + { + // MruCache doesn't have a this[] operator, so we first remove (just in case it exists already) + resolveCache.Remove(hostName); + resolveCache.Add(hostName, new DnsCacheEntry(hostEntry, now)); + } + + if (dnsException != null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new EndpointNotFoundException(SR.Format(SR.DnsResolveFailed, hostName), dnsException)); + } + } + + return hostEntry; + } + + class DnsCacheEntry + { + IPHostEntry hostEntry; + DateTime timeStamp; + + public DnsCacheEntry(IPHostEntry hostEntry, DateTime timeStamp) + { + this.hostEntry = hostEntry; + this.timeStamp = timeStamp; + } + + public IPHostEntry HostEntry + { + get + { + return hostEntry; + } + } + + public DateTime TimeStamp + { + get + { + return timeStamp; + } + } + } + } + +} \ No newline at end of file diff --git a/src/Common/src/CoreWCF/Channels/MaxMessageSizeStream.cs b/src/Common/src/CoreWCF/Channels/MaxMessageSizeStream.cs new file mode 100644 index 000000000..c076ced8c --- /dev/null +++ b/src/Common/src/CoreWCF/Channels/MaxMessageSizeStream.cs @@ -0,0 +1,111 @@ +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace CoreWCF.Channels +{ + internal class MaxMessageSizeStream : DelegatingStream + { + private long _maxMessageSize; + private long _totalBytesRead; + private long _bytesWritten; + + public MaxMessageSizeStream(Stream stream, long maxMessageSize) + : base(stream) + { + _maxMessageSize = maxMessageSize; + } + + public override async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + count = PrepareRead(count); + int bytesRead = await base.ReadAsync(buffer, offset, count, cancellationToken); + return FinishRead(bytesRead); + } + + public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + PrepareWrite(count); + return base.WriteAsync(buffer, offset, count, cancellationToken); + } + + public override int Read(byte[] buffer, int offset, int count) + { + count = PrepareRead(count); + return FinishRead(base.Read(buffer, offset, count)); + } + + public override int ReadByte() + { + PrepareRead(1); + int i = base.ReadByte(); + if (i != -1) + FinishRead(1); + return i; + } + + public override void Write(byte[] buffer, int offset, int count) + { + PrepareWrite(count); + base.Write(buffer, offset, count); + } + + public override void WriteByte(byte value) + { + PrepareWrite(1); + base.WriteByte(value); + } + + public static Exception CreateMaxReceivedMessageSizeExceededException(long maxMessageSize) + { + string message = SR.Format(SR.MaxReceivedMessageSizeExceeded, maxMessageSize); + Exception inner = new QuotaExceededException(message); + + return new CommunicationException(message, inner); + } + + internal static Exception CreateMaxSentMessageSizeExceededException(long maxMessageSize) + { + string message = SR.Format(SR.MaxSentMessageSizeExceeded, maxMessageSize); + Exception inner = new QuotaExceededException(message); + + return new CommunicationException(message, inner); + } + + private int PrepareRead(int bytesToRead) + { + if (_totalBytesRead >= _maxMessageSize) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateMaxReceivedMessageSizeExceededException(_maxMessageSize)); + } + + long bytesRemaining = _maxMessageSize - _totalBytesRead; + + if (bytesRemaining > int.MaxValue) + { + return bytesToRead; + } + else + { + return Math.Min(bytesToRead, (int)(_maxMessageSize - _totalBytesRead)); + } + } + + private int FinishRead(int bytesRead) + { + _totalBytesRead += bytesRead; + return bytesRead; + } + + private void PrepareWrite(int bytesToWrite) + { + if (_bytesWritten + bytesToWrite > _maxMessageSize) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateMaxSentMessageSizeExceededException(_maxMessageSize)); + } + + _bytesWritten += bytesToWrite; + } + } +} \ No newline at end of file diff --git a/src/Common/src/CoreWCF/Channels/RequestContextBase.cs b/src/Common/src/CoreWCF/Channels/RequestContextBase.cs new file mode 100644 index 000000000..0ecd586ed --- /dev/null +++ b/src/Common/src/CoreWCF/Channels/RequestContextBase.cs @@ -0,0 +1,311 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using CoreWCF.Runtime; +using CoreWCF.Diagnostics; +using System.Diagnostics; + +namespace CoreWCF.Channels +{ + internal abstract class RequestContextBase : RequestContext + { + TimeSpan defaultSendTimeout; + TimeSpan defaultCloseTimeout; + CommunicationState state = CommunicationState.Opened; + Message requestMessage; + Exception requestMessageException; + bool replySent; + bool replyInitiated; + bool aborted; + object thisLock = new object(); + + protected RequestContextBase(Message requestMessage, TimeSpan defaultCloseTimeout, TimeSpan defaultSendTimeout) + { + this.defaultSendTimeout = defaultSendTimeout; + this.defaultCloseTimeout = defaultCloseTimeout; + this.requestMessage = requestMessage; + } + + public void ReInitialize(Message requestMessage) + { + state = CommunicationState.Opened; + requestMessageException = null; + replySent = false; + replyInitiated = false; + aborted = false; + this.requestMessage = requestMessage; + } + + public override Message RequestMessage + { + get + { + if (requestMessageException != null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(requestMessageException); + } + + return requestMessage; + } + } + + protected void SetRequestMessage(Message requestMessage) + { + Fx.Assert(requestMessageException == null, "Cannot have both a requestMessage and a requestException."); + this.requestMessage = requestMessage; + } + + protected void SetRequestMessage(Exception requestMessageException) + { + Fx.Assert(requestMessage == null, "Cannot have both a requestMessage and a requestException."); + this.requestMessageException = requestMessageException; + } + + protected bool ReplyInitiated + { + get { return replyInitiated; } + } + + protected object ThisLock + { + get + { + return thisLock; + } + } + + public bool Aborted + { + get + { + return aborted; + } + } + + public TimeSpan DefaultCloseTimeout + { + get { return defaultCloseTimeout; } + } + + public TimeSpan DefaultSendTimeout + { + get { return defaultSendTimeout; } + } + + public override void Abort() + { + lock (ThisLock) + { + if (state == CommunicationState.Closed) + return; + + state = CommunicationState.Closing; + + aborted = true; + } + + //if (DiagnosticUtility.ShouldTraceWarning) + //{ + // TraceUtility.TraceEvent(TraceEventType.Warning, TraceCode.RequestContextAbort, + // SR.Format(SR.TraceCodeRequestContextAbort), this); + //} + + try + { + OnAbort(); + } + finally + { + state = CommunicationState.Closed; + } + } + + public override Task CloseAsync() + { + var helper = new TimeoutHelper(defaultCloseTimeout); + return CloseAsync(helper.GetCancellationToken()); + } + + public override async Task CloseAsync(CancellationToken token) + { + bool sendAck = false; + lock (ThisLock) + { + if (state != CommunicationState.Opened) + return; + + if (TryInitiateReply()) + { + sendAck = true; + } + + state = CommunicationState.Closing; + } + + bool throwing = true; + + try + { + if (sendAck) + { + await OnReplyAsync(null, token); + } + + await OnCloseAsync(token); + state = CommunicationState.Closed; + throwing = false; + } + finally + { + if (throwing) + Abort(); + } + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + + if (!disposing) + return; + + if (replySent) + { + CloseAsync().GetAwaiter().GetResult(); + } + else + { + Abort(); + } + } + + protected abstract void OnAbort(); + protected abstract Task OnCloseAsync(CancellationToken token); + protected abstract Task OnReplyAsync(Message message, CancellationToken token); + + protected void ThrowIfInvalidReply() + { + if (state == CommunicationState.Closed || state == CommunicationState.Closing) + { + if (aborted) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationObjectAbortedException(SR.RequestContextAborted)); + else + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ObjectDisposedException(GetType().FullName)); + } + + if (replyInitiated) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.ReplyAlreadySent)); + } + + /// + /// Attempts to initiate the reply. If a reply is not initiated already (and the object is opened), + /// then it initiates the reply and returns true. Otherwise, it returns false. + /// + protected bool TryInitiateReply() + { + lock (thisLock) + { + if ((state != CommunicationState.Opened) || replyInitiated) + { + return false; + } + else + { + replyInitiated = true; + return true; + } + } + } + + public override Task ReplyAsync(Message message) + { + var helper = new TimeoutHelper(defaultSendTimeout); + return ReplyAsync(message, helper.GetCancellationToken()); + } + + public override async Task ReplyAsync(Message message, CancellationToken token) + { + // "null" is a valid reply (signals a 202-style "ack"), so we don't have a null-check here + lock (thisLock) + { + ThrowIfInvalidReply(); + replyInitiated = true; + } + + await OnReplyAsync(message, token); + replySent = true; + } + + // This method is designed for WebSocket only, and will only be used once the WebSocket response was sent. + // For WebSocket, we never call HttpRequestContext.Reply to send the response back. + // Instead we call AcceptWebSocket directly. So we need to set the replyInitiated and + // replySent boolean to be true once the response was sent successfully. Otherwise when we + // are disposing the HttpRequestContext, we will see a bunch of warnings in trace log. + protected void SetReplySent() + { + lock (thisLock) + { + ThrowIfInvalidReply(); + replyInitiated = true; + } + + replySent = true; + } + } + + class RequestContextMessageProperty : IDisposable + { + RequestContext context; + object thisLock = new object(); + + public RequestContextMessageProperty(RequestContext context) + { + this.context = context; + } + + public static string Name + { + get { return "requestContext"; } + } + + void IDisposable.Dispose() + { + bool success = false; + RequestContext thisContext; + + lock (thisLock) + { + if (context == null) + return; + thisContext = context; + context = null; + } + + try + { + thisContext.CloseAsync().GetAwaiter().GetResult(); + success = true; + } + catch (CommunicationException e) + { + DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); + } + catch (TimeoutException e) + { + //if (TD.CloseTimeoutIsEnabled()) + //{ + // TD.CloseTimeout(e.Message); + //} + DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); + } + finally + { + if (!success) + { + thisContext.Abort(); + } + } + } + } + +} \ No newline at end of file diff --git a/src/Common/src/CoreWCF/Channels/UriPrefixTable.cs b/src/Common/src/CoreWCF/Channels/UriPrefixTable.cs new file mode 100644 index 000000000..a75287325 --- /dev/null +++ b/src/Common/src/CoreWCF/Channels/UriPrefixTable.cs @@ -0,0 +1,529 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using CoreWCF.Runtime; +using CoreWCF.Runtime.Collections; + +namespace CoreWCF.Channels +{ + internal sealed class UriPrefixTable + where TItem : class + { + int count; + const int HopperSize = 128; + volatile HopperCache lookupCache; // cache matches, for lookup speed + AsyncLock _asyncLock = new AsyncLock(); + + SegmentHierarchyNode root; + bool useWeakReferences; + bool includePortInComparison; + + public UriPrefixTable() + : this(false) + { + } + + public UriPrefixTable(bool includePortInComparison) + : this(includePortInComparison, false) + { + } + + public UriPrefixTable(bool includePortInComparison, bool useWeakReferences) + { + this.includePortInComparison = includePortInComparison; + this.useWeakReferences = useWeakReferences; + root = new SegmentHierarchyNode(null, useWeakReferences); + lookupCache = new HopperCache(HopperSize, useWeakReferences); + } + + internal UriPrefixTable(UriPrefixTable objectToClone) + : this(objectToClone.includePortInComparison, objectToClone.useWeakReferences) + { + if (objectToClone.Count > 0) + { + foreach (KeyValuePair current in objectToClone.GetAll()) + { + RegisterUri(current.Key.BaseAddress, current.Key.HostNameComparisonMode, current.Value); + } + } + } + + object ThisLock + { + get + { + // The UriPrefixTable instance itself is used as a + // synchronization primitive in the TransportManagers and the + // TransportManagerContainers so we return 'this' to keep them in sync. + return this; + } + } + + public int Count + { + get + { + return count; + } + } + + public AsyncLock AsyncLock { get { return _asyncLock; } } + + public bool IsRegistered(BaseUriWithWildcard key) + { + Uri uri = key.BaseAddress; + + // don't need to normalize path since SegmentHierarchyNode is + // already OrdinalIgnoreCase + string[] paths = UriSegmenter.ToPath(uri, key.HostNameComparisonMode, includePortInComparison); + bool exactMatch; + SegmentHierarchyNode node; + using (AsyncLock.TakeLock()) + { + node = FindDataNode(paths, out exactMatch); + } + return exactMatch && node != null && node.Data != null; + } + + public IEnumerable> GetAll() + { + using (AsyncLock.TakeLock()) + { + List> result = new List>(); + root.Collect(result); + return result; + } + } + + bool TryCacheLookup(BaseUriWithWildcard key, out TItem item) + { + object value = lookupCache.GetValue(ThisLock, key); + + // We might return null and true in the case of DBNull (cached negative result). + // When TItem is object, the cast isn't sufficient to weed out DBNulls, so we need an explicit check. + item = value == DBNull.Value ? null : (TItem)value; + return value != null; + } + + void AddToCache(BaseUriWithWildcard key, TItem item) + { + // Don't allow explicitly adding DBNulls. + Fx.Assert(item != DBNull.Value, "Can't add DBNull to UriPrefixTable."); + + // HopperCache uses null as 'doesn't exist', so use DBNull as a stand-in for null. + lookupCache.Add(key, item ?? (object)DBNull.Value); + } + + void ClearCache() + { + lookupCache = new HopperCache(HopperSize, useWeakReferences); + } + + public bool TryLookupUri(Uri uri, HostNameComparisonMode hostNameComparisonMode, out TItem item) + { + BaseUriWithWildcard key = new BaseUriWithWildcard(uri, hostNameComparisonMode); + if (TryCacheLookup(key, out item)) + { + return item != null; + } + + using (AsyncLock.TakeLock()) + { + // exact match failed, perform the full lookup (which will also + // catch case-insensitive variations that aren't yet in our cache) + bool dummy; + SegmentHierarchyNode node = FindDataNode( + UriSegmenter.ToPath(key.BaseAddress, hostNameComparisonMode, includePortInComparison), out dummy); + if (node != null) + { + item = node.Data; + } + // We want to cache both positive AND negative results + AddToCache(key, item); + return (item != null); + } + } + + public void RegisterUri(Uri uri, HostNameComparisonMode hostNameComparisonMode, TItem item) + { + Fx.Assert(HostNameComparisonModeHelper.IsDefined(hostNameComparisonMode), "RegisterUri: Invalid HostNameComparisonMode value passed in."); + + using (AsyncLock.TakeLock()) + { + // Since every newly registered Uri could alter what Prefixes should have matched, we + // should clear the cache of any existing results and start over + ClearCache(); + BaseUriWithWildcard key = new BaseUriWithWildcard(uri, hostNameComparisonMode); + SegmentHierarchyNode node = FindOrCreateNode(key); + if (node.Data != null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format( + SR.DuplicateRegistration, uri))); + } + node.SetData(item, key); + count++; + } + } + + public void UnregisterUri(Uri uri, HostNameComparisonMode hostNameComparisonMode) + { + using (AsyncLock.TakeLock()) + { + // Since every removed Uri could alter what Prefixes should have matched, we + // should clear the cache of any existing results and start over + ClearCache(); + string[] path = UriSegmenter.ToPath(uri, hostNameComparisonMode, includePortInComparison); + // Never remove the root + if (path.Length == 0) + { + root.RemoveData(); + } + else + { + root.RemovePath(path, 0); + } + count--; + } + } + + SegmentHierarchyNode FindDataNode(string[] path, out bool exactMatch) + { + Fx.Assert(path != null, "FindDataNode: path is null"); + + exactMatch = false; + SegmentHierarchyNode current = root; + SegmentHierarchyNode result = null; + for (int i = 0; i < path.Length; ++i) + { + SegmentHierarchyNode next; + if (!current.TryGetChild(path[i], out next)) + { + break; + } + else if (next.Data != null) + { + result = next; + exactMatch = (i == path.Length - 1); + } + current = next; + } + return result; + } + + SegmentHierarchyNode FindOrCreateNode(BaseUriWithWildcard baseUri) + { + Fx.Assert(baseUri != null, "FindOrCreateNode: baseUri is null"); + + string[] path = UriSegmenter.ToPath(baseUri.BaseAddress, baseUri.HostNameComparisonMode, includePortInComparison); + SegmentHierarchyNode current = root; + for (int i = 0; i < path.Length; ++i) + { + SegmentHierarchyNode next; + if (!current.TryGetChild(path[i], out next)) + { + next = new SegmentHierarchyNode(path[i], useWeakReferences); + current.SetChildNode(path[i], next); + } + current = next; + } + return current; + } + + static class UriSegmenter + { + internal static string[] ToPath(Uri uriPath, HostNameComparisonMode hostNameComparisonMode, + bool includePortInComparison) + { + if (null == uriPath) + { + return new string[0]; + } + UriSegmentEnum segmentEnum = new UriSegmentEnum(uriPath); // struct + return segmentEnum.GetSegments(hostNameComparisonMode, includePortInComparison); + } + + struct UriSegmentEnum + { + string segment; + int segmentStartAt; + int segmentLength; + UriSegmentType type; + Uri uri; + + internal UriSegmentEnum(Uri uri) + { + Fx.Assert(null != uri, "UreSegmentEnum: null uri"); + this.uri = uri; + type = UriSegmentType.Unknown; + segment = null; + segmentStartAt = 0; + segmentLength = 0; + } + + void ClearSegment() + { + type = UriSegmentType.None; + segment = string.Empty; + segmentStartAt = 0; + segmentLength = 0; + } + + public string[] GetSegments(HostNameComparisonMode hostNameComparisonMode, + bool includePortInComparison) + { + List segments = new List(); + while (Next()) + { + switch (type) + { + case UriSegmentType.Path: + segments.Add(segment.Substring(segmentStartAt, segmentLength)); + break; + + case UriSegmentType.Host: + if (hostNameComparisonMode == HostNameComparisonMode.StrongWildcard) + { + segments.Add("+"); + } + else if (hostNameComparisonMode == HostNameComparisonMode.Exact) + { + segments.Add(segment); + } + else + { + segments.Add("*"); + } + break; + + case UriSegmentType.Port: + if (includePortInComparison || hostNameComparisonMode == HostNameComparisonMode.Exact) + { + segments.Add(segment); + } + break; + + default: + segments.Add(segment); + break; + } + } + return segments.ToArray(); + } + + public bool Next() + { + while (true) + { + switch (type) + { + case UriSegmentType.Unknown: + type = UriSegmentType.Scheme; + SetSegment(uri.Scheme); + return true; + + case UriSegmentType.Scheme: + type = UriSegmentType.Host; + string host = uri.Host; + // The userName+password also accompany... + string userInfo = uri.UserInfo; + if (null != userInfo && userInfo.Length > 0) + { + host = userInfo + '@' + host; + } + SetSegment(host); + return true; + + case UriSegmentType.Host: + type = UriSegmentType.Port; + int port = uri.Port; + SetSegment(port.ToString(CultureInfo.InvariantCulture)); + return true; + + case UriSegmentType.Port: + type = UriSegmentType.Path; + string absPath = uri.AbsolutePath; + Fx.Assert(null != absPath, "Next: nill absPath"); + if (0 == absPath.Length) + { + ClearSegment(); + return false; + } + segment = absPath; + segmentStartAt = 0; + segmentLength = 0; + return NextPathSegment(); + + case UriSegmentType.Path: + return NextPathSegment(); + + case UriSegmentType.None: + return false; + + default: + Fx.Assert("Next: unknown enum value"); + return false; + } + } + } + + public bool NextPathSegment() + { + segmentStartAt += segmentLength; + while (segmentStartAt < segment.Length && segment[segmentStartAt] == '/') + { + segmentStartAt++; + } + + if (segmentStartAt < segment.Length) + { + int next = segment.IndexOf('/', segmentStartAt); + if (-1 == next) + { + segmentLength = segment.Length - segmentStartAt; + } + else + { + segmentLength = next - segmentStartAt; + } + return true; + } + ClearSegment(); + return false; + } + + void SetSegment(string segment) + { + this.segment = segment; + segmentStartAt = 0; + segmentLength = segment.Length; + } + + enum UriSegmentType + { + Unknown, + Scheme, + Host, + Port, + Path, + None + } + } + } + } + + class SegmentHierarchyNode + where TData : class + { + BaseUriWithWildcard path; + TData data; + string name; + Dictionary> children; + WeakReference weakData; + bool useWeakReferences; + + public SegmentHierarchyNode(string name, bool useWeakReferences) + { + this.name = name; + this.useWeakReferences = useWeakReferences; + children = new Dictionary>(StringComparer.OrdinalIgnoreCase); + } + + public TData Data + { + get + { + if (useWeakReferences) + { + if (weakData == null) + { + return null; + } + else + { + return weakData.Target as TData; + } + } + else + { + return data; + } + } + } + + public void SetData(TData data, BaseUriWithWildcard path) + { + this.path = path; + if (useWeakReferences) + { + if (data == null) + { + weakData = null; + } + else + { + weakData = new WeakReference(data); + } + } + else + { + this.data = data; + } + } + + public void SetChildNode(string name, SegmentHierarchyNode node) + { + children[name] = node; + } + + public void Collect(List> result) + { + TData localData = Data; + if (localData != null) + { + result.Add(new KeyValuePair(path, localData)); + } + + foreach (SegmentHierarchyNode child in children.Values) + { + child.Collect(result); + } + } + + public bool TryGetChild(string segment, out SegmentHierarchyNode value) + { + return children.TryGetValue(segment, out value); + } + + public void RemoveData() + { + SetData(null, null); + } + + // bool is whether to remove this node + public bool RemovePath(string[] path, int seg) + { + if (seg == path.Length) + { + RemoveData(); + return children.Count == 0; + } + + SegmentHierarchyNode node; + if (!TryGetChild(path[seg], out node)) + { + return (children.Count == 0 && Data == null); + } + + if (node.RemovePath(path, seg + 1)) + { + children.Remove(path[seg]); + return (children.Count == 0 && Data == null); + } + else + { + return false; + } + } + } + +} \ No newline at end of file diff --git a/src/Common/src/CoreWCF/Collections/Generic/MruCache.cs b/src/Common/src/CoreWCF/Collections/Generic/MruCache.cs new file mode 100644 index 000000000..8c5541838 --- /dev/null +++ b/src/Common/src/CoreWCF/Collections/Generic/MruCache.cs @@ -0,0 +1,170 @@ +using System.Collections.Generic; +using CoreWCF.Runtime; + +namespace CoreWCF.Collections.Generic +{ + class MruCache + where TKey : class + where TValue : class + { + LinkedList _mruList; + Dictionary _items; + int _lowWatermark; + int _highWatermark; + CacheEntry _mruEntry; + + public MruCache(int watermark) + : this(watermark * 4 / 5, watermark) + { + } + + // + // The cache will grow until the high watermark. At which point, the least recently used items + // will be purge until the cache's size is reduced to low watermark + // + public MruCache(int lowWatermark, int highWatermark) + : this(lowWatermark, highWatermark, null) + { + } + + public MruCache(int lowWatermark, int highWatermark, IEqualityComparer comparer) + { + Fx.Assert(lowWatermark < highWatermark, ""); + Fx.Assert(lowWatermark >= 0, ""); + + _lowWatermark = lowWatermark; + _highWatermark = highWatermark; + _mruList = new LinkedList(); + if (comparer == null) + { + _items = new Dictionary(); + } + else + { + _items = new Dictionary(comparer); + } + } + + public int Count + { + get + { + return _items.Count; + } + } + + public void Add(TKey key, TValue value) + { + Fx.Assert(null != key, ""); + + // if anything goes wrong (duplicate entry, etc) we should + // clear our caches so that we don't get out of sync + bool success = false; + try + { + if (_items.Count == _highWatermark) + { + // If the cache is full, purge enough LRU items to shrink the + // cache down to the low watermark + int countToPurge = _highWatermark - _lowWatermark; + for (int i = 0; i < countToPurge; i++) + { + TKey keyRemove = _mruList.Last.Value; + _mruList.RemoveLast(); + TValue item = _items[keyRemove].value; + _items.Remove(keyRemove); + OnSingleItemRemoved(item); + OnItemAgedOutOfCache(item); + } + } + // Add the new entry to the cache and make it the MRU element + CacheEntry entry; + entry.node = _mruList.AddFirst(key); + entry.value = value; + _items.Add(key, entry); + _mruEntry = entry; + success = true; + } + finally + { + if (!success) + { + Clear(); + } + } + } + + public void Clear() + { + _mruList.Clear(); + _items.Clear(); + _mruEntry.value = null; + _mruEntry.node = null; + } + + public bool Remove(TKey key) + { + Fx.Assert(null != key, ""); + + CacheEntry entry; + if (_items.TryGetValue(key, out entry)) + { + _items.Remove(key); + OnSingleItemRemoved(entry.value); + _mruList.Remove(entry.node); + if (ReferenceEquals(_mruEntry.node, entry.node)) + { + _mruEntry.value = null; + _mruEntry.node = null; + } + return true; + } + + return false; + } + + protected virtual void OnSingleItemRemoved(TValue item) + { + } + + protected virtual void OnItemAgedOutOfCache(TValue item) + { + } + + // + // If found, make the entry most recently used + // + public bool TryGetValue(TKey key, out TValue value) + { + // first check our MRU item + if (_mruEntry.node != null && key != null && key.Equals(_mruEntry.node.Value)) + { + value = _mruEntry.value; + return true; + } + + CacheEntry entry; + + bool found = _items.TryGetValue(key, out entry); + value = entry.value; + + // Move the node to the head of the MRU list if it's not already there + if (found && _mruList.Count > 1 + && !ReferenceEquals(_mruList.First, entry.node)) + { + _mruList.Remove(entry.node); + _mruList.AddFirst(entry.node); + _mruEntry = entry; + } + + return found; + } + + struct CacheEntry + { + internal TValue value; + internal LinkedListNode node; + } + } + +} \ No newline at end of file diff --git a/src/Common/src/CoreWCF/HostNameComparisonModeHelper.cs b/src/Common/src/CoreWCF/HostNameComparisonModeHelper.cs new file mode 100644 index 000000000..a282f49e6 --- /dev/null +++ b/src/Common/src/CoreWCF/HostNameComparisonModeHelper.cs @@ -0,0 +1,25 @@ +using CoreWCF; +using System.ComponentModel; + +namespace CoreWCF +{ + internal static class HostNameComparisonModeHelper + { + internal static bool IsDefined(HostNameComparisonMode value) + { + return + value == HostNameComparisonMode.StrongWildcard + || value == HostNameComparisonMode.Exact + || value == HostNameComparisonMode.WeakWildcard; + } + + public static void Validate(HostNameComparisonMode value) + { + if (!IsDefined(value)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidEnumArgumentException("value", (int)value, + typeof(HostNameComparisonMode))); + } + } + } +} diff --git a/src/Common/src/CoreWCF/ProtocolExceptionHelper.cs b/src/Common/src/CoreWCF/ProtocolExceptionHelper.cs new file mode 100644 index 000000000..604171b67 --- /dev/null +++ b/src/Common/src/CoreWCF/ProtocolExceptionHelper.cs @@ -0,0 +1,34 @@ +using System; +using System.Globalization; +using System.Runtime.Serialization; +using CoreWCF.Channels; + +namespace CoreWCF +{ + internal static class ProtocolExceptionHelper + { + internal static ProtocolException ReceiveShutdownReturnedNonNull(Message message) + { + if (message.IsFault) + { + try + { + MessageFault fault = MessageFault.CreateFault(message, 64 * 1024); + FaultReasonText reason = fault.Reason.GetMatchingTranslation(CultureInfo.CurrentCulture); + string text = SR.Format(SR.ReceiveShutdownReturnedFault, reason.Text); + return new ProtocolException(text); + } + catch (QuotaExceededException) + { + string text = SR.Format(SR.ReceiveShutdownReturnedLargeFault, message.Headers.Action); + return new ProtocolException(text); + } + } + else + { + string text = SR.Format(SR.ReceiveShutdownReturnedMessage, message.Headers.Action); + return new ProtocolException(text); + } + } + } +} \ No newline at end of file diff --git a/src/Common/src/CoreWCF/Runtime/AsyncLock.cs b/src/Common/src/CoreWCF/Runtime/AsyncLock.cs new file mode 100644 index 000000000..9ee318b6b --- /dev/null +++ b/src/Common/src/CoreWCF/Runtime/AsyncLock.cs @@ -0,0 +1,130 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +#if DEBUG +using System.Diagnostics; +#endif + +namespace CoreWCF.Runtime +{ + internal class AsyncLock + { +#if DEBUG + private StackTrace _lockTakenCallStack; + private string _lockTakenCallStackString; +#endif + private readonly SemaphoreSlim _semaphore; + private readonly SafeSemaphoreRelease _semaphoreRelease; + private AsyncLocal _lockTaken; + + public AsyncLock() + { + _semaphore = new SemaphoreSlim(1); + _semaphoreRelease = new SafeSemaphoreRelease(this); + _lockTaken = new AsyncLocal(LockTakenValueChanged); + _lockTaken.Value = false; + } + + private void LockTakenValueChanged(AsyncLocalValueChangedArgs obj) + { + // Without this fixup, when completing the call to await TakeLockAsync there is + // a switch of Context and _localTaken will be reset to false. This is because + // of leaving the task. + + if (obj.ThreadContextChanged) + { + _lockTaken.Value = obj.PreviousValue; + } + } + + public async Task TakeLockAsync() + { + if (_lockTaken.Value) + return null; + + await _semaphore.WaitAsync(); + _lockTaken.Value = true; +#if DEBUG + _lockTakenCallStack = new StackTrace(); + _lockTakenCallStackString = _lockTakenCallStack.ToString(); +#endif + return _semaphoreRelease; + } + + public async Task TakeLockAsync(CancellationToken token) + { + if (_lockTaken.Value) + return null; + + await _semaphore.WaitAsync(token); + _lockTaken.Value = true; +#if DEBUG + _lockTakenCallStack = new StackTrace(); + _lockTakenCallStackString = _lockTakenCallStack.ToString(); +#endif + return _semaphoreRelease; + } + + public IDisposable TakeLock() + { + if (_lockTaken.Value) + return null; + + _semaphore.Wait(); + _lockTaken.Value = true; +#if DEBUG + _lockTakenCallStack = new StackTrace(); + _lockTakenCallStackString = _lockTakenCallStack.ToString(); +#endif + return _semaphoreRelease; + } + + public IDisposable TakeLock(TimeSpan timeout) + { + if (_lockTaken.Value) + return null; + + _semaphore.Wait(timeout); + _lockTaken.Value = true; +#if DEBUG + _lockTakenCallStack = new StackTrace(); + _lockTakenCallStackString = _lockTakenCallStack.ToString(); +#endif + return _semaphoreRelease; + } + + public IDisposable TakeLock(int timeout) + { + if (_lockTaken.Value) + return null; + + _semaphore.Wait(timeout); + _lockTaken.Value = true; +#if DEBUG + _lockTakenCallStack = new StackTrace(); + _lockTakenCallStackString = _lockTakenCallStack.ToString(); +#endif + return _semaphoreRelease; + } + + public struct SafeSemaphoreRelease : IDisposable + { + private readonly AsyncLock _asyncLock; + + public SafeSemaphoreRelease(AsyncLock asyncLock) + { + _asyncLock = asyncLock; + } + + public void Dispose() + { +#if DEBUG + _asyncLock._lockTakenCallStack = null; + _asyncLock._lockTakenCallStackString = null; +#endif + _asyncLock._lockTaken.Value = false; + _asyncLock._semaphore.Release(); + } + } + } +} \ No newline at end of file diff --git a/src/Common/src/CoreWCF/Runtime/Collections/HopperCache.cs b/src/Common/src/CoreWCF/Runtime/Collections/HopperCache.cs new file mode 100644 index 000000000..92b86d462 --- /dev/null +++ b/src/Common/src/CoreWCF/Runtime/Collections/HopperCache.cs @@ -0,0 +1,245 @@ +using System; +using System.Collections; +using System.Threading; + +namespace CoreWCF.Runtime.Collections +{ + // This cache works like a MruCache, but operates loosely and without locks in the mainline path. + // + // It consists of three 'hoppers', which are Hashtables (chosen for their nice threading characteristics - reading + // doesn't require a lock). Items enter the cache in the second hopper. On lookups, cache hits result in the + // cache entry being promoted to the first hopper. When the first hopper is full, the third hopper is dropped, + // and the first and second hoppers are shifted down, leaving an empty first hopper. If the second hopper is + // full when a new cache entry is added, the third hopper is dropped, the second hopper is shifted down, and a + // new second hopper is slotted in to become the new item entrypoint. + // + // Items can only be added and looked up. There's no way to remove an item besides through attrition. + // + // This cache has a built-in concept of weakly-referenced items (which can be enabled or disabled in the + // constructor). It needs this concept since the caller of the cache can't remove dead cache items itself. + // A weak HopperCache will simply ignore dead entries. + // + // This structure allows cache lookups to be almost lock-free. The only time the first hopper is written to + // is when a cache entry is promoted. Promoting a cache entry is not critical - it's ok to skip a promotion. + // Only one promotion is allowed at a time. If a second is attempted, it is skipped. This allows promotions + // to be synchronized with just an Interlocked call. + // + // New cache entries go into the second hopper, which requires a lock, as does shifting the hoppers down. + // + // The hopperSize parameter determines the size of the first hopper. When it reaches this size, the hoppers + // are shifted. The second hopper is allowed to grow to twice this size. This is because it needs room to get + // new cache entries into the system, and the second hopper typically starts out 'full'. Entries are never added + // directly to the third hopper. + // + // It's a error on the part of the caller to add the same key to the cache again if it's already in the cache + // with a different value. The new value will not necessarily overwrite the old value. + // + // If a cache entry is about to be promoted from the third hopper, and in the mean time the third hopper has been + // shifted away, an intervening GetValue for the same key might return null, even though the item is still in + // the cache and a later GetValue might find it. So it's very important never to add the same key to the cache + // with two different values, even if GetValue returns null for the key in-between the first add and the second. + // (If this particular behavior is a problem, it may be possible to tighten up, but it's not necessary for the + // current use of HopperCache - UriPrefixTable.) + internal class HopperCache + { + readonly int hopperSize; + readonly bool weak; + + Hashtable outstandingHopper; + Hashtable strongHopper; + Hashtable limitedHopper; + int promoting; + LastHolder mruEntry; + + + public HopperCache(int hopperSize, bool weak) + { + Fx.Assert(hopperSize > 0, "HopperCache hopperSize must be positive."); + + this.hopperSize = hopperSize; + this.weak = weak; + + outstandingHopper = new Hashtable(hopperSize * 2); + strongHopper = new Hashtable(hopperSize * 2); + limitedHopper = new Hashtable(hopperSize * 2); + } + + // Calls to Add must be synchronized. + public void Add(object key, object value) + { + Fx.Assert(key != null, "HopperCache key cannot be null."); + Fx.Assert(value != null, "HopperCache value cannot be null."); + + // Special-case DBNull since it can never be collected. + if (weak && !object.ReferenceEquals(value, DBNull.Value)) + { + value = new WeakReference(value); + } + + Fx.Assert(strongHopper.Count <= hopperSize * 2, + "HopperCache strongHopper is bigger than it's allowed to get."); + + if (strongHopper.Count >= hopperSize * 2) + { + Hashtable recycled = limitedHopper; + recycled.Clear(); + recycled.Add(key, value); + + // The try/finally is here to make sure these happen without interruption. + try { } + finally + { + limitedHopper = strongHopper; + strongHopper = recycled; + } + } + else + { + // We do nothing to prevent things from getting added multiple times. Also may be writing over + // a dead weak entry. + strongHopper[key] = value; + } + } + + // Calls to GetValue do not need to be synchronized, but the object used to synchronize the Add calls + // must be passed in. It's sometimes used. + public object GetValue(object syncObject, object key) + { + Fx.Assert(key != null, "Can't look up a null key."); + + WeakReference weakRef; + object value; + + // The MruCache does this so we have to too. + LastHolder last = mruEntry; + if (last != null && key.Equals(last.Key)) + { + if (weak && (weakRef = last.Value as WeakReference) != null) + { + value = weakRef.Target; + if (value != null) + { + return value; + } + mruEntry = null; + } + else + { + return last.Value; + } + } + + // Try the first hopper. + object origValue = outstandingHopper[key]; + value = weak && (weakRef = origValue as WeakReference) != null ? weakRef.Target : origValue; + if (value != null) + { + mruEntry = new LastHolder(key, origValue); + return value; + } + + // Try the subsequent hoppers. + origValue = strongHopper[key]; + value = weak && (weakRef = origValue as WeakReference) != null ? weakRef.Target : origValue; + if (value == null) + { + origValue = limitedHopper[key]; + value = weak && (weakRef = origValue as WeakReference) != null ? weakRef.Target : origValue; + if (value == null) + { + // Still no value? It's not here. + return null; + } + } + + mruEntry = new LastHolder(key, origValue); + + // If we can get the promoting semaphore, move up to the outstanding hopper. + int wasPromoting = 1; + try + { + try { } + finally + { + // This is effectively a lock, which is why it uses lock semantics. If the Interlocked call + // were 'lost', the cache wouldn't deadlock, but it would be permanently broken. + wasPromoting = Interlocked.CompareExchange(ref promoting, 1, 0); + } + + // Only one thread can be inside this 'if' at a time. + if (wasPromoting == 0) + { + Fx.Assert(outstandingHopper.Count <= hopperSize, + "HopperCache outstandingHopper is bigger than it's allowed to get."); + + if (outstandingHopper.Count >= hopperSize) + { + lock (syncObject) + { + Hashtable recycled = limitedHopper; + recycled.Clear(); + recycled.Add(key, origValue); + + // The try/finally is here to make sure these happen without interruption. + try { } + finally + { + limitedHopper = strongHopper; + strongHopper = outstandingHopper; + outstandingHopper = recycled; + } + } + } + else + { + // It's easy for this to happen twice with the same key. + // + // It's important that no one else can be shifting the current oustandingHopper + // during this operation. We are only allowed to modify the *current* outstandingHopper + // while holding the pseudo-lock, which would be violated if it could be shifted out from + // under us (and potentially added to by Add in a race). + outstandingHopper[key] = origValue; + } + } + } + finally + { + if (wasPromoting == 0) + { + promoting = 0; + } + } + + return value; + } + + class LastHolder + { + readonly object key; + readonly object value; + + internal LastHolder(object key, object value) + { + this.key = key; + this.value = value; + } + + internal object Key + { + get + { + return key; + } + } + + internal object Value + { + get + { + return value; + } + } + } + } + +} \ No newline at end of file diff --git a/src/Common/src/CoreWCF/Runtime/ExceptionTrace.cs b/src/Common/src/CoreWCF/Runtime/ExceptionTrace.cs new file mode 100644 index 000000000..64a877abd --- /dev/null +++ b/src/Common/src/CoreWCF/Runtime/ExceptionTrace.cs @@ -0,0 +1,164 @@ +using System; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Reflection; +using CoreWCF.Runtime.Diagnostics; + +namespace CoreWCF.Runtime +{ + internal class ExceptionTrace + { + private string _eventSourceName; + private EtwDiagnosticTrace _diagnosticTrace; + + internal ExceptionTrace(string eventSourceName, EtwDiagnosticTrace diagnosticTrace) + { + Fx.Assert(diagnosticTrace != null, "'diagnosticTrace' MUST NOT be NULL."); + + _eventSourceName = eventSourceName; + _diagnosticTrace = diagnosticTrace; + } + + public Exception AsError(Exception exception) + { + // AggregateExceptions are automatically unwrapped. + AggregateException aggregateException = exception as AggregateException; + if (aggregateException != null) + { + return AsError(aggregateException); + } + + // TargetInvocationExceptions are automatically unwrapped. + TargetInvocationException targetInvocationException = exception as TargetInvocationException; + if (targetInvocationException != null && targetInvocationException.InnerException != null) + { + return AsError(targetInvocationException.InnerException); + } + + return TraceException(exception); + } + + public Exception AsError(AggregateException aggregateException) + { + return AsError(aggregateException, _eventSourceName); + } + + public Exception AsError(AggregateException aggregateException, string eventSource) + { + Fx.Assert(aggregateException != null, "aggregateException cannot be null."); + + // If aggregateException contains any fatal exceptions, return it directly + // without tracing it or any inner exceptions. + if (Fx.IsFatal(aggregateException)) + { + return aggregateException; + } + + // Collapse possibly nested graph into a flat list. + // Empty inner exception list is unlikely but possible via public api. + var innerExceptions = aggregateException.Flatten().InnerExceptions; + if (innerExceptions.Count == 0) + { + return TraceException(aggregateException, eventSource); + } + + // Find the first inner exception, giving precedence to TPreferredException + Exception favoredException = null; + foreach (Exception nextInnerException in innerExceptions) + { + // AggregateException may wrap TargetInvocationException, so unwrap those as well + TargetInvocationException targetInvocationException = nextInnerException as TargetInvocationException; + + Exception innerException = (targetInvocationException != null && targetInvocationException.InnerException != null) + ? targetInvocationException.InnerException + : nextInnerException; + + if (innerException is TPreferredException && favoredException == null) + { + favoredException = innerException; + } + + // All inner exceptions are traced + TraceException(innerException, eventSource); + } + + if (favoredException == null) + { + Fx.Assert(innerExceptions.Count > 0, "InnerException.Count is known to be > 0 here."); + favoredException = innerExceptions[0]; + } + + return favoredException; + } + + public ArgumentException Argument(string paramName, string message) + { + return TraceException(new ArgumentException(message, paramName)); + } + + public ArgumentNullException ArgumentNull(string paramName) + { + return TraceException(new ArgumentNullException(paramName)); + } + + TException TraceException(TException exception) + where TException : Exception + { + return TraceException(exception, _eventSourceName); + } + + TException TraceException(TException exception, string eventSource) + where TException : Exception + { + //if (TraceCore.ThrowingExceptionIsEnabled(this.diagnosticTrace)) + //{ + // TraceCore.ThrowingException(this.diagnosticTrace, eventSource, exception != null ? exception.ToString() : string.Empty, exception); + //} + + //BreakOnException(exception); + + return exception; + } + + public ArgumentOutOfRangeException ArgumentOutOfRange(string paramName, object actualValue, string message) + { + return TraceException(new ArgumentOutOfRangeException(paramName, actualValue, message)); + } + + public void TraceUnhandledException(Exception exception) + { + //TraceCore.UnhandledException(this.diagnosticTrace, exception != null ? exception.ToString() : string.Empty, exception); + } + + public void TraceHandledException(Exception exception, TraceEventType traceEventType) + { + //switch (traceEventType) + //{ + // case TraceEventType.Error: + // if (TraceCore.HandledExceptionErrorIsEnabled(this.diagnosticTrace)) + // { + // TraceCore.HandledExceptionError(this.diagnosticTrace, exception != null ? exception.ToString() : string.Empty, exception); + // } + // break; + // case TraceEventType.Warning: + // if (TraceCore.HandledExceptionWarningIsEnabled(this.diagnosticTrace)) + // { + // TraceCore.HandledExceptionWarning(this.diagnosticTrace, exception != null ? exception.ToString() : string.Empty, exception); + // } + // break; + // case TraceEventType.Verbose: + // if (TraceCore.HandledExceptionVerboseIsEnabled(this.diagnosticTrace)) + // { + // TraceCore.HandledExceptionVerbose(this.diagnosticTrace, exception != null ? exception.ToString() : string.Empty, exception); + // } + // break; + // default: + // if (TraceCore.HandledExceptionIsEnabled(this.diagnosticTrace)) + // { + // TraceCore.HandledException(this.diagnosticTrace, exception != null ? exception.ToString() : string.Empty, exception); + // } + // break; + //} + } + } +} \ No newline at end of file diff --git a/src/Common/src/CoreWCF/Runtime/FatalException.cs b/src/Common/src/CoreWCF/Runtime/FatalException.cs new file mode 100644 index 000000000..be74702b7 --- /dev/null +++ b/src/Common/src/CoreWCF/Runtime/FatalException.cs @@ -0,0 +1,27 @@ +using System; +using System.Diagnostics.Contracts; + +namespace CoreWCF.Runtime +{ + // TODO: This is in internals so probably needs to go away + class FatalException : Exception //SystemException + { + public FatalException() + { + } + public FatalException(string message) : base(message) + { + } + + public FatalException(string message, Exception innerException) : base(message, innerException) + { + // This can't throw something like ArgumentException because that would be worse than + // throwing the fatal exception that was requested. + Contract.Assert(innerException == null || !Fx.IsFatal(innerException), "FatalException can't be used to wrap fatal exceptions."); + } + + //protected FatalException(SerializationInfo info, StreamingContext context) : base(info, context) + //{ + //} + } +} \ No newline at end of file diff --git a/src/Common/src/CoreWCF/Runtime/Fx.cs b/src/Common/src/CoreWCF/Runtime/Fx.cs new file mode 100644 index 000000000..590bdc06c --- /dev/null +++ b/src/Common/src/CoreWCF/Runtime/Fx.cs @@ -0,0 +1,455 @@ +using System; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Diagnostics.Contracts; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Threading; +using CoreWCF.Runtime.Diagnostics; +using System.Threading.Tasks; +using System.Runtime.Serialization; + +namespace CoreWCF.Runtime +{ + internal static class Fx + { + const string defaultEventSource = "Microsoft.Runtime"; + static ExceptionTrace s_exceptionTrace; + static EtwDiagnosticTrace s_diagnosticTrace; + static ExceptionHandler s_asynchronousThreadExceptionHandler; + + public static ExceptionTrace Exception + { + get + { + if (s_exceptionTrace == null) + { + // don't need a lock here since a true singleton is not required + s_exceptionTrace = new ExceptionTrace(defaultEventSource, Trace); + } + + return s_exceptionTrace; + } + } + + public static EtwDiagnosticTrace Trace + { + get + { + if (s_diagnosticTrace == null) + { + s_diagnosticTrace = InitializeTracing(); + } + + return s_diagnosticTrace; + } + } + + static EtwDiagnosticTrace InitializeTracing() + { + EtwDiagnosticTrace trace = new EtwDiagnosticTrace(defaultEventSource, EtwDiagnosticTrace.DefaultEtwProviderId); + + //if (null != trace.EtwProvider) + //{ + // trace.RefreshState += delegate () + // { + // Fx.UpdateLevel(); + // }; + //} + //Fx.UpdateLevel(trace); + return trace; + } + + public static ExceptionHandler AsynchronousThreadExceptionHandler + { + get + { + return s_asynchronousThreadExceptionHandler; + } + + set + { + s_asynchronousThreadExceptionHandler = value; + } + } + + public static void AssertAndThrow(bool condition, string description) + { + if (!condition) + { + AssertAndThrow(description); + } + } + + public static Exception AssertAndThrow(string description) + { + Assert(description); + //TraceCore.ShipAssertExceptionMessage(Trace, description); + throw new InternalException(description); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Exception AssertAndThrowFatal(string description) + { + Fx.Assert(description); + //TraceCore.ShipAssertExceptionMessage(Trace, description); + throw new FatalInternalException(description); + } + + [Conditional("DEBUG")] + public static void Assert(string description) + { + if (Debugger.IsAttached) + { + Debugger.Break(); + } + Contract.Assert(false, description); + } + + [Conditional("DEBUG")] + public static void Assert(bool condition, string description) + { + if (!condition) + { + Assert(description); + } + } + + public static bool IsFatal(Exception exception) + { + while (exception != null) + { + if (exception is FatalException || + (exception is OutOfMemoryException /*&& !(exception is InsufficientMemoryException)*/) || + //exception is ThreadAbortException || + exception is FatalInternalException) + { + return true; + } + + // These exceptions aren't themselves fatal, but since the CLR uses them to wrap other exceptions, + // we want to check to see whether they've been used to wrap a fatal exception. If so, then they + // count as fatal. + if (exception is TypeInitializationException || + exception is TargetInvocationException) + { + exception = exception.InnerException; + } + else if (exception is AggregateException) + { + // AggregateExceptions have a collection of inner exceptions, which may themselves be other + // wrapping exceptions (including nested AggregateExceptions). Recursively walk this + // hierarchy. The (singular) InnerException is included in the collection. + ReadOnlyCollection innerExceptions = ((AggregateException)exception).InnerExceptions; + foreach (Exception innerException in innerExceptions) + { + if (IsFatal(innerException)) + { + return true; + } + } + + break; + } + else + { + break; + } + } + + return false; + } + + [Serializable] + internal class InternalException : SystemException + { + public InternalException(string description) + : base($"ShipAssertExceptionMessage,{description}" /*InternalSR.ShipAssertExceptionMessage(description)*/) + { + } + + protected InternalException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } + + [Serializable] + internal class FatalInternalException : InternalException + { + public FatalInternalException(string description) + : base(description) + { + } + + protected FatalInternalException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } + + internal static byte[] AllocateByteArray(int size) + { + try + { + // Safe to catch OOM from this as long as the ONLY thing it does is a simple allocation of a primitive type (no method calls). + return new byte[size]; + } + catch (OutOfMemoryException exception) + { + // Convert OOM into an exception that can be safely handled by higher layers. + throw Fx.Exception.AsError(exception); + //new InsufficientMemoryException(InternalSR.BufferAllocationFailed(size), exception)); + } + } + + public static AsyncCallback ThunkCallback(AsyncCallback callback) + { + return new AsyncThunk(callback).ThunkFrame; + } + + public static Action ThunkCallback(Action callback) + { + return new ActionThunk(callback).ThunkFrame; + } + + public static Action ThunkCallback(Action callback) + { + return new ActionThunk(callback).ThunkFrame; + } + + public static Action ThunkCallback(Action callback) + { + return new ActionThunk(callback).ThunkFrame; + } + + public static IOCompletionCallback ThunkCallback(IOCompletionCallback callback) + { + Fx.Assert(callback != null, "Trying to create a ThunkCallback with a null callback method"); + return (new IOCompletionThunk(callback)).ThunkFrame; + } + + abstract class Thunk where T : class + { + T callback; + + protected Thunk(T callback) + { + this.callback = callback; + } + + internal T Callback + { + get + { + return callback; + } + } + } + + sealed class ActionThunk : Thunk> + { + public ActionThunk(Action callback) : base(callback) + { + } + + public Action ThunkFrame + { + get + { + return UnhandledExceptionFrame; + } + } + + void UnhandledExceptionFrame(T1 param1) + { + RuntimeHelpers.PrepareConstrainedRegions(); + try + { + Callback(param1); + } + catch (Exception exception) + { + if (!Fx.HandleAtThreadBase(exception)) + { + throw; + } + } + } + } + + sealed class ActionThunk : Thunk> + { + public ActionThunk(Action callback) : base(callback) + { + } + + public Action ThunkFrame + { + get + { + return UnhandledExceptionFrame; + } + } + + void UnhandledExceptionFrame(T1 param1, T2 param2) + { + RuntimeHelpers.PrepareConstrainedRegions(); + try + { + Callback(param1, param2); + } + catch (Exception exception) + { + if (!Fx.HandleAtThreadBase(exception)) + { + throw; + } + } + } + } + + sealed class ActionThunk : Thunk> + { + public ActionThunk(Action callback) : base(callback) + { + } + + public Action ThunkFrame + { + get + { + return UnhandledExceptionFrame; + } + } + + void UnhandledExceptionFrame(T1 param1, T2 param2, T3 param3) + { + RuntimeHelpers.PrepareConstrainedRegions(); + try + { + Callback(param1, param2, param3); + } + catch (Exception exception) + { + if (!Fx.HandleAtThreadBase(exception)) + { + throw; + } + } + } + } + + sealed class AsyncThunk : Thunk + { + public AsyncThunk(AsyncCallback callback) : base(callback) + { + } + + public AsyncCallback ThunkFrame + { + get + { + return new AsyncCallback(UnhandledExceptionFrame); + } + } + + void UnhandledExceptionFrame(IAsyncResult result) + { + // PrepareConstrainedRegions are in .net standard 1.7+ + //RuntimeHelpers.PrepareConstrainedRegions(); + try + { + Callback(result); + } + catch (Exception exception) + { + if (!Fx.HandleAtThreadBase(exception)) + { + throw; + } + } + } + } + + static void TraceExceptionNoThrow(Exception exception) + { + try + { + // This call exits the CER. However, when still inside a catch, normal ThreadAbort is prevented. + // Rude ThreadAbort will still be allowed to terminate processing. + Fx.Exception.TraceUnhandledException(exception); + } + catch + { + // This empty catch is only acceptable because we are a) in a CER and b) processing an exception + // which is about to crash the process anyway. + } + } + + static bool HandleAtThreadBase(Exception exception) + { + // This area is too sensitive to do anything but return. + if (exception == null) + { + Fx.Assert("Null exception in HandleAtThreadBase."); + return false; + } + + TraceExceptionNoThrow(exception); + + try + { + ExceptionHandler handler = Fx.AsynchronousThreadExceptionHandler; + return handler == null ? false : handler.HandleException(exception); + } + catch (Exception secondException) + { + // Don't let a new exception hide the original exception. + TraceExceptionNoThrow(secondException); + } + + return false; + } + + public abstract class ExceptionHandler + { + public abstract bool HandleException(Exception exception); + } + + // This can't derive from Thunk since T would be unsafe. + unsafe sealed class IOCompletionThunk + { + IOCompletionCallback callback; + + public IOCompletionThunk(IOCompletionCallback callback) + { + this.callback = callback; + } + + public IOCompletionCallback ThunkFrame + { + get + { + return new IOCompletionCallback(UnhandledExceptionFrame); + } + } + + void UnhandledExceptionFrame(uint error, uint bytesRead, NativeOverlapped* nativeOverlapped) + { + RuntimeHelpers.PrepareConstrainedRegions(); + try + { + callback(error, bytesRead, nativeOverlapped); + } + catch (Exception exception) + { + if (!Fx.HandleAtThreadBase(exception)) + { + throw; + } + } + } + } + } +} \ No newline at end of file diff --git a/src/Common/src/CoreWCF/Runtime/IOThreadScheduler.cs b/src/Common/src/CoreWCF/Runtime/IOThreadScheduler.cs new file mode 100644 index 000000000..3c0e39f32 --- /dev/null +++ b/src/Common/src/CoreWCF/Runtime/IOThreadScheduler.cs @@ -0,0 +1,624 @@ +using System; +using System.Diagnostics.Contracts; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; + +namespace CoreWCF.Runtime +{ + class IOThreadScheduler + { + // Do not increase the maximum capacity above 32k! It must be a power of two, 0x8000 or less, in order to + // work with the strategy for 'headTail'. + const int MaximumCapacity = 0x8000; + + static class Bits + { + public const int HiShift = 32 / 2; + + public const int HiOne = 1 << HiShift; + public const int LoHiBit = HiOne >> 1; + public const int HiHiBit = LoHiBit << HiShift; + public const int LoCountMask = LoHiBit - 1; + public const int HiCountMask = LoCountMask << HiShift; + public const int LoMask = LoCountMask | LoHiBit; + public const int HiMask = HiCountMask | HiHiBit; + public const int HiBits = LoHiBit | HiHiBit; + + public static int Count(int slot) + { + return ((slot >> HiShift) - slot + 2 & LoMask) - 1; + } + + public static int CountNoIdle(int slot) + { + return (slot >> HiShift) - slot + 1 & LoMask; + } + + public static int IncrementLo(int slot) + { + return slot + 1 & LoMask | slot & HiMask; + } + + // This method is only valid if you already know that (gate & HiBits) != 0. + public static bool IsComplete(int gate) + { + return (gate & HiMask) == gate << HiShift; + } + } + + static IOThreadScheduler current = new IOThreadScheduler(32, 32); + readonly ScheduledOverlapped overlapped; + + readonly Slot[] slots; + + readonly Slot[] slotsLowPri; + + // This field holds both the head (HiWord) and tail (LoWord) indicies into the slot array. This limits each + // value to 64k. In order to be able to distinguish wrapping the slot array (allowed) from wrapping the + // indicies relative to each other (not allowed), the size of the slot array is limited by an additional bit + // to 32k. + // + // The HiWord (head) holds the index of the last slot to have been scheduled into. The LoWord (tail) holds + // the index of the next slot to be dispatched from. When the queue is empty, the LoWord will be exactly + // one slot ahead of the HiWord. When the two are equal, the queue holds one item. + // + // When the tail is *two* slots ahead of the head (equivalent to a count of -1), that means the IOTS is + // idle. Hence, we start out headTail with a -2 (equivalent) in the head and zero in the tail. + int headTail = -2 << Bits.HiShift; + + // This field is the same except that it governs the low-priority work items. It doesn't have a concept + // of idle (-2) so starts empty (-1). + int headTailLowPri = -1 << Bits.HiShift; + + IOThreadScheduler(int capacity, int capacityLowPri) + { + Fx.Assert(capacity > 0, "Capacity must be positive."); + Fx.Assert(capacity <= 0x8000, "Capacity cannot exceed 32k."); + + Fx.Assert(capacityLowPri > 0, "Low-priority capacity must be positive."); + Fx.Assert(capacityLowPri <= 0x8000, "Low-priority capacity cannot exceed 32k."); + + slots = new Slot[capacity]; + Fx.Assert((slots.Length & SlotMask) == 0, "Capacity must be a power of two."); + + slotsLowPri = new Slot[capacityLowPri]; + Fx.Assert((slotsLowPri.Length & SlotMaskLowPri) == 0, "Low-priority capacity must be a power of two."); + + overlapped = new ScheduledOverlapped(); + } + + public static void ScheduleCallbackNoFlow(Action callback, object state) + { + if (callback == null) + { + throw Fx.Exception.ArgumentNull("callback"); + } + + bool queued = false; + while (!queued) + { + try { } + finally + { + // Called in a finally because it needs to run uninterrupted in order to maintain consistency. + queued = IOThreadScheduler.current.ScheduleCallbackHelper(callback, state); + } + } + } + + public static void ScheduleCallbackLowPriNoFlow(Action callback, object state) + { + if (callback == null) + { + throw Fx.Exception.ArgumentNull("callback"); + } + + bool queued = false; + while (!queued) + { + try { } + finally + { + // Called in a finally because it needs to run uninterrupted in order to maintain consistency. + queued = IOThreadScheduler.current.ScheduleCallbackLowPriHelper(callback, state); + } + } + } + + // Returns true if successfully scheduled, false otherwise. + bool ScheduleCallbackHelper(Action callback, object state) + { + // See if there's a free slot. Fortunately the overflow bit is simply lost. + int slot = Interlocked.Add(ref headTail, Bits.HiOne); + + // If this brings us to 'empty', then the IOTS used to be 'idle'. Remember that, and increment + // again. This doesn't need to be in a loop, because until we call Post(), we can't go back to idle. + bool wasIdle = Bits.Count(slot) == 0; + if (wasIdle) + { + slot = Interlocked.Add(ref headTail, Bits.HiOne); + Fx.Assert(Bits.Count(slot) != 0, "IOTS went idle when it shouldn't have."); + } + + // Check if we wrapped *around* to idle. + if (Bits.Count(slot) == -1) + { + // Since the capacity is limited to 32k, this means we wrapped the array at least twice. That's bad + // because headTail no longer knows how many work items we have - it looks like zero. This can + // only happen if 32k threads come through here while one is swapped out. + throw Fx.AssertAndThrowFatal("Head/Tail overflow!"); + } + + bool wrapped; + bool queued = slots[slot >> Bits.HiShift & SlotMask].TryEnqueueWorkItem(callback, state, out wrapped); + + if (wrapped) + { + // Wrapped around the circular buffer. Create a new, bigger IOThreadScheduler. + IOThreadScheduler next = + new IOThreadScheduler(Math.Min(slots.Length * 2, MaximumCapacity), slotsLowPri.Length); + Interlocked.CompareExchange(ref IOThreadScheduler.current, next, this); + } + + if (wasIdle) + { + // It's our responsibility to kick off the overlapped. + overlapped.Post(this); + } + + return queued; + } + + // Returns true if successfully scheduled, false otherwise. + bool ScheduleCallbackLowPriHelper(Action callback, object state) + { + // See if there's a free slot. Fortunately the overflow bit is simply lost. + int slot = Interlocked.Add(ref headTailLowPri, Bits.HiOne); + + // If this is the first low-priority work item, make sure we're not idle. + bool wasIdle = false; + if (Bits.CountNoIdle(slot) == 1) + { + // Since Interlocked calls create a full thread barrier, this will read the value of headTail + // at the time of the Interlocked.Add or later. The invariant is that the IOTS is unidle at some + // point after the Add. + int ht = headTail; + + if (Bits.Count(ht) == -1) + { + // Use a temporary local here to store the result of the Interlocked.CompareExchange. This + // works around a codegen bug in the 32-bit JIT (TFS 749182). + int interlockedResult = Interlocked.CompareExchange(ref headTail, ht + Bits.HiOne, ht); + if (ht == interlockedResult) + { + wasIdle = true; + } + } + } + + // Check if we wrapped *around* to empty. + if (Bits.CountNoIdle(slot) == 0) + { + // Since the capacity is limited to 32k, this means we wrapped the array at least twice. That's bad + // because headTail no longer knows how many work items we have - it looks like zero. This can + // only happen if 32k threads come through here while one is swapped out. + throw Fx.AssertAndThrowFatal("Low-priority Head/Tail overflow!"); + } + + bool wrapped; + bool queued = slotsLowPri[slot >> Bits.HiShift & SlotMaskLowPri].TryEnqueueWorkItem( + callback, state, out wrapped); + + if (wrapped) + { + IOThreadScheduler next = + new IOThreadScheduler(slots.Length, Math.Min(slotsLowPri.Length * 2, MaximumCapacity)); + Interlocked.CompareExchange(ref IOThreadScheduler.current, next, this); + } + + if (wasIdle) + { + // It's our responsibility to kick off the overlapped. + overlapped.Post(this); + } + + return queued; + } + + void CompletionCallback(out Action callback, out object state) + { + int slot = headTail; + int slotLowPri; + while (true) + { + Fx.Assert(Bits.Count(slot) != -1, "CompletionCallback called on idle IOTS!"); + + bool wasEmpty = Bits.Count(slot) == 0; + if (wasEmpty) + { + // We're about to set this to idle. First check the low-priority queue. This alone doesn't + // guarantee we service all the low-pri items - there hasn't even been an Interlocked yet. But + // we take care of that later. + slotLowPri = headTailLowPri; + while (Bits.CountNoIdle(slotLowPri) != 0) + { + if (slotLowPri == (slotLowPri = Interlocked.CompareExchange(ref headTailLowPri, + Bits.IncrementLo(slotLowPri), slotLowPri))) + { + overlapped.Post(this); + slotsLowPri[slotLowPri & SlotMaskLowPri].DequeueWorkItem(out callback, out state); + return; + } + } + } + + if (slot == (slot = Interlocked.CompareExchange(ref headTail, Bits.IncrementLo(slot), slot))) + { + if (!wasEmpty) + { + overlapped.Post(this); + slots[slot & SlotMask].DequeueWorkItem(out callback, out state); + return; + } + + // We just set the IOThreadScheduler to idle. Check if a low-priority item got added in the + // interim. + // Interlocked calls create a thread barrier, so this read will give us the value of + // headTailLowPri at the time of the interlocked that set us to idle, or later. The invariant + // here is that either the low-priority queue was empty at some point after we set the IOTS to + // idle (so that the next enqueue will notice, and issue a Post), or that the IOTS was unidle at + // some point after we set it to idle (so that the next attempt to go idle will verify that the + // low-priority queue is empty). + slotLowPri = headTailLowPri; + + if (Bits.CountNoIdle(slotLowPri) != 0) + { + // Whoops, go back from being idle (unless someone else already did). If we go back, start + // over. (We still owe a Post.) + slot = Bits.IncrementLo(slot); + if (slot == Interlocked.CompareExchange(ref headTail, slot + Bits.HiOne, slot)) + { + slot += Bits.HiOne; + continue; + } + + // We know that there's a low-priority work item. But we also know that the IOThreadScheduler + // wasn't idle. It's best to let it take care of itself, since according to this method, we + // just set the IOThreadScheduler to idle so shouldn't take on any tasks. + } + + break; + } + } + + callback = null; + state = null; + return; + } + + bool TryCoalesce(out Action callback, out object state) + { + int slot = headTail; + int slotLowPri; + while (true) + { + if (Bits.Count(slot) > 0) + { + if (slot == (slot = Interlocked.CompareExchange(ref headTail, Bits.IncrementLo(slot), slot))) + { + slots[slot & SlotMask].DequeueWorkItem(out callback, out state); + return true; + } + continue; + } + + slotLowPri = headTailLowPri; + if (Bits.CountNoIdle(slotLowPri) > 0) + { + if (slotLowPri == (slotLowPri = Interlocked.CompareExchange(ref headTailLowPri, + Bits.IncrementLo(slotLowPri), slotLowPri))) + { + slotsLowPri[slotLowPri & SlotMaskLowPri].DequeueWorkItem(out callback, out state); + return true; + } + slot = headTail; + continue; + } + + break; + } + + callback = null; + state = null; + return false; + } + + int SlotMask + { + get + { + return slots.Length - 1; + } + } + + int SlotMaskLowPri + { + get + { + return slotsLowPri.Length - 1; + } + } + + //TODO, Dev10,607596 cannot apply security critical on finalizer + //[Fx.Tag.SecurityNote(Critical = "touches slots, may be called outside of user context")] + //[SecurityCritical] + ~IOThreadScheduler() + { + // If the AppDomain is shutting down, we may still have pending ops. The AppDomain shutdown will clean + // everything up. + if (!Environment.HasShutdownStarted && !AppDomain.CurrentDomain.IsFinalizingForUnload()) + { +#if DEBUG + DebugVerifyHeadTail(); +#endif + Cleanup(); + } + } + + void Cleanup() + { + if (overlapped != null) + { + overlapped.Cleanup(); + } + } + +#if DEBUG + private void DebugVerifyHeadTail() + { + if (slots != null) + { + // The headTail value could technically be zero if the constructor was aborted early. The + // constructor wasn't aborted early if the slot array got created. + Fx.Assert(Bits.Count(headTail) == -1, "IOTS finalized while not idle."); + + for (int i = 0; i < slots.Length; i++) + { + slots[i].DebugVerifyEmpty(); + } + } + + if (slotsLowPri != null) + { + Fx.Assert(Bits.CountNoIdle(headTailLowPri) == 0, "IOTS finalized with low-priority items queued."); + + for (int i = 0; i < slotsLowPri.Length; i++) + { + slotsLowPri[i].DebugVerifyEmpty(); + } + } + } +#endif + + // TryEnqueueWorkItem and DequeueWorkItem use the slot's 'gate' field for synchronization. Because the + // slot array is circular and there are no locks, we must assume that multiple threads can be entering each + // method simultaneously. If the first DequeueWorkItem occurs before the first TryEnqueueWorkItem, the + // sequencing (and the enqueue) fails. + // + // The gate is a 32-bit int divided into four fields. The bottom 15 bits (0x00007fff) are the count of + // threads that have entered TryEnqueueWorkItem. The first thread to enter is the one responsible for + // filling the slot with work. The 16th bit (0x00008000) is a flag indicating that the slot has been + // successfully filled. Only the first thread to enter TryEnqueueWorkItem can set this flag. The + // high-word (0x7fff0000) is the count of threads entering DequeueWorkItem. The first thread to enter + // is the one responsible for accepting (and eventually dispatching) the work in the slot. The + // high-bit (0x80000000) is a flag indicating that the slot has been successfully emptied. + // + // When the low-word and high-work counters are equal, and both bit flags have been set, the gate is considered + // 'complete' and can be reset back to zero. Any operation on the gate might bring it to this state. + // It's the responsibility of the thread that brings the gate to a completed state to reset it to zero. + // (It's possible that the gate will fall out of the completed state before it can be reset - that's ok, + // the next time it becomes completed it can be reset.) + // + // It's unlikely either count will ever go higher than 2 or 3. + // + // The value of 'callback' has these properties: + // - When the gate is zero, callback is null. + // - When the low-word count is non-zero, but the 0x8000 bit is unset, callback is writable by the thread + // that incremented the low word to 1. Its value is undefined for other threads. The thread that + // sets callback is responsible for setting the 0x8000 bit when it's done. + // - When the 0x8000 bit is set and the high-word count is zero, callback is valid. (It may be null.) + // - When the 0x8000 bit is set, the high-word count is non-zero, and the high bit is unset, callback is + // writable by the thread that incremented the high word to 1 *or* the thread that set the 0x8000 bit, + // whichever happened last. That thread can read the value and set callback to null. Its value is + // undefined for other threads. The thread that clears the callback is responsible for setting the + // high bit. + // - When the high bit is set, callback is null. + // - It's illegal for the gate to be in a state that would satisfy more than one of these conditions. + // - The state field follows the same rules as callback. + struct Slot + { + int gate; + Action callback; + object state; + + public bool TryEnqueueWorkItem(Action callback, object state, out bool wrapped) + { + // Register our arrival and check the state of this slot. If the slot was already full, we wrapped. + int gateSnapshot = Interlocked.Increment(ref gate); + wrapped = (gateSnapshot & Bits.LoCountMask) != 1; + if (wrapped) + { + if ((gateSnapshot & Bits.LoHiBit) != 0 && Bits.IsComplete(gateSnapshot)) + { + Interlocked.CompareExchange(ref gate, 0, gateSnapshot); + } + return false; + } + + Fx.Assert(this.callback == null, "Slot already has a work item."); + Fx.Assert((gateSnapshot & Bits.HiBits) == 0, "Slot already marked."); + + this.state = state; + this.callback = callback; + + // Set the special bit to show that the slot is filled. + gateSnapshot = Interlocked.Add(ref gate, Bits.LoHiBit); + Fx.Assert((gateSnapshot & Bits.HiBits) == Bits.LoHiBit, "Slot already empty."); + + if ((gateSnapshot & Bits.HiCountMask) == 0) + { + // Good - no one has shown up looking for this work yet. + return true; + } + + // Oops - someone already came looking for this work. We have to abort and reschedule. + this.state = null; + this.callback = null; + + // Indicate that the slot is clear. We might be able to bypass setting the high bit. + if (gateSnapshot >> Bits.HiShift != (gateSnapshot & Bits.LoCountMask) || + Interlocked.CompareExchange(ref gate, 0, gateSnapshot) != gateSnapshot) + { + gateSnapshot = Interlocked.Add(ref gate, Bits.HiHiBit); + if (Bits.IsComplete(gateSnapshot)) + { + Interlocked.CompareExchange(ref gate, 0, gateSnapshot); + } + } + + return false; + } + + public void DequeueWorkItem(out Action callback, out object state) + { + // Stake our claim on the item. + int gateSnapshot = Interlocked.Add(ref gate, Bits.HiOne); + + if ((gateSnapshot & Bits.LoHiBit) == 0) + { + // Whoops, a race. The work item hasn't made it in yet. In this context, returning a null callback + // is treated like a degenrate work item (rather than an empty queue). The enqueuing thread will + // notice this race and reschedule the real work in a new slot. Do not reset the slot to zero, + // since it's still going to get enqueued into. (The enqueueing thread will reset it.) + callback = null; + state = null; + return; + } + + // If we're the first, we get to do the work. + if ((gateSnapshot & Bits.HiCountMask) == Bits.HiOne) + { + callback = this.callback; + state = this.state; + this.state = null; + this.callback = null; + + // Indicate that the slot is clear. + // We should be able to bypass setting the high-bit in the common case. + if ((gateSnapshot & Bits.LoCountMask) != 1 || + Interlocked.CompareExchange(ref gate, 0, gateSnapshot) != gateSnapshot) + { + gateSnapshot = Interlocked.Add(ref gate, Bits.HiHiBit); + if (Bits.IsComplete(gateSnapshot)) + { + Interlocked.CompareExchange(ref gate, 0, gateSnapshot); + } + } + } + else + { + callback = null; + state = null; + + // If we're the last, we get to reset the slot. + if (Bits.IsComplete(gateSnapshot)) + { + Interlocked.CompareExchange(ref gate, 0, gateSnapshot); + } + } + } + +#if DEBUG + public void DebugVerifyEmpty() + { + Fx.Assert(gate == 0, "Finalized with unfinished slot."); + Fx.Assert(callback == null, "Finalized with leaked callback."); + Fx.Assert(state == null, "Finalized with leaked state."); + } +#endif + } + + // A note about the IOThreadScheduler and the ScheduledOverlapped references: + // Although for each scheduler we have a single instance of overlapped, we cannot point to the scheduler from the + // overlapped, through the entire lifetime of the overlapped. This is because the ScheduledOverlapped is pinned + // and if it has a reference to the IOTS, it would be rooted and the finalizer will never get called. + // Therefore, we are passing the reference, when we post a pending callback and reset it, once the callback was + // invoked; during that time the scheduler is rooted but in that time we don't want that it would be collected + // by the GC anyway. + unsafe class ScheduledOverlapped + { + readonly NativeOverlapped* nativeOverlapped; + IOThreadScheduler scheduler; + + public ScheduledOverlapped() + { + nativeOverlapped = (new Overlapped()).UnsafePack( + Fx.ThunkCallback(new IOCompletionCallback(IOCallback)), null); + } + + void IOCallback(uint errorCode, uint numBytes, NativeOverlapped* nativeOverlapped) + { + // Unhook the IOThreadScheduler ASAP to prevent it from leaking. + IOThreadScheduler iots = scheduler; + scheduler = null; + Fx.Assert(iots != null, "Overlapped completed without a scheduler."); + + Action callback; + object state; + try { } + finally + { + // Called in a finally because it needs to run uninterrupted in order to maintain consistency. + iots.CompletionCallback(out callback, out state); + } + + bool found = true; + while (found) + { + // The callback can be null if synchronization misses result in unsuable slots. Keep going onto + // the next slot in such cases until there are no more slots. + if (callback != null) + { + callback(state); + } + + try { } + finally + { + // Called in a finally because it needs to run uninterrupted in order to maintain consistency. + found = iots.TryCoalesce(out callback, out state); + } + } + } + + public void Post(IOThreadScheduler iots) + { + Fx.Assert(scheduler == null, "Post called on an overlapped that is already posted."); + Fx.Assert(iots != null, "Post called with a null scheduler."); + + scheduler = iots; + ThreadPool.UnsafeQueueNativeOverlapped(nativeOverlapped); + } + + public void Cleanup() + { + if (scheduler != null) + { + throw Fx.AssertAndThrowFatal("Cleanup called on an overlapped that is in-flight."); + } + Overlapped.Free(nativeOverlapped); + } + } + } +} \ No newline at end of file diff --git a/src/Common/src/CoreWCF/Runtime/IOThreadTimer.cs b/src/Common/src/CoreWCF/Runtime/IOThreadTimer.cs new file mode 100644 index 000000000..d7e494a02 --- /dev/null +++ b/src/Common/src/CoreWCF/Runtime/IOThreadTimer.cs @@ -0,0 +1,641 @@ +using System; +using System.Threading; +using System.Diagnostics.Contracts; + +namespace CoreWCF.Runtime +{ + // IOThreadTimer has several characterstics that are important for performance: + // - Timers that expire benefit from being scheduled to run on IO threads using IOThreadScheduler.Schedule. + // - The timer "waiter" thread thread is only allocated if there are set timers. + // - The timer waiter thread itself is an IO thread, which allows it to go away if there is no need for it, + // and allows it to be reused for other purposes. + // - After the timer count goes to zero, the timer waiter thread remains active for a bounded amount + // of time to wait for additional timers to be set. + // - Timers are stored in an array-based priority queue to reduce the amount of time spent in updates, and + // to always provide O(1) access to the minimum timer (the first one that will expire). + // - The standard textbook priority queue data structure is extended to allow efficient Delete in addition to + // DeleteMin for efficient handling of canceled timers. + // - Timers that are typically set, then immediately canceled (such as a retry timer, + // or a flush timer), are tracked separately from more stable timers, to avoid having + // to update the waitable timer in the typical case when a timer is canceled. Whether + // a timer instance follows this pattern is specified when the timer is constructed. + // - Extending a timer by a configurable time delta (maxSkew) does not involve updating the + // waitable timer, or taking a lock. + // - Timer instances are relatively cheap. They share "heavy" resources like the waiter thread and + // waitable timer handle. + // - Setting or canceling a timer does not typically involve any allocations. + + internal class IOThreadTimer + { + const int maxSkewInMillisecondsDefault = 100; + Action callback; + object callbackState; + long dueTime; + + int index; + long maxSkew; + TimerGroup timerGroup; + + public IOThreadTimer(Action callback, object callbackState, bool isTypicallyCanceledShortlyAfterBeingSet) + : this(callback, callbackState, isTypicallyCanceledShortlyAfterBeingSet, maxSkewInMillisecondsDefault) + { + } + + public IOThreadTimer(Action callback, object callbackState, bool isTypicallyCanceledShortlyAfterBeingSet, int maxSkewInMilliseconds) + { + this.callback = callback; + this.callbackState = callbackState; + maxSkew = Ticks.FromMilliseconds(maxSkewInMilliseconds); + timerGroup = + (isTypicallyCanceledShortlyAfterBeingSet ? TimerManager.Value.VolatileTimerGroup : TimerManager.Value.StableTimerGroup); + } + + public bool Cancel() + { + return TimerManager.Value.Cancel(this); + } + + public void Set(TimeSpan timeFromNow) + { + if (timeFromNow != TimeSpan.MaxValue) + { + SetAt(Ticks.Add(Ticks.Now, Ticks.FromTimeSpan(timeFromNow))); + } + } + + public void Set(int millisecondsFromNow) + { + SetAt(Ticks.Add(Ticks.Now, Ticks.FromMilliseconds(millisecondsFromNow))); + } + + public void SetAt(long dueTime) + { + TimerManager.Value.Set(this, dueTime); + } + + protected void Reinitialize(Action callback, object callbackState) + { + this.callback = callback; + this.callbackState = callbackState; + } + + internal static void KillTimers() + { + TimerManager.Value.Kill(); + } + + class TimerManager + { + const long maxTimeToWaitForMoreTimers = 1000 * TimeSpan.TicksPerMillisecond; + + static TimerManager value = new TimerManager(); + + Action onWaitCallback; + TimerGroup stableTimerGroup; + TimerGroup volatileTimerGroup; + WaitableTimer[] waitableTimers; + + bool waitScheduled; + + public TimerManager() + { + onWaitCallback = new Action(OnWaitCallback); + stableTimerGroup = new TimerGroup(); + volatileTimerGroup = new TimerGroup(); + waitableTimers = new WaitableTimer[] { stableTimerGroup.WaitableTimer, volatileTimerGroup.WaitableTimer }; + } + + object ThisLock + { + get { return this; } + } + + public static TimerManager Value + { + get + { + return TimerManager.value; + } + } + + public TimerGroup StableTimerGroup + { + get + { + return stableTimerGroup; + } + } + public TimerGroup VolatileTimerGroup + { + get + { + return volatileTimerGroup; + } + } + + internal void Kill() + { + stableTimerGroup.WaitableTimer.Kill(); + volatileTimerGroup.WaitableTimer.Kill(); + } + + public void Set(IOThreadTimer timer, long dueTime) + { + long timeDiff = dueTime - timer.dueTime; + if (timeDiff < 0) + { + timeDiff = -timeDiff; + } + + if (timeDiff > timer.maxSkew) + { + lock (ThisLock) + { + TimerGroup timerGroup = timer.timerGroup; + TimerQueue timerQueue = timerGroup.TimerQueue; + + if (timer.index > 0) + { + if (timerQueue.UpdateTimer(timer, dueTime)) + { + UpdateWaitableTimer(timerGroup); + } + } + else + { + if (timerQueue.InsertTimer(timer, dueTime)) + { + UpdateWaitableTimer(timerGroup); + + if (timerQueue.Count == 1) + { + EnsureWaitScheduled(); + } + } + } + } + } + } + + public bool Cancel(IOThreadTimer timer) + { + lock (ThisLock) + { + if (timer.index > 0) + { + TimerGroup timerGroup = timer.timerGroup; + TimerQueue timerQueue = timerGroup.TimerQueue; + + timerQueue.DeleteTimer(timer); + + if (timerQueue.Count > 0) + { + UpdateWaitableTimer(timerGroup); + } + else + { + TimerGroup otherTimerGroup = GetOtherTimerGroup(timerGroup); + if (otherTimerGroup.TimerQueue.Count == 0) + { + long now = Ticks.Now; + long thisGroupRemainingTime = timerGroup.WaitableTimer.DueTime - now; + long otherGroupRemainingTime = otherTimerGroup.WaitableTimer.DueTime - now; + if (thisGroupRemainingTime > maxTimeToWaitForMoreTimers && + otherGroupRemainingTime > maxTimeToWaitForMoreTimers) + { + timerGroup.WaitableTimer.Set(Ticks.Add(now, maxTimeToWaitForMoreTimers)); + } + } + } + + return true; + } + else + { + return false; + } + } + } + + void EnsureWaitScheduled() + { + if (!waitScheduled) + { + ScheduleWait(); + } + } + + TimerGroup GetOtherTimerGroup(TimerGroup timerGroup) + { + if (object.ReferenceEquals(timerGroup, volatileTimerGroup)) + { + return stableTimerGroup; + } + else + { + return volatileTimerGroup; + } + } + + void OnWaitCallback(object state) + { + WaitableTimer.WaitAny(waitableTimers); + long now = Ticks.Now; + lock (ThisLock) + { + waitScheduled = false; + ScheduleElapsedTimers(now); + ReactivateWaitableTimers(); + ScheduleWaitIfAnyTimersLeft(); + } + } + + void ReactivateWaitableTimers() + { + ReactivateWaitableTimer(stableTimerGroup); + ReactivateWaitableTimer(volatileTimerGroup); + } + + void ReactivateWaitableTimer(TimerGroup timerGroup) + { + TimerQueue timerQueue = timerGroup.TimerQueue; + + if (timerGroup.WaitableTimer.dead) + return; + + if (timerQueue.Count > 0) + { + timerGroup.WaitableTimer.Set(timerQueue.MinTimer.dueTime); + } + else + { + timerGroup.WaitableTimer.Set(long.MaxValue); + } + } + + void ScheduleElapsedTimers(long now) + { + ScheduleElapsedTimers(stableTimerGroup, now); + ScheduleElapsedTimers(volatileTimerGroup, now); + } + + void ScheduleElapsedTimers(TimerGroup timerGroup, long now) + { + TimerQueue timerQueue = timerGroup.TimerQueue; + while (timerQueue.Count > 0) + { + IOThreadTimer timer = timerQueue.MinTimer; + long timeDiff = timer.dueTime - now; + if (timeDiff <= timer.maxSkew) + { + timerQueue.DeleteMinTimer(); + ActionItem.Schedule(timer.callback, timer.callbackState); + } + else + { + break; + } + } + } + + void ScheduleWait() + { + ActionItem.Schedule(onWaitCallback, null); + waitScheduled = true; + } + + void ScheduleWaitIfAnyTimersLeft() + { + if (this.stableTimerGroup.WaitableTimer.dead && + this.volatileTimerGroup.WaitableTimer.dead) + return; + + if (this.stableTimerGroup.TimerQueue.Count > 0 || + this.volatileTimerGroup.TimerQueue.Count > 0) + { + ScheduleWait(); + } + } + + void UpdateWaitableTimer(TimerGroup timerGroup) + { + WaitableTimer waitableTimer = timerGroup.WaitableTimer; + IOThreadTimer minTimer = timerGroup.TimerQueue.MinTimer; + long timeDiff = waitableTimer.DueTime - minTimer.dueTime; + if (timeDiff < 0) + { + timeDiff = -timeDiff; + } + if (timeDiff > minTimer.maxSkew) + { + waitableTimer.Set(minTimer.dueTime); + } + } + } + + class TimerGroup + { + TimerQueue timerQueue; + WaitableTimer waitableTimer; + + public TimerGroup() + { + waitableTimer = new WaitableTimer(); + timerQueue = new TimerQueue(); + } + + public TimerQueue TimerQueue + { + get + { + return timerQueue; + } + } + public WaitableTimer WaitableTimer + { + get + { + return waitableTimer; + } + } + } + + class TimerQueue + { + int count; + IOThreadTimer[] timers; + + public TimerQueue() + { + timers = new IOThreadTimer[4]; + } + + public int Count + { + get { return count; } + } + + public IOThreadTimer MinTimer + { + get + { + Fx.Assert(count > 0, "Should have at least one timer in our queue."); + return timers[1]; + } + } + public void DeleteMinTimer() + { + IOThreadTimer minTimer = MinTimer; + DeleteMinTimerCore(); + minTimer.index = 0; + minTimer.dueTime = 0; + } + public void DeleteTimer(IOThreadTimer timer) + { + int index = timer.index; + + Fx.Assert(index > 0, ""); + Fx.Assert(index <= count, ""); + + IOThreadTimer[] timers = this.timers; + + for (; ; ) + { + int parentIndex = index / 2; + + if (parentIndex >= 1) + { + IOThreadTimer parentTimer = timers[parentIndex]; + timers[index] = parentTimer; + parentTimer.index = index; + } + else + { + break; + } + + index = parentIndex; + } + + timer.index = 0; + timer.dueTime = 0; + timers[1] = null; + DeleteMinTimerCore(); + } + + public bool InsertTimer(IOThreadTimer timer, long dueTime) + { + Fx.Assert(timer.index == 0, "Timer should not have an index."); + + IOThreadTimer[] timers = this.timers; + + int index = count + 1; + + if (index == timers.Length) + { + timers = new IOThreadTimer[timers.Length * 2]; + Array.Copy(this.timers, timers, this.timers.Length); + this.timers = timers; + } + + count = index; + + if (index > 1) + { + for (; ; ) + { + int parentIndex = index / 2; + + if (parentIndex == 0) + { + break; + } + + IOThreadTimer parent = timers[parentIndex]; + + if (parent.dueTime > dueTime) + { + timers[index] = parent; + parent.index = index; + index = parentIndex; + } + else + { + break; + } + } + } + + timers[index] = timer; + timer.index = index; + timer.dueTime = dueTime; + return index == 1; + } + public bool UpdateTimer(IOThreadTimer timer, long dueTime) + { + int index = timer.index; + + IOThreadTimer[] timers = this.timers; + int count = this.count; + + Fx.Assert(index > 0, ""); + Fx.Assert(index <= count, ""); + + int parentIndex = index / 2; + if (parentIndex == 0 || + timers[parentIndex].dueTime <= dueTime) + { + int leftChildIndex = index * 2; + if (leftChildIndex > count || + timers[leftChildIndex].dueTime >= dueTime) + { + int rightChildIndex = leftChildIndex + 1; + if (rightChildIndex > count || + timers[rightChildIndex].dueTime >= dueTime) + { + timer.dueTime = dueTime; + return index == 1; + } + } + } + + DeleteTimer(timer); + InsertTimer(timer, dueTime); + return true; + } + + void DeleteMinTimerCore() + { + int count = this.count; + + if (count == 1) + { + this.count = 0; + timers[1] = null; + } + else + { + IOThreadTimer[] timers = this.timers; + IOThreadTimer lastTimer = timers[count]; + this.count = --count; + + int index = 1; + for (; ; ) + { + int leftChildIndex = index * 2; + + if (leftChildIndex > count) + { + break; + } + + int childIndex; + IOThreadTimer child; + + if (leftChildIndex < count) + { + IOThreadTimer leftChild = timers[leftChildIndex]; + int rightChildIndex = leftChildIndex + 1; + IOThreadTimer rightChild = timers[rightChildIndex]; + + if (rightChild.dueTime < leftChild.dueTime) + { + child = rightChild; + childIndex = rightChildIndex; + } + else + { + child = leftChild; + childIndex = leftChildIndex; + } + } + else + { + childIndex = leftChildIndex; + child = timers[childIndex]; + } + + if (lastTimer.dueTime > child.dueTime) + { + timers[index] = child; + child.index = index; + } + else + { + break; + } + + index = childIndex; + + if (leftChildIndex >= count) + { + break; + } + } + + timers[index] = lastTimer; + lastTimer.index = index; + timers[count + 1] = null; + } + } + } + + public class WaitableTimer : EventWaitHandle + { + long dueTime; // Ticks + public bool dead; + + public WaitableTimer() : base(false, EventResetMode.AutoReset) + { + } + + public long DueTime + { + get { return dueTime; } + } + + public void Set(long dueTime) + { + if (dueTime < this.dueTime) + { + this.dueTime = dueTime; + Set(); // We might be waiting on a later time so nudge it to reworkout the time + } + else + { + this.dueTime = dueTime; + } + } + + public void Kill() + { + dead = true; + Set(); + } + + public static int WaitAny(WaitableTimer[] waitableTimers) + { + do + { + var earliestDueTime = waitableTimers[0].dueTime; + for (int i = 1; i < waitableTimers.Length; i++) + { + if (waitableTimers[i].dead) + return 0; + if (waitableTimers[i].dueTime < earliestDueTime) + earliestDueTime = waitableTimers[i].dueTime; + waitableTimers[i].Reset(); + } + + var waitDurationInMillis = (earliestDueTime - DateTime.UtcNow.Ticks) / TimeSpan.TicksPerMillisecond; + if (waitDurationInMillis < 0) // Already passed the due time + return 0; + + Contract.Assert(waitDurationInMillis < int.MaxValue, "Waiting for longer than is possible"); + WaitHandle.WaitAny(waitableTimers, (int)waitDurationInMillis); + // Always loop around and check wait time again as values might have changed. + } while (true); + } + } + } +} diff --git a/src/Common/src/CoreWCF/Runtime/RecoverableTimeoutCancellationTokenSource.cs b/src/Common/src/CoreWCF/Runtime/RecoverableTimeoutCancellationTokenSource.cs new file mode 100644 index 000000000..95050c5a7 --- /dev/null +++ b/src/Common/src/CoreWCF/Runtime/RecoverableTimeoutCancellationTokenSource.cs @@ -0,0 +1,131 @@ +using CoreWCF; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; +using System.Threading; + +namespace CoreWCF.Runtime +{ + internal class RecoverableTimeoutCancellationTokenSource : CancellationTokenSource + { + TimeSpan _originalTimeout; + + public RecoverableTimeoutCancellationTokenSource(TimeSpan timeout) : base() + { + if (timeout.TotalMilliseconds > int.MaxValue) + throw new ArgumentOutOfRangeException(nameof(timeout), $"Only TimeSpan's representing up to {int.MaxValue}ms are supported"); + + _originalTimeout = timeout; + } + + public RecoverableTimeoutCancellationTokenSource(int millisecondsDelay) + { + if (millisecondsDelay == Timeout.Infinite) + { + _originalTimeout = Timeout.InfiniteTimeSpan; + } + else + { + _originalTimeout = TimeSpan.FromMilliseconds(millisecondsDelay); + } + } + + public override int GetHashCode() + { + return (int)_originalTimeout.TotalMilliseconds; + } + + internal static TimeSpan GetOriginalTimeout(CancellationToken token) + { + // Covers CancellationToken.None as well as any other non-cancellable token + if (!token.CanBeCanceled) + return Timeout.InfiniteTimeSpan; + + return TimeSpan.FromMilliseconds(token.GetHashCode()); + } + } + + class CancellationTokenSourceIOThreadTimer : IOThreadTimer + { + List _cancellationTokenSources = new List(); + bool _timerFired = false; + Action _timerFiredCallback; + object _timerFiredState; + + public CancellationTokenSourceIOThreadTimer() : base(TimerCallback, null, false) + { + Reinitialize(TimerCallback, this); + } + + public void SetCompletionCallback(Action callback, object state) + { + _timerFiredCallback = Fx.ThunkCallback(callback); + _timerFiredState = state; + } + + public void RegisterTokenSourceForCancellation(CancellationTokenSource cts) + { + // TODO: Consider if unregistering would be helpful. It would require + // knowing that the CancellationToken is no longer needed. + lock (_cancellationTokenSources) + { + if (!_timerFired) + { + _cancellationTokenSources.Add(cts); + return; + } + } + + // Timer has already fired so cancelling now. + CancelTokenSource(cts); + } + + internal static void CancelTokenSource(object state) + { + var cts = (CancellationTokenSource)state; + try + { + // Ensure all callbacks are fired + cts.Cancel(throwOnFirstException: false); + cts.Dispose(); + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + // Callbacks shouldn't be throwing + DiagnosticUtility.TraceHandledException(e, TraceEventType.Error); + } + } + + internal void OnTimer() + { + _timerFiredCallback(_timerFiredState); + + lock (_cancellationTokenSources) + { + _timerFired = true; + } + // Once _timerFired is set, there's no need to hold the lock as + // no more will be added to the list. + foreach (var cts in _cancellationTokenSources) + { + // TODO: ActionItem.Schedule might be overkill here as I don't expect there + // to be many cancellations. There's just no + if (!cts.IsCancellationRequested) + { + ActionItem.Schedule(CancelTokenSource, cts); + } + } + } + + internal static void TimerCallback(object state) + { + var thisPtr = (CancellationTokenSourceIOThreadTimer)state; + thisPtr.OnTimer(); + } + } +} diff --git a/src/Common/src/CoreWCF/Runtime/ServiceModelSynchronizationContext.cs b/src/Common/src/CoreWCF/Runtime/ServiceModelSynchronizationContext.cs new file mode 100644 index 000000000..466ff3e25 --- /dev/null +++ b/src/Common/src/CoreWCF/Runtime/ServiceModelSynchronizationContext.cs @@ -0,0 +1,16 @@ +using System; +using System.Threading; + +namespace CoreWCF.Runtime +{ + internal class ServiceModelSynchronizationContext : SynchronizationContext + { + public static ServiceModelSynchronizationContext Instance = new ServiceModelSynchronizationContext(); + + public override void Post(SendOrPostCallback d, object state) + { + IOThreadScheduler.ScheduleCallbackNoFlow( + (s) => { d(s); }, state); + } + } +} \ No newline at end of file diff --git a/src/Common/src/CoreWCF/Runtime/TaskHelpers.cs b/src/Common/src/CoreWCF/Runtime/TaskHelpers.cs new file mode 100644 index 000000000..9a012f57c --- /dev/null +++ b/src/Common/src/CoreWCF/Runtime/TaskHelpers.cs @@ -0,0 +1,515 @@ +using CoreWCF; +using CoreWCF.Dispatcher; +using System; +using System.Diagnostics.Contracts; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; + +namespace CoreWCF.Runtime +{ + internal static class TaskHelpers + { + //This replaces the Wait(this Task task) method as we want to await and not Wait() + public static async Task AsyncWait(this Task task) + { + try + { + await task; + } + catch + { + throw Fx.Exception.AsError(task.Exception); + } + } + + // Helper method when implementing an APM wrapper around a Task based async method which returns a result. + // In the BeginMethod method, you would call use ToApm to wrap a call to MethodAsync: + // return MethodAsync(params).ToApm(callback, state); + // In the EndMethod, you would use ToApmEnd to ensure the correct exception handling + // This will handle throwing exceptions in the correct place and ensure the IAsyncResult contains the provided + // state object + public static Task ToApm(this Task task, AsyncCallback callback, object state) + { + // When using APM, the returned IAsyncResult must have the passed in state object stored in AsyncState. This + // is so the callback can regain state. If the incoming task already holds the state object, there's no need + // to create a TaskCompletionSource to ensure the returned (IAsyncResult)Task has the right state object. + // This is a performance optimization for this special case. + if (task.AsyncState == state) + { + if (callback != null) + { + task.ContinueWith((antecedent, obj) => + { + var callbackObj = obj as AsyncCallback; + callbackObj(antecedent); + }, callback, CancellationToken.None, TaskContinuationOptions.HideScheduler, TaskScheduler.Default); + } + return task; + } + + // Need to create a TaskCompletionSource so that the returned Task object has the correct AsyncState value. + var tcs = new TaskCompletionSource(state); + var continuationState = Tuple.Create(tcs, callback); + task.ContinueWith((antecedent, obj) => + { + var tuple = obj as Tuple, AsyncCallback>; + var tcsObj = tuple.Item1; + var callbackObj = tuple.Item2; + if (antecedent.IsFaulted) tcsObj.TrySetException(antecedent.Exception.InnerException); + else if (antecedent.IsCanceled) tcsObj.TrySetCanceled(); + else tcsObj.TrySetResult(antecedent.Result); + + if (callbackObj != null) + { + callbackObj(tcsObj.Task); + } + }, continuationState, CancellationToken.None, TaskContinuationOptions.HideScheduler, TaskScheduler.Default); + return tcs.Task; + } + + // Helper method when implementing an APM wrapper around a Task based async method which returns a result. + // In the BeginMethod method, you would call use ToApm to wrap a call to MethodAsync: + // return MethodAsync(params).ToApm(callback, state); + // In the EndMethod, you would use ToApmEnd to ensure the correct exception handling + // This will handle throwing exceptions in the correct place and ensure the IAsyncResult contains the provided + // state object + public static Task ToApm(this Task task, AsyncCallback callback, object state) + { + // When using APM, the returned IAsyncResult must have the passed in state object stored in AsyncState. This + // is so the callback can regain state. If the incoming task already holds the state object, there's no need + // to create a TaskCompletionSource to ensure the returned (IAsyncResult)Task has the right state object. + // This is a performance optimization for this special case. + if (task.AsyncState == state) + { + if (callback != null) + { + task.ContinueWith((antecedent, obj) => + { + var callbackObj = obj as AsyncCallback; + callbackObj(antecedent); + }, callback, CancellationToken.None, TaskContinuationOptions.HideScheduler, TaskScheduler.Default); + } + return task; + } + + // Need to create a TaskCompletionSource so that the returned Task object has the correct AsyncState value. + // As we intend to create a task with no Result value, we don't care what result type the TCS holds as we + // won't be using it. As Task derives from Task, the returned Task is compatible. + var tcs = new TaskCompletionSource(state); + var continuationState = Tuple.Create(tcs, callback); + task.ContinueWith((antecedent, obj) => + { + var tuple = obj as Tuple, AsyncCallback>; + var tcsObj = tuple.Item1; + var callbackObj = tuple.Item2; + if (antecedent.IsFaulted) + { + tcsObj.TrySetException(antecedent.Exception.InnerException); + } + else if (antecedent.IsCanceled) + { + tcsObj.TrySetCanceled(); + } + else + { + tcsObj.TrySetResult(null); + } + + if (callbackObj != null) + { + callbackObj(tcsObj.Task); + } + }, continuationState, CancellationToken.None, TaskContinuationOptions.HideScheduler, TaskScheduler.Default); + return tcs.Task; + } + + // Helper method to implement the End method of an APM method pair which is wrapping a Task based + // async method when the Task returns a result. By using task.GetAwaiter.GetResult(), the exception + // handling conventions are the same as when await'ing a task, i.e. this throws the first exception + // and doesn't wrap it in an AggregateException. It also throws the right exception if the task was + // cancelled. + public static TResult ToApmEnd(this IAsyncResult iar) + { + Task task = iar as Task; + Fx.Assert(task != null, "IAsyncResult must be an instance of Task"); + return task.GetAwaiter().GetResult(); + } + + // Helper method to implement the End method of an APM method pair which is wrapping a Task based + // async method when the Task does not return result. + public static void ToApmEnd(this IAsyncResult iar) + { + Task task = iar as Task; + Fx.Assert(task != null, "IAsyncResult must be an instance of Task"); + task.GetAwaiter().GetResult(); + } + + // Awaitable helper to await a maximum amount of time for a task to complete. If the task doesn't + // complete in the specified amount of time, returns false. This does not modify the state of the + // passed in class, but instead is a mechanism to allow interrupting awaiting a task if a timeout + // period passes. + public static async Task AwaitWithTimeout(this Task task, TimeSpan timeout) + { + if (task.IsCompleted) + { + return true; + } + + if (timeout == TimeSpan.MaxValue || timeout == Timeout.InfiniteTimeSpan) + { + await task; + return true; + } + + using (CancellationTokenSource cts = new CancellationTokenSource()) + { + var completedTask = await Task.WhenAny(task, Task.Delay(timeout, cts.Token)); + if (completedTask == task) + { + cts.Cancel(); + return true; + } + else + { + return (task.IsCompleted); + } + } + } + + // Task.GetAwaiter().GetResult() calls an internal variant of Wait() which doesn't wrap exceptions in + // an AggregateException. It does spinwait so if it's expected that the Task isn't about to complete, + // then use the NoSpin variant. + public static void WaitForCompletion(this Task task) + { + task.GetAwaiter().GetResult(); + } + + // If the task is about to complete, this method will be more expensive than the regular method as it + // always causes a WaitHandle to be allocated. If it is expected that the task will take longer than + // the time of a spin wait, then a WaitHandle will be allocated anyway and this method avoids the CPU + // cost of the spin wait. + public static void WaitForCompletionNoSpin(this Task task) + { + if (!task.IsCompleted) + { + ((IAsyncResult)task).AsyncWaitHandle.WaitOne(); + } + + // Call GetResult() to get any exceptions that were thrown + task.GetAwaiter().GetResult(); + } + + public static TResult WaitForCompletion(this Task task) + { + return task.GetAwaiter().GetResult(); + } + + public static TResult WaitForCompletionNoSpin(this Task task) + { + if (!task.IsCompleted) + { + ((IAsyncResult)task).AsyncWaitHandle.WaitOne(); + } + + return task.GetAwaiter().GetResult(); + } + + public static bool WaitForCompletionNoSpin(this Task task, TimeSpan timeout) + { + if (timeout >= TimeoutHelper.MaxWait) + { + task.WaitForCompletionNoSpin(); + return true; + } + + bool completed = true; + if (!task.IsCompleted) + { + completed = ((IAsyncResult)task).AsyncWaitHandle.WaitOne(timeout); + } + + if (completed) + { + // Throw any exceptions if there are any + task.GetAwaiter().GetResult(); + } + + return completed; + } + + // Used by WebSocketTransportDuplexSessionChannel on the sync code path. + // TODO: Try and switch as many code paths as possible which use this to async + public static void Wait(this Task task, TimeSpan timeout, Action exceptionConverter, string operationType) + { + bool timedOut = false; + + try + { + timedOut = !task.WaitForCompletionNoSpin(timeout); + } + catch (Exception ex) + { + if (Fx.IsFatal(ex) || exceptionConverter == null) + { + throw; + } + + exceptionConverter(ex, timeout, operationType); + } + + if (timedOut) + { + throw Fx.Exception.AsError(new TimeoutException(SR.Format(SR.TaskTimedOutError, timeout))); + } + } + + public static Task CompletedTask() + { + return Task.FromResult(true); + } + + public static DefaultTaskSchedulerAwaiter EnsureDefaultTaskScheduler() + { + return DefaultTaskSchedulerAwaiter.Singleton; + } + + public static Action OnAsyncCompletionCallback = OnAsyncCompletion; + + // Method to act as callback for asynchronous code which uses AsyncCompletionResult as the return type when used within + // a Task based async method. These methods require a callback which is called in the case of the IO completing asynchronously. + // This pattern still requires an allocation, whereas the purpose of using the AsyncCompletionResult enum is to avoid allocation. + // In the future, this pattern should be replaced with a reusable awaitable object, potentially with a global pool. + private static void OnAsyncCompletion(object state) + { + var tcs = state as TaskCompletionSource; + Fx.Assert(state != null, "Async state should be of type TaskCompletionSource"); + tcs.TrySetResult(true); + } + + public static IDisposable RunTaskContinuationsOnOurThreads() + { + if (SynchronizationContext.Current == ServiceModelSynchronizationContext.Instance) + { + return null; // No need to save and restore state as we're already using the correct sync context + } + + return new SyncContextScope(); + } + + // Calls the given Action asynchronously. + public static async Task CallActionAsync(Action action, TArg argument) + { + using (var scope = TaskHelpers.RunTaskContinuationsOnOurThreads()) + { + if (scope != null) // No need to change threads if already off of thread pool + { + await Task.Yield(); // Move synchronous method off of thread pool + } + + action(argument); + } + } + + public static Task CompletedOrCanceled(CancellationToken token) + { + if (token.IsCancellationRequested) + { + return Task.FromCanceled(token); + } + + return Task.CompletedTask; + } + + private class SyncContextScope : IDisposable + { + private readonly SynchronizationContext _prevContext; + + public SyncContextScope() + { + _prevContext = SynchronizationContext.Current; + SynchronizationContext.SetSynchronizationContext(ServiceModelSynchronizationContext.Instance); + } + + public void Dispose() + { + SynchronizationContext.SetSynchronizationContext(_prevContext); + } + } + + public static Task CancellableAsyncWait(this Task task, CancellationToken token) + { + if (!token.CanBeCanceled) + { + return task; + } + + var state = new object[2]; + var tcs = new TaskCompletionSource(state); + state[0] = tcs; + var registration = token.Register(OnCancellation, state); + state[1] = registration; + task.ContinueWith((antecedent, obj) => + { + var stateArr = (object[])obj; + var tcsObj = (TaskCompletionSource)stateArr[0]; + var tokenRegistration = (CancellationTokenRegistration)stateArr[1]; + tokenRegistration.Dispose(); + if (antecedent.IsFaulted) tcsObj.TrySetException(antecedent.Exception.InnerException); + else if (antecedent.IsCanceled) tcsObj.TrySetCanceled(); + else tcsObj.TrySetResult(antecedent.Result); + }, state, CancellationToken.None, TaskContinuationOptions.HideScheduler, TaskScheduler.Default); + + return tcs.Task; + } + + private static void OnCancellation(object state) + { + var stateArr = (object[])state; + var tcsObj = (TaskCompletionSource)stateArr[0]; + var tokenRegistration = (CancellationTokenRegistration)stateArr[1]; + tcsObj.TrySetCanceled(); + tokenRegistration.Dispose(); + } + + internal static SynchronizationContextAwaiter GetAwaiter(this SynchronizationContext syncContext) + { + return new SynchronizationContextAwaiter(syncContext); + } + + internal static Task<(object result, object[] outputs)> InvokeAsync(this IOperationInvoker operationInvoker, object instance, object[] inputs) + { + var helper = new OperationInvokerAsyncHelper(operationInvoker, instance); + return helper.InvokeAsync(inputs); + } + } + + // This awaiter causes an awaiting async method to continue on the same thread if using the + // default task scheduler, otherwise it posts the continuation to the ThreadPool. While this + // does a similar function to Task.ConfigureAwait, this code doesn't require a Task to function. + // With Task.ConfigureAwait, you would need to call it on the first task on each potential code + // path in a method. This could mean calling ConfigureAwait multiple times in a single method. + // This awaiter can be awaited on at the beginning of a method a single time and isn't dependant + // on running other awaitable code. + internal struct DefaultTaskSchedulerAwaiter : INotifyCompletion + { + public static DefaultTaskSchedulerAwaiter Singleton = new DefaultTaskSchedulerAwaiter(); + + // If the current TaskScheduler is the default, if we aren't currently running inside a task and + // the default SynchronizationContext isn't current, when a Task starts, it will change the TaskScheduler + // to one based off the current SynchronizationContext. Also, any async api's that WCF consumes will + // post back to the same SynchronizationContext as they were started in which could cause WCF to deadlock + // on our Sync code path. + public bool IsCompleted + { + get + { + return (TaskScheduler.Current == TaskScheduler.Default) && + (SynchronizationContext.Current == null || + (SynchronizationContext.Current.GetType() == typeof(SynchronizationContext))); + } + } + + // Only called when IsCompleted returns false, otherwise the caller will call the continuation + // directly causing it to stay on the same thread. + public void OnCompleted(Action continuation) + { + Task.Run(continuation); + } + + // Awaiter is only used to control where subsequent awaitable's run so GetResult needs no + // implementation. Normally any exceptions would be thrown here, but we have nothing to throw + // as we don't run anything, only control where other code runs. + public void GetResult() { } + + public DefaultTaskSchedulerAwaiter GetAwaiter() + { + return this; + } + } + + internal struct SynchronizationContextAwaiter : INotifyCompletion + { + private readonly SynchronizationContext _syncContext; + + public SynchronizationContextAwaiter(SynchronizationContext syncContext) + { + _syncContext = syncContext; + } + + public bool IsCompleted => _syncContext == SynchronizationContext.Current; + + public void OnCompleted(Action continuation) => _syncContext.Post(SynchronizationContextAwaiter.PostCallback, continuation); + + public void GetResult() { } + + internal static void PostCallback(object state) + { + ((Action)state)(); + } + } + + internal class OperationInvokerAsyncHelper + { + private IOperationInvoker _operationInvoker; + private object _instance; + private TaskCompletionSource<(object result, object[] outputs)> _tcs; + + public OperationInvokerAsyncHelper(IOperationInvoker operationInvoker, object instance) + { + _operationInvoker = operationInvoker; + _instance = instance; + } + + public Task<(object result, object[] outputs)> InvokeAsync(object[] inputs) + { + _tcs = new TaskCompletionSource<(object result, object[] outputs)>(); + var iar = _operationInvoker.InvokeBegin(_instance, inputs, Callback, this); + if (iar.CompletedSynchronously) + { + Complete(iar); + } + + return _tcs.Task; + } + + private void Complete(IAsyncResult iar) + { + var result = _operationInvoker.InvokeEnd(_instance, out object[] outputs, iar); + _tcs.TrySetResult((result: result, outputs: outputs)); + } + + internal static void Callback(IAsyncResult iar) + { + if (iar.CompletedSynchronously) + { + return; + } + + var helper = iar.AsyncState as OperationInvokerAsyncHelper; + if (helper == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.SFxInvalidAsyncResultState0); + } + + helper.Complete(iar); + } + } + + // Async methods can't take an out (or ref) argument. This wrapper allows passing in place of an out argument + // and can be used to return a value via a method argument. + internal class OutWrapper + { + public OutWrapper() + { + Value = default(T); + } + + public T Value { get; set; } + + public static implicit operator T(OutWrapper wrapper) + { + return wrapper.Value; + } + } +} \ No newline at end of file diff --git a/src/Common/src/CoreWCF/Runtime/Ticks.cs b/src/Common/src/CoreWCF/Runtime/Ticks.cs new file mode 100644 index 000000000..abb0f7b0a --- /dev/null +++ b/src/Common/src/CoreWCF/Runtime/Ticks.cs @@ -0,0 +1,57 @@ +using System; + +namespace CoreWCF.Runtime +{ + internal static class Ticks + { + public static long Now + { + get + { + return DateTime.UtcNow.Ticks; + } + } + + public static long FromMilliseconds(int milliseconds) + { + return checked((long)milliseconds * TimeSpan.TicksPerMillisecond); + } + + public static int ToMilliseconds(long ticks) + { + return checked((int)(ticks / TimeSpan.TicksPerMillisecond)); + } + + public static long FromTimeSpan(TimeSpan duration) + { + return duration.Ticks; + } + + public static TimeSpan ToTimeSpan(long ticks) + { + return new TimeSpan(ticks); + } + + public static long Add(long firstTicks, long secondTicks) + { + if (firstTicks == long.MaxValue || firstTicks == long.MinValue) + { + return firstTicks; + } + if (secondTicks == long.MaxValue || secondTicks == long.MinValue) + { + return secondTicks; + } + if (firstTicks >= 0 && long.MaxValue - firstTicks <= secondTicks) + { + return long.MaxValue - 1; + } + if (firstTicks <= 0 && long.MinValue - firstTicks >= secondTicks) + { + return long.MinValue + 1; + } + return checked(firstTicks + secondTicks); + } + } + +} \ No newline at end of file diff --git a/src/Common/src/CoreWCF/Runtime/TimeoutHelper.cs b/src/Common/src/CoreWCF/Runtime/TimeoutHelper.cs new file mode 100644 index 000000000..658287864 --- /dev/null +++ b/src/Common/src/CoreWCF/Runtime/TimeoutHelper.cs @@ -0,0 +1,332 @@ +using System; +using System.Collections.Concurrent; +using System.Diagnostics; +using System.Diagnostics.Contracts; +using System.Threading; +using System.Threading.Tasks; + +namespace CoreWCF.Runtime +{ + internal struct TimeoutHelper + { + // This is a brief plan on how to recover original timeout from CancellationToken + // The coalescing is needed to prevent hammering at the timer queue otherwise we + // get a lot of contention on it. It's really only the timer that needs to be coalesced. + // 1. Change the coalescing to keep track of IOThreadTimer object + // 2. Create a class which derives from/encapsulates an IOThreadTimer which can be used to + // register CancellationTokenSource objects to call cancel when the timer fires. This + // is the class that we coalesce. + // 3. Create RecoverableTimeoutCancellationTokenSource which derives from Cancellation token source. + // It has the following behavior: + // a. Takes the derived IOThreadTimer from (2) in the constructor and registers cancelling itself + // when the timer fires. + // b. Takes the requested timeout in the constructor and saves it. + // c. Override the GetHashCode method to return a value representing the original timeout value. + // CancellationToken defers to the owning CancellationTokenSource to implement GetHashCode() + // 4. Add a TimeoutHelper method which returns a TimeSpan from a CancellationToken. It queries the + // GetHashCode method to get the original requested timeout. + public static readonly TimeSpan MaxWait = TimeSpan.FromMilliseconds(int.MaxValue); + private static readonly CancellationToken s_precancelledToken = new CancellationToken(true); + + private bool _cancellationTokenInitialized; + private bool _deadlineSet; + + private CancellationToken _cancellationToken; + private DateTime _deadline; + private TimeSpan _originalTimeout; + + public TimeoutHelper(TimeSpan timeout) + { + Fx.Assert(timeout >= TimeSpan.Zero || timeout == Timeout.InfiniteTimeSpan, + $"timeout must be non-negative or {Timeout.InfiniteTimeSpan}"); + + _cancellationTokenInitialized = false; + _originalTimeout = timeout; + _deadline = DateTime.MaxValue; + _deadlineSet = (timeout == TimeSpan.MaxValue || timeout == Timeout.InfiniteTimeSpan); + } + + public CancellationToken GetCancellationToken() + { + if (!_cancellationTokenInitialized) + { + var timeout = RemainingTime(); + if (timeout >= MaxWait || timeout == Timeout.InfiniteTimeSpan) + { + _cancellationToken = CancellationToken.None; + } + else if (timeout > TimeSpan.Zero) + { + _cancellationToken = TimeoutTokenSource.FromTimeout((int)timeout.TotalMilliseconds); + } + else + { + _cancellationToken = s_precancelledToken; + } + _cancellationTokenInitialized = true; + } + + return _cancellationToken; + } + + + public TimeSpan OriginalTimeout + { + get { return _originalTimeout; } + } + + public static bool IsTooLarge(TimeSpan timeout) + { + return (timeout > TimeoutHelper.MaxWait) && (timeout != TimeSpan.MaxValue); + } + + public static TimeSpan FromMilliseconds(int milliseconds) + { + if (milliseconds == Timeout.Infinite) + { + return TimeSpan.MaxValue; + } + else + { + return TimeSpan.FromMilliseconds(milliseconds); + } + } + + public static int ToMilliseconds(TimeSpan timeout) + { + if (timeout == TimeSpan.MaxValue) + { + return Timeout.Infinite; + } + else + { + long ticks = Ticks.FromTimeSpan(timeout); + if (ticks / TimeSpan.TicksPerMillisecond > int.MaxValue) + { + return int.MaxValue; + } + return Ticks.ToMilliseconds(ticks); + } + } + + public static TimeSpan Min(TimeSpan val1, TimeSpan val2) + { + if (val1 > val2) + { + return val2; + } + else + { + return val1; + } + } + + public static TimeSpan Add(TimeSpan timeout1, TimeSpan timeout2) + { + return Ticks.ToTimeSpan(Ticks.Add(Ticks.FromTimeSpan(timeout1), Ticks.FromTimeSpan(timeout2))); + } + + public static DateTime Add(DateTime time, TimeSpan timeout) + { + if (timeout >= TimeSpan.Zero && DateTime.MaxValue - time <= timeout) + { + return DateTime.MaxValue; + } + if (timeout <= TimeSpan.Zero && DateTime.MinValue - time >= timeout) + { + return DateTime.MinValue; + } + return time + timeout; + } + + public static DateTime Subtract(DateTime time, TimeSpan timeout) + { + return Add(time, TimeSpan.Zero - timeout); + } + + public static TimeSpan Divide(TimeSpan timeout, int factor) + { + if (timeout == TimeSpan.MaxValue) + { + return TimeSpan.MaxValue; + } + + return Ticks.ToTimeSpan((Ticks.FromTimeSpan(timeout) / factor) + 1); + } + + public TimeSpan RemainingTime() + { + if (!_deadlineSet) + { + SetDeadline(); + return _originalTimeout; + } + else if (_deadline == DateTime.MaxValue) + { + return TimeSpan.MaxValue; + } + else + { + TimeSpan remaining = _deadline - DateTime.UtcNow; + if (remaining <= TimeSpan.Zero) + { + return TimeSpan.Zero; + } + else + { + return remaining; + } + } + } + + public TimeSpan ElapsedTime() + { + return _originalTimeout - RemainingTime(); + } + + private void SetDeadline() + { + Fx.Assert(!_deadlineSet, "TimeoutHelper deadline set twice."); + _deadline = DateTime.UtcNow + _originalTimeout; + _deadlineSet = true; + } + + public static TimeSpan GetOriginalTimeout(CancellationToken token) + { + return RecoverableTimeoutCancellationTokenSource.GetOriginalTimeout(token); + } + + public static void ThrowIfNegativeArgument(TimeSpan timeout) + { + ThrowIfNegativeArgument(timeout, "timeout"); + } + + public static void ThrowIfNegativeArgument(TimeSpan timeout, string argumentName) + { + if (timeout < TimeSpan.Zero) + { + throw Fx.Exception.ArgumentOutOfRange(argumentName, timeout, SR.Format(SR.TimeoutMustBeNonNegative, argumentName, timeout)); + } + } + + public static void ThrowIfNonPositiveArgument(TimeSpan timeout) + { + ThrowIfNonPositiveArgument(timeout, "timeout"); + } + + public static void ThrowIfNonPositiveArgument(TimeSpan timeout, string argumentName) + { + if (timeout <= TimeSpan.Zero) + { + throw Fx.Exception.ArgumentOutOfRange(argumentName, timeout, SR.Format(SR.TimeoutMustBePositive, argumentName, timeout)); + } + } + + public static bool WaitOne(WaitHandle waitHandle, TimeSpan timeout) + { + ThrowIfNegativeArgument(timeout); + if (timeout == TimeSpan.MaxValue) + { + waitHandle.WaitOne(); + return true; + } + else + { + // http://msdn.microsoft.com/en-us/library/85bbbxt9(v=vs.110).aspx + // with exitContext was used in Desktop which is not supported in Net Native or CoreClr + return waitHandle.WaitOne(timeout); + } + } + + internal static TimeoutException CreateEnterTimedOutException(TimeSpan timeout) + { + return new TimeoutException(SR.Format(SR.LockTimeoutExceptionMessage, timeout)); + } + } + + /// + /// This class coalesces timeout tokens because cancelation tokens with timeouts are more expensive to expose. + /// Disposing too many such tokens will cause thread contentions in high throughput scenario. + /// + /// Tokens with target cancelation time 15ms apart would resolve to the same instance. + /// + internal static class TimeoutTokenSource + { + /// + /// These are constants use to calculate timeout coalescing, for more description see method FromTimeoutAsync + /// + private const int CoalescingFactor = 15; + private const int GranularityFactor = 2000; + private const int SegmentationFactor = CoalescingFactor * GranularityFactor; + + private static readonly ConcurrentDictionary s_timerCache = + new ConcurrentDictionary(); + + private static readonly Action s_deregisterTimer = (object state) => + { + var targetTime = (long)state; + CancellationTokenSourceIOThreadTimer ignored; + s_timerCache.TryRemove(targetTime, out ignored); + }; + + public static CancellationToken FromTimeout(int millisecondsTimeout) + { + // Note that CancellationTokenSource constructor requires input to be >= -1, + // restricting millisecondsTimeout to be >= -1 would enforce that + if (millisecondsTimeout < -1) + { + throw new ArgumentOutOfRangeException("Invalid millisecondsTimeout value " + millisecondsTimeout); + } + + // To prevent s_tokenCache growing too large, we have to adjust the granularity of the our coalesce depending + // on the value of millisecondsTimeout. The coalescing span scales proportionally with millisecondsTimeout which + // would garentee constant s_tokenCache size in the case where similar millisecondsTimeout values are accepted. + // If the method is given a wildly different millisecondsTimeout values all the time, the dictionary would still + // only grow logarithmically with respect to the range of the input values + + uint currentTime = (uint)Environment.TickCount; + long targetTime = millisecondsTimeout + currentTime; + + // Formula for our coalescing span: + // Divide millisecondsTimeout by SegmentationFactor and take the highest bit and then multiply CoalescingFactor back + var segmentValue = millisecondsTimeout / SegmentationFactor; + var coalescingSpanMs = CoalescingFactor; + while (segmentValue > 0) + { + segmentValue >>= 1; + coalescingSpanMs <<= 1; + } + targetTime = ((targetTime + (coalescingSpanMs - 1)) / coalescingSpanMs) * coalescingSpanMs; + + CancellationTokenSourceIOThreadTimer ctsTimer; + if (!s_timerCache.TryGetValue(targetTime, out ctsTimer)) + { + ctsTimer = new CancellationTokenSourceIOThreadTimer(); + + // only a single thread may succeed adding its timer into the cache + if (s_timerCache.TryAdd(targetTime, ctsTimer)) + { + // Clean up cache when timer fires + ctsTimer.SetCompletionCallback(s_deregisterTimer, targetTime); + ctsTimer.Set((int)(targetTime - currentTime)); + } + else + { + // for threads that failed when calling TryAdd, there should be one already in the cache + if (!s_timerCache.TryGetValue(targetTime, out ctsTimer)) + { + // In unlikely scenario the timer has already fired, we would not find it in cache. + // In this case we would simply create a CTS which doesn't use the coalesced timer. + var cts = new RecoverableTimeoutCancellationTokenSource(millisecondsTimeout); + cts.CancelAfter(millisecondsTimeout); + return cts.Token; + } + } + } + + var tokenSource = new RecoverableTimeoutCancellationTokenSource(millisecondsTimeout); + ctsTimer.RegisterTokenSourceForCancellation(tokenSource); + return tokenSource.Token; + } + } +} \ No newline at end of file diff --git a/src/Common/src/CoreWCF/Runtime/TraceSR.cs b/src/Common/src/CoreWCF/Runtime/TraceSR.cs new file mode 100644 index 000000000..6c7f12134 --- /dev/null +++ b/src/Common/src/CoreWCF/Runtime/TraceSR.cs @@ -0,0 +1,21 @@ +namespace CoreWCF.Runtime +{ + internal sealed class TraceSR + { + internal const string GenericCallbackException = "A user callback threw an exception. Check the exception stack and inner exception to determine the callback that failed."; + + /* Here are the TraceSR resource strings which come from SMDiagnostics + * TODO: Convert this to use the resource mechanism so localization can happen + ActivityBoundary=Activity boundary. + ThrowingException=Throwing an exception. + TraceHandledException=Handling an exception. + TraceCodeAppDomainUnload=AppDomain unloading. + TraceCodeEventLog=Wrote to the EventLog. + TraceCodeTraceTruncatedQuotaExceeded=A trace size quota was exceeded. The trace was truncated. + UnhandledException=Unhandled exception + WriteCharsInvalidContent=The contents of the buffer passed to PlainXmlWriter.WriteChars are invalid. + GenericCallbackException=A user callback threw an exception. Check the exception stack and inner exception to determine the callback that failed. + StringNullOrEmpty=The input string parameter is either null or empty. + */ + } +} \ No newline at end of file diff --git a/src/Common/src/CoreWCF/SR.cs b/src/Common/src/CoreWCF/SR.cs new file mode 100644 index 000000000..0ae689226 --- /dev/null +++ b/src/Common/src/CoreWCF/SR.cs @@ -0,0 +1,100 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Resources; +using System.Runtime.CompilerServices; + +namespace CoreWCF +{ + internal partial class SR + { + private static ResourceManager s_resourceManager; + + private static ResourceManager ResourceManager + { + get + { + if (s_resourceManager == null) + { + s_resourceManager = new ResourceManager(SR.ResourceType); + } + return s_resourceManager; + } + } + + // This method is used to decide if we need to append the exception message parameters to the message when calling SR.Format. + // by default it returns false. + [MethodImpl(MethodImplOptions.NoInlining)] + private static bool UsingResourceKeys() + { + return false; + } + + internal static string GetResourceString(string resourceKey, string defaultString) + { + string resourceString = null; + try { resourceString = ResourceManager.GetString(resourceKey); } + catch (MissingManifestResourceException) { } + + if (defaultString != null && resourceKey.Equals(resourceString, StringComparison.Ordinal)) + { + return defaultString; + } + + return resourceString; + } + + internal static string Format(string resourceFormat, params object[] args) + { + if (args != null) + { + if (UsingResourceKeys()) + { + return resourceFormat + string.Join(", ", args); + } + + return string.Format(resourceFormat, args); + } + + return resourceFormat; + } + + // This is to make conversions of SR.Format(SR.ResourceName) to SR.Format(SR.ResourceName) be equivalent + // to SR.ResourceName. The call should be automatically refactored out. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static string Format(string resourceFormat) + { + return resourceFormat; + } + + internal static string Format(string resourceFormat, object p1) + { + if (UsingResourceKeys()) + { + return string.Join(", ", resourceFormat, p1); + } + + return string.Format(resourceFormat, p1); + } + + internal static string Format(string resourceFormat, object p1, object p2) + { + if (UsingResourceKeys()) + { + return string.Join(", ", resourceFormat, p1, p2); + } + + return string.Format(resourceFormat, p1, p2); + } + + internal static string Format(string resourceFormat, object p1, object p2, object p3) + { + if (UsingResourceKeys()) + { + return string.Join(", ", resourceFormat, p1, p2, p3); + } + return string.Format(resourceFormat, p1, p2, p3); + } + } +} diff --git a/src/Common/src/CoreWCF/TransferModeHelper.cs b/src/Common/src/CoreWCF/TransferModeHelper.cs new file mode 100644 index 000000000..2b05ffff1 --- /dev/null +++ b/src/Common/src/CoreWCF/TransferModeHelper.cs @@ -0,0 +1,34 @@ +using System; +using System.ComponentModel; +using System.Globalization; + +namespace CoreWCF +{ + static class TransferModeHelper + { + public static bool IsDefined(TransferMode v) + { + return ((v == TransferMode.Buffered) || (v == TransferMode.Streamed) || + (v == TransferMode.StreamedRequest) || (v == TransferMode.StreamedResponse)); + } + + public static bool IsRequestStreamed(TransferMode v) + { + return ((v == TransferMode.StreamedRequest) || (v == TransferMode.Streamed)); + } + + public static bool IsResponseStreamed(TransferMode v) + { + return ((v == TransferMode.StreamedResponse) || (v == TransferMode.Streamed)); + } + + public static void Validate(TransferMode value) + { + if (!IsDefined(value)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidEnumArgumentException(nameof(value), (int)value, + typeof(TransferMode))); + } + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Http/src/CoreWCF.Http.csproj b/src/CoreWCF.Http/src/CoreWCF.Http.csproj new file mode 100644 index 000000000..818d7c442 --- /dev/null +++ b/src/CoreWCF.Http/src/CoreWCF.Http.csproj @@ -0,0 +1,14 @@ + + + netcoreapp2.2 + CoreWCF.Http + CoreWCF.Http + True + + + + + + + + \ No newline at end of file diff --git a/src/CoreWCF.Http/src/CoreWCF/ActionMismatchAddressingException.cs b/src/CoreWCF.Http/src/CoreWCF/ActionMismatchAddressingException.cs new file mode 100644 index 000000000..e07b1a95f --- /dev/null +++ b/src/CoreWCF.Http/src/CoreWCF/ActionMismatchAddressingException.cs @@ -0,0 +1,51 @@ +using CoreWCF.Runtime; +using CoreWCF.Channels; +using System; +using System.Runtime.Serialization; + +namespace CoreWCF +{ + [Serializable] + internal class ActionMismatchAddressingException : ProtocolException + { + string httpActionHeader; + string soapActionHeader; + + public ActionMismatchAddressingException(string message, string soapActionHeader, string httpActionHeader) + : base(message) + { + this.httpActionHeader = httpActionHeader; + this.soapActionHeader = soapActionHeader; + } + + protected ActionMismatchAddressingException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + public string HttpActionHeader + { + get + { + return httpActionHeader; + } + } + + public string SoapActionHeader + { + get + { + return soapActionHeader; + } + } + + internal Message ProvideFault(MessageVersion messageVersion) + { + Fx.Assert(messageVersion.Addressing == AddressingVersion.WSAddressing10, ""); + WSAddressing10ProblemHeaderQNameFault phf = new WSAddressing10ProblemHeaderQNameFault(this); + Message message = CoreWCF.Channels.Message.CreateMessage(messageVersion, phf, messageVersion.Addressing.FaultAction()); + phf.AddHeaders(message.Headers); + return message; + } + } +} diff --git a/src/CoreWCF.Http/src/CoreWCF/BasicHttpBinding.cs b/src/CoreWCF.Http/src/CoreWCF/BasicHttpBinding.cs new file mode 100644 index 000000000..bf9f721b7 --- /dev/null +++ b/src/CoreWCF.Http/src/CoreWCF/BasicHttpBinding.cs @@ -0,0 +1,55 @@ +using System; +using CoreWCF.Channels; +using CoreWCF.Description; + +namespace CoreWCF +{ + public class BasicHttpBinding : HttpBindingBase + { + private WSMessageEncoding _messageEncoding = BasicHttpBindingDefaults.MessageEncoding; + + //public BasicHttpBinding(System.ServiceModel.BasicHttpSecurityMode securityMode) { } + //public System.ServiceModel.BasicHttpSecurity Security { get { return default(CoreWCF.BasicHttpSecurity); } set { } } + //public override CoreWCF.Channels.IChannelFactory BuildChannelFactory(CoreWCF.Channels.BindingParameterCollection parameters) { return default(CoreWCF.Channels.IChannelFactory); } + + internal WSMessageEncoding MessageEncoding + { + get { return _messageEncoding; } + set { _messageEncoding = value; } + } + + public override IChannelListener BuildChannelListener(Uri listenUriBaseAddress, + string listenUriRelativeAddress, ListenUriMode listenUriMode, BindingParameterCollection parameters) + { + throw new PlatformNotSupportedException("BuildChannelListener not supported for HTTP"); + } + + public override bool CanBuildChannelListener(BindingParameterCollection parameters) + { + return false; + } + + public override BindingElementCollection CreateBindingElements() + { + CheckSettings(); + + // return collection of BindingElements + BindingElementCollection bindingElements = new BindingElementCollection(); + + // order of BindingElements is important + // add security (*optional) + //SecurityBindingElement wsSecurity = BasicHttpSecurity.CreateMessageSecurity(); + //if (wsSecurity != null) + //{ + // bindingElements.Add(wsSecurity); + //} + // add encoding + if (MessageEncoding == WSMessageEncoding.Text) + bindingElements.Add(TextMessageEncodingBindingElement); + // add transport (http or https) + bindingElements.Add(GetTransport()); + + return bindingElements.Clone(); + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Http/src/CoreWCF/CallbackException.cs b/src/CoreWCF.Http/src/CoreWCF/CallbackException.cs new file mode 100644 index 000000000..df147d6d5 --- /dev/null +++ b/src/CoreWCF.Http/src/CoreWCF/CallbackException.cs @@ -0,0 +1,23 @@ +using System; + +namespace CoreWCF.Runtime +{ + //[Serializable] + class CallbackException : FatalException + { + public CallbackException() + { + } + + public CallbackException(string message, Exception innerException) : base(message, innerException) + { + // This can't throw something like ArgumentException because that would be worse than + // throwing the callback exception that was requested. + Fx.Assert(innerException != null, "CallbackException requires an inner exception."); + Fx.Assert(!Fx.IsFatal(innerException), "CallbackException can't be used to wrap fatal exceptions."); + } + //protected CallbackException(SerializationInfo info, StreamingContext context) : base(info, context) + //{ + //} + } +} \ No newline at end of file diff --git a/src/CoreWCF.Http/src/CoreWCF/Channels/AddressingVersionExtensions.cs b/src/CoreWCF.Http/src/CoreWCF/Channels/AddressingVersionExtensions.cs new file mode 100644 index 000000000..b978ca1bb --- /dev/null +++ b/src/CoreWCF.Http/src/CoreWCF/Channels/AddressingVersionExtensions.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Text; + +namespace CoreWCF.Channels +{ + internal static class AddressingVersionExtensions + { + static AddressingVersionExtensions() + { + s_namespaceGetter = GetStringGetterForProperty("Namespace"); + s_faultActionGetter = GetStringGetterForProperty("FaultAction"); + s_anonymousUriGetter = GetUriGetterForProperty("AnonymousUri"); + s_noneUriGetter = GetUriGetterForProperty("NoneUri"); + } + + private static StringGetter GetStringGetterForProperty(string propName) + { + // NonPublic and public in case they are made public in the future. Currently they are internal + var getterPropInfo = typeof(AddressingVersion).GetProperty(propName, BindingFlags.NonPublic | BindingFlags.Public); + var getterMethodInfo = getterPropInfo.GetGetMethod(); + return (StringGetter)getterMethodInfo.CreateDelegate(typeof(StringGetter)); + } + + private delegate string StringGetter(AddressingVersion addressingVersion); + + private static StringGetter s_namespaceGetter; + private static StringGetter s_faultActionGetter; + + public static string Namespace(this AddressingVersion addressingVersion) + { + return s_namespaceGetter(addressingVersion); + } + + public static string FaultAction(this AddressingVersion addressingVersion) + { + return s_faultActionGetter(addressingVersion); + } + + private static UriGetter GetUriGetterForProperty(string propName) + { + // NonPublic and public in case they are made public in the future. Currently they are internal + var getterPropInfo = typeof(AddressingVersion).GetProperty(propName, BindingFlags.NonPublic | BindingFlags.Public); + var getterMethodInfo = getterPropInfo.GetGetMethod(); + return (UriGetter)getterMethodInfo.CreateDelegate(typeof(UriGetter)); + } + + private delegate Uri UriGetter(AddressingVersion addressingVersion); + + private static UriGetter s_anonymousUriGetter; + private static UriGetter s_noneUriGetter; + + public static Uri AnonymousUri(this AddressingVersion addressingVersion) + { + return s_anonymousUriGetter(addressingVersion); + } + + public static Uri NoneUri(this AddressingVersion addressingVersion) + { + return s_noneUriGetter(addressingVersion); + } + } +} diff --git a/src/CoreWCF.Http/src/CoreWCF/Channels/AspNetCoreReplyChannel.cs b/src/CoreWCF.Http/src/CoreWCF/Channels/AspNetCoreReplyChannel.cs new file mode 100644 index 000000000..3b454e4c6 --- /dev/null +++ b/src/CoreWCF.Http/src/CoreWCF/Channels/AspNetCoreReplyChannel.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; + +namespace CoreWCF.Channels +{ + internal class AspNetCoreReplyChannel : IReplyChannel + { + private IServiceProvider _serviceProvider; + + public AspNetCoreReplyChannel(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + } + + // TODO: Verify what happens on .NET Framework. Looking at code it looks like it doesn't set this value + public EndpointAddress LocalAddress => null; + + // TODO: Might want to do something a bit smarter with the state and actually have a concept of opening and closing to enable event handlers to be + // connected and fire them when the service is shutting down. + public CommunicationState State => CommunicationState.Created; + + public event EventHandler Closed; + public event EventHandler Closing; + public event EventHandler Faulted; + public event EventHandler Opened; + public event EventHandler Opening; + + public void Abort() + { + + } + + public Task CloseAsync() + { + return Task.CompletedTask; + } + + public Task CloseAsync(CancellationToken token) + { + return Task.CompletedTask; + } + + public T GetProperty() where T : class + { + return _serviceProvider.GetService(); + } + + public Task OpenAsync() + { + return Task.CompletedTask; + } + + public Task OpenAsync(CancellationToken token) + { + return Task.CompletedTask; + } + + public Task ReceiveRequestAsync() + { + throw new NotImplementedException(); + } + + public Task ReceiveRequestAsync(CancellationToken token) + { + throw new NotImplementedException(); + } + + public Task> TryReceiveRequestAsync(CancellationToken token) + { + throw new NotImplementedException(); + } + + public Task WaitForRequestAsync(CancellationToken token) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/CoreWCF.Http/src/CoreWCF/Channels/BinaryVersion.cs b/src/CoreWCF.Http/src/CoreWCF/Channels/BinaryVersion.cs new file mode 100644 index 000000000..a49e108b6 --- /dev/null +++ b/src/CoreWCF.Http/src/CoreWCF/Channels/BinaryVersion.cs @@ -0,0 +1,24 @@ +using System.Xml; + +namespace CoreWCF.Channels +{ + class BinaryVersion + { + static public readonly BinaryVersion Version1 = new BinaryVersion(FramingEncodingString.Binary, FramingEncodingString.BinarySession, ServiceModelDictionary.Version1); + static public readonly BinaryVersion GZipVersion1 = new BinaryVersion(FramingEncodingString.ExtendedBinaryGZip, FramingEncodingString.ExtendedBinarySessionGZip, ServiceModelDictionary.Version1); + static public readonly BinaryVersion DeflateVersion1 = new BinaryVersion(FramingEncodingString.ExtendedBinaryDeflate, FramingEncodingString.ExtendedBinarySessionDeflate, ServiceModelDictionary.Version1); + private readonly IXmlDictionary dictionary; + + BinaryVersion(string contentType, string sessionContentType, IXmlDictionary dictionary) + { + this.ContentType = contentType; + this.SessionContentType = sessionContentType; + this.dictionary = dictionary; + } + + static public BinaryVersion CurrentVersion { get { return Version1; } } + public string ContentType { get; } + public string SessionContentType { get; } + public IXmlDictionary Dictionary { get { return dictionary; } } + } +} diff --git a/src/CoreWCF.Http/src/CoreWCF/Channels/ContentOnlyMessage.cs b/src/CoreWCF.Http/src/CoreWCF/Channels/ContentOnlyMessage.cs new file mode 100644 index 000000000..74f9ca57b --- /dev/null +++ b/src/CoreWCF.Http/src/CoreWCF/Channels/ContentOnlyMessage.cs @@ -0,0 +1,106 @@ +using System; +using System.Xml; +using CoreWCF.Diagnostics; + +// TODO: This is duplicated from Primitives. Either move to common code and include in both places or add to contract. I would prefer the latter. +namespace CoreWCF.Channels +{ + /// + /// Base class for non-SOAP messages + /// + abstract class ContentOnlyMessage : Message + { + MessageHeaders headers; + MessageProperties properties; + + protected ContentOnlyMessage() + { + headers = new MessageHeaders(MessageVersion.None); + } + + public override MessageHeaders Headers + { + get + { + if (IsDisposed) + { + throw TraceUtility.ThrowHelperError(CreateMessageDisposedException(), this); + } + + return headers; + } + } + + public override MessageProperties Properties + { + get + { + if (IsDisposed) + { + throw TraceUtility.ThrowHelperError(CreateMessageDisposedException(), this); + } + + if (properties == null) + { + properties = new MessageProperties(); + } + + return properties; + } + } + + public override MessageVersion Version + { + get + { + return headers.MessageVersion; + } + } + + protected override void OnBodyToString(XmlDictionaryWriter writer) + { + OnWriteBodyContents(writer); + } + + internal Exception CreateMessageDisposedException() + { + return new ObjectDisposedException("", SR.MessageClosed); + } + } + + class StringMessage : ContentOnlyMessage + { + string data; + + public StringMessage(string data) + : base() + { + this.data = data; + } + + public override bool IsEmpty + { + get + { + return string.IsNullOrEmpty(data); + } + } + + protected override void OnWriteBodyContents(XmlDictionaryWriter writer) + { + if (data != null && data.Length > 0) + { + writer.WriteElementString("BODY", data); + } + } + } + + class NullMessage : StringMessage + { + public NullMessage() + : base(string.Empty) + { + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Http/src/CoreWCF/Channels/DetectEofStream.cs b/src/CoreWCF.Http/src/CoreWCF/Channels/DetectEofStream.cs new file mode 100644 index 000000000..e3fc555b8 --- /dev/null +++ b/src/CoreWCF.Http/src/CoreWCF/Channels/DetectEofStream.cs @@ -0,0 +1,72 @@ +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace CoreWCF.Channels +{ + abstract class DetectEofStream : DelegatingStream + { + bool _isAtEof; + + protected DetectEofStream(Stream stream) + : base(stream) + { + _isAtEof = false; + } + + protected bool IsAtEof => _isAtEof; + + public override async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + if (_isAtEof) + { + return 0; + } + int returnValue = await base.ReadAsync(buffer, offset, count, cancellationToken); + if (returnValue == 0) + { + ReceivedEof(); + } + return returnValue; + } + + public override int ReadByte() + { + int returnValue = base.ReadByte(); + if (returnValue == -1) + { + ReceivedEof(); + } + return returnValue; + } + + public override int Read(byte[] buffer, int offset, int count) + { + if (_isAtEof) + { + return 0; + } + int returnValue = base.Read(buffer, offset, count); + if (returnValue == 0) + { + ReceivedEof(); + } + return returnValue; + } + + private void ReceivedEof() + { + if (!_isAtEof) + { + _isAtEof = true; + OnReceivedEof(); + } + } + + protected virtual void OnReceivedEof() + { + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Http/src/CoreWCF/Channels/FramingFormat.cs b/src/CoreWCF.Http/src/CoreWCF/Channels/FramingFormat.cs new file mode 100644 index 000000000..f74b3ea6a --- /dev/null +++ b/src/CoreWCF.Http/src/CoreWCF/Channels/FramingFormat.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoreWCF.Channels +{ + static class FramingEncodingString + { + public const string Binary = "application/soap+msbin1"; + public const string BinarySession = "application/soap+msbinsession1"; + public const string ExtendedBinaryGZip = Binary + "+gzip"; + public const string ExtendedBinarySessionGZip = BinarySession + "+gzip"; + public const string ExtendedBinaryDeflate = Binary + "+deflate"; + public const string ExtendedBinarySessionDeflate = BinarySession + "+deflate"; + } +} diff --git a/src/CoreWCF.Http/src/CoreWCF/Channels/HttpAnonymousUriPrefixMatcher.cs b/src/CoreWCF.Http/src/CoreWCF/Channels/HttpAnonymousUriPrefixMatcher.cs new file mode 100644 index 000000000..a7eda2a1e --- /dev/null +++ b/src/CoreWCF.Http/src/CoreWCF/Channels/HttpAnonymousUriPrefixMatcher.cs @@ -0,0 +1,61 @@ +using CoreWCF.Runtime; +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoreWCF.Channels +{ + class HttpAnonymousUriPrefixMatcher : IAnonymousUriPrefixMatcher + { + UriPrefixTable anonymousUriPrefixes; + + internal HttpAnonymousUriPrefixMatcher() + { + } + + internal HttpAnonymousUriPrefixMatcher(HttpAnonymousUriPrefixMatcher objectToClone) + : this() + { + if (objectToClone.anonymousUriPrefixes != null) + { + this.anonymousUriPrefixes = new UriPrefixTable(objectToClone.anonymousUriPrefixes); + } + } + + public void Register(Uri anonymousUriPrefix) + { + if (anonymousUriPrefix == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(anonymousUriPrefix)); + } + + if (!anonymousUriPrefix.IsAbsoluteUri) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(nameof(anonymousUriPrefix), SR.UriMustBeAbsolute); + } + + if (this.anonymousUriPrefixes == null) + { + this.anonymousUriPrefixes = new UriPrefixTable(true); + } + + if (!this.anonymousUriPrefixes.IsRegistered(new BaseUriWithWildcard(anonymousUriPrefix, HostNameComparisonMode.Exact))) + { + this.anonymousUriPrefixes.RegisterUri(anonymousUriPrefix, HostNameComparisonMode.Exact, anonymousUriPrefix); + } + } + + internal bool IsAnonymousUri(Uri to) + { + Fx.Assert(to == null || to.IsAbsoluteUri, SR.UriMustBeAbsolute); + + if (this.anonymousUriPrefixes == null) + { + return false; + } + + Uri returnValue; + return this.anonymousUriPrefixes.TryLookupUri(to, HostNameComparisonMode.Exact, out returnValue); + } + } +} diff --git a/src/CoreWCF.Http/src/CoreWCF/Channels/HttpChannelHelpers.cs b/src/CoreWCF.Http/src/CoreWCF/Channels/HttpChannelHelpers.cs new file mode 100644 index 000000000..572c43369 --- /dev/null +++ b/src/CoreWCF.Http/src/CoreWCF/Channels/HttpChannelHelpers.cs @@ -0,0 +1,2006 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Features; +using CoreWCF.Runtime; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Net.Mime; +using System.Security.Authentication.ExtendedProtection; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Xml; + +namespace CoreWCF.Channels +{ + // abstract out the common functionality of an "HttpInput" + abstract class HttpInput + { + const string multipartRelatedMediaType = "multipart/related"; + const string startInfoHeaderParam = "start-info"; + const string defaultContentType = "application/octet-stream"; + + BufferManager bufferManager; + bool isRequest; + MessageEncoder messageEncoder; + IHttpTransportFactorySettings settings; + bool streamed; + WebException webException; + Stream inputStream; + bool enableChannelBinding; + bool errorGettingInputStream; + + protected HttpInput(IHttpTransportFactorySettings settings, bool isRequest, bool enableChannelBinding) + { + this.settings = settings; + this.bufferManager = settings.BufferManager; + this.messageEncoder = settings.MessageEncoderFactory.Encoder; + this.webException = null; + this.isRequest = isRequest; + this.inputStream = null; + this.enableChannelBinding = enableChannelBinding; + + if (isRequest) + { + this.streamed = TransferModeHelper.IsRequestStreamed(settings.TransferMode); + } + else + { + this.streamed = TransferModeHelper.IsResponseStreamed(settings.TransferMode); + } + } + + internal WebException WebException + { + get { return webException; } + set { webException = value; } + } + + // Note: This method will return null in the case where throwOnError is false, and a non-fatal error occurs. + // Please exercice caution when passing in throwOnError = false. This should basically only be done in error + // code paths, or code paths where there is very good reason that you would not want this method to throw. + // When passing in throwOnError = false, please handle the case where this method returns null. + public Stream GetInputStream(bool throwOnError) + { + if (inputStream == null && (throwOnError || !this.errorGettingInputStream)) + { + try + { + inputStream = GetInputStream(); + this.errorGettingInputStream = false; + } + catch (Exception e) + { + this.errorGettingInputStream = true; + if (throwOnError || Fx.IsFatal(e)) + { + throw; + } + + DiagnosticUtility.TraceHandledException(e, TraceEventType.Warning); + } + } + + return inputStream; + } + + // -1 if chunked + public abstract long ContentLength { get; } + protected abstract string ContentTypeCore { get; } + protected abstract bool HasContent { get; } + protected abstract string SoapActionHeader { get; } + protected abstract Stream GetInputStream(); + protected virtual ChannelBinding ChannelBinding { get { return null; } } + + protected string ContentType + { + get + { + string contentType = ContentTypeCore; + + if (string.IsNullOrEmpty(contentType)) + { + return defaultContentType; + } + + return contentType; + } + } + + void ThrowMaxReceivedMessageSizeExceeded() + { + if (isRequest) + { + ThrowHttpProtocolException(SR.Format(SR.MaxReceivedMessageSizeExceeded, settings.MaxReceivedMessageSize), HttpStatusCode.RequestEntityTooLarge); + } + else + { + string message = SR.Format(SR.MaxReceivedMessageSizeExceeded, settings.MaxReceivedMessageSize); + Exception inner = new QuotaExceededException(message); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(message, inner)); + } + } + + Message DecodeBufferedMessage(ArraySegment buffer, Stream inputStream) + { + try + { + // if we're chunked, make sure we've consumed the whole body + if (ContentLength == -1 && buffer.Count == settings.MaxReceivedMessageSize) + { + byte[] extraBuffer = new byte[1]; + int extraReceived = inputStream.Read(extraBuffer, 0, 1); + if (extraReceived > 0) + { + ThrowMaxReceivedMessageSizeExceeded(); + } + } + + try + { + return messageEncoder.ReadMessage(buffer, bufferManager, ContentType); + } + catch (XmlException xmlException) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new ProtocolException(SR.MessageXmlProtocolError, xmlException)); + } + } + finally + { + inputStream.Close(); + } + } + + Message ReadBufferedMessage(Stream inputStream) + { + ArraySegment messageBuffer = GetMessageBuffer(); + byte[] buffer = messageBuffer.Array; + int offset = 0; + int count = messageBuffer.Count; + + while (count > 0) + { + int bytesRead = inputStream.Read(buffer, offset, count); + if (bytesRead == 0) // EOF + { + if (ContentLength != -1) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new ProtocolException(SR.HttpContentLengthIncorrect)); + } + + break; + } + count -= bytesRead; + offset += bytesRead; + } + + return DecodeBufferedMessage(new ArraySegment(buffer, 0, offset), inputStream); + } + + Message ReadChunkedBufferedMessage(Stream inputStream) + { + try + { + return messageEncoder.ReadMessage(BufferMessageStream(inputStream, bufferManager, settings.MaxBufferSize), bufferManager, ContentType); + } + catch (XmlException xmlException) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new ProtocolException(SR.MessageXmlProtocolError, xmlException)); + } + } + + Message ReadStreamedMessage(Stream inputStream) + { + MaxMessageSizeStream maxMessageSizeStream = new MaxMessageSizeStream(inputStream, settings.MaxReceivedMessageSize); + + try + { + return messageEncoder.ReadMessage(maxMessageSizeStream, settings.MaxBufferSize, ContentType); + } + catch (XmlException xmlException) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new ProtocolException(SR.MessageXmlProtocolError, xmlException)); + } + } + + // used for buffered streaming + internal ArraySegment BufferMessageStream(Stream stream, BufferManager bufferManager, int maxBufferSize) + { + byte[] buffer = bufferManager.TakeBuffer(ConnectionOrientedTransportDefaults.ConnectionBufferSize); + int offset = 0; + int currentBufferSize = Math.Min(buffer.Length, maxBufferSize); + + while (offset < currentBufferSize) + { + int count = stream.Read(buffer, offset, currentBufferSize - offset); + if (count == 0) + { + stream.Dispose(); + break; + } + + offset += count; + if (offset == currentBufferSize) + { + if (currentBufferSize >= maxBufferSize) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(MaxMessageSizeStream.CreateMaxReceivedMessageSizeExceededException(maxBufferSize)); + } + + currentBufferSize = Math.Min(currentBufferSize * 2, maxBufferSize); + byte[] temp = bufferManager.TakeBuffer(currentBufferSize); + Buffer.BlockCopy(buffer, 0, temp, 0, offset); + bufferManager.ReturnBuffer(buffer); + buffer = temp; + } + } + + return new ArraySegment(buffer, 0, offset); + } + + protected abstract void AddProperties(Message message); + + void ApplyChannelBinding(Message message) + { + if (this.enableChannelBinding) + { + ChannelBindingUtility.TryAddToMessage(this.ChannelBinding, message, true); + } + } + + // makes sure that appropriate HTTP level headers are included in the received Message + Exception ProcessHttpAddressing(Message message) + { + Exception result = null; + AddProperties(message); + + // check if user is receiving WS-1 messages + if (message.Version.Addressing == AddressingVersion.None) + { + bool actionAbsent = false; + try + { + actionAbsent = (message.Headers.Action == null); + } + catch (XmlException e) + { + DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); + } + catch (CommunicationException e) + { + DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); + } + + if (!actionAbsent) + { + result = new ProtocolException(SR.Format(SR.HttpAddressingNoneHeaderOnWire, "Action")); + } + + bool toAbsent = false; + try + { + toAbsent = (message.Headers.To == null); + } + catch (XmlException e) + { + DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); + } + catch (CommunicationException e) + { + DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); + } + + if (!toAbsent) + { + result = new ProtocolException(SR.Format(SR.HttpAddressingNoneHeaderOnWire, "To")); + } + message.Headers.To = message.Properties.Via; + } + + if (isRequest) + { + string action = null; + + if (message.Version.Envelope == EnvelopeVersion.Soap11) + { + action = SoapActionHeader; + } + else if (message.Version.Envelope == EnvelopeVersion.Soap12 && !String.IsNullOrEmpty(ContentType)) + { + ContentType parsedContentType = new ContentType(ContentType); + + if (parsedContentType.MediaType == multipartRelatedMediaType && parsedContentType.Parameters.ContainsKey(startInfoHeaderParam)) + { + // fix to grab action from start-info as stated in RFC2387 + action = new ContentType(parsedContentType.Parameters[startInfoHeaderParam]).Parameters["action"]; + } + if (action == null) + { + // only if we can't find an action inside start-info + action = parsedContentType.Parameters["action"]; + } + } + + if (action != null) + { + action = UrlUtility.UrlDecode(action, Encoding.UTF8); + + if (action.Length >= 2 && action[0] == '"' && action[action.Length - 1] == '"') + { + action = action.Substring(1, action.Length - 2); + } + + if (message.Version.Addressing == AddressingVersion.None) + { + message.Headers.Action = action; + } + + try + { + + if (action.Length > 0 && string.Compare(message.Headers.Action, action, StringComparison.Ordinal) != 0) + { + result = new ActionMismatchAddressingException(SR.Format(SR.HttpSoapActionMismatchFault, + message.Headers.Action, action), message.Headers.Action, action); + } + + } + catch (XmlException e) + { + DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); + } + catch (CommunicationException e) + { + DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); + } + } + } + + ApplyChannelBinding(message); + + return result; + } + + void ValidateContentType() + { + if (!HasContent) + return; + + if (string.IsNullOrEmpty(ContentType)) + { + ThrowHttpProtocolException(SR.HttpContentTypeHeaderRequired, HttpStatusCode.UnsupportedMediaType, HttpChannelUtilities.StatusDescriptionStrings.HttpContentTypeMissing); + } + if (!messageEncoder.IsContentTypeSupported(ContentType)) + { + string statusDescription = string.Format(CultureInfo.InvariantCulture, HttpChannelUtilities.StatusDescriptionStrings.HttpContentTypeMismatch, ContentType, messageEncoder.ContentType); + ThrowHttpProtocolException(SR.Format(SR.ContentTypeMismatch, ContentType, messageEncoder.ContentType), HttpStatusCode.UnsupportedMediaType, statusDescription); + } + } + + public Message ParseIncomingMessage(out Exception requestException) + { + Message message = null; + requestException = null; + bool throwing = true; + try + { + ValidateContentType(); + + if (!this.HasContent) + { + if (this.messageEncoder.MessageVersion == MessageVersion.None) + { + message = new NullMessage(); + } + else + { + return null; + } + } + else + { + Stream stream = this.GetInputStream(true); + if (streamed) + { + message = ReadStreamedMessage(stream); + } + else if (this.ContentLength == -1) + { + message = ReadChunkedBufferedMessage(stream); + } + else + { + message = ReadBufferedMessage(stream); + } + } + + requestException = ProcessHttpAddressing(message); + + throwing = false; + return message; + } + finally + { + if (throwing) + { + Close(); + } + } + } + + void ThrowHttpProtocolException(string message, HttpStatusCode statusCode) + { + ThrowHttpProtocolException(message, statusCode, null); + } + + void ThrowHttpProtocolException(string message, HttpStatusCode statusCode, string statusDescription) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateHttpProtocolException(message, statusCode, statusDescription, webException)); + } + + internal static ProtocolException CreateHttpProtocolException(string message, HttpStatusCode statusCode, string statusDescription, Exception innerException) + { + ProtocolException exception = new ProtocolException(message, innerException); + exception.Data.Add(HttpChannelUtilities.HttpStatusCodeExceptionKey, statusCode); + if (statusDescription != null && statusDescription.Length > 0) + { + exception.Data.Add(HttpChannelUtilities.HttpStatusDescriptionExceptionKey, statusDescription); + } + + return exception; + } + + protected virtual void Close() + { + } + + ArraySegment GetMessageBuffer() + { + long count = ContentLength; + int bufferSize; + + if (count > settings.MaxReceivedMessageSize) + { + ThrowMaxReceivedMessageSizeExceeded(); + } + + bufferSize = (int)count; + + return new ArraySegment(bufferManager.TakeBuffer(bufferSize), 0, bufferSize); + } + } + + // abstract out the common functionality of an "HttpOutput" + abstract class HttpOutput + { + const string DefaultMimeVersion = "1.0"; + + HttpAbortReason abortReason; + bool isDisposed; + bool isRequest; + Message message; + IHttpTransportFactorySettings settings; + byte[] bufferToRecycle; + BufferManager bufferManager; + MessageEncoder messageEncoder; + bool streamed; + static Action onStreamSendTimeout; + string mtomBoundary; + Stream outputStream; + bool supportsConcurrentIO; + bool canSendCompressedResponses; + + protected HttpOutput(IHttpTransportFactorySettings settings, Message message, bool isRequest, bool supportsConcurrentIO) + { + this.settings = settings; + this.message = message; + this.isRequest = isRequest; + this.bufferManager = settings.BufferManager; + this.messageEncoder = settings.MessageEncoderFactory.Encoder; + ICompressedMessageEncoder compressedMessageEncoder = this.messageEncoder as ICompressedMessageEncoder; + this.canSendCompressedResponses = compressedMessageEncoder != null && compressedMessageEncoder.CompressionEnabled; + if (isRequest) + { + this.streamed = TransferModeHelper.IsRequestStreamed(settings.TransferMode); + } + else + { + this.streamed = TransferModeHelper.IsResponseStreamed(settings.TransferMode); + } + this.supportsConcurrentIO = supportsConcurrentIO; + } + + protected virtual bool IsChannelBindingSupportEnabled { get { return false; } } + protected virtual ChannelBinding ChannelBinding { get { return null; } } + + protected void Abort() + { + Abort(HttpAbortReason.Aborted); + } + + public virtual void Abort(HttpAbortReason reason) + { + if (isDisposed) + { + return; + } + + this.abortReason = reason; + + CleanupBuffer(); + } + + public Task CloseAsync() + { + if (isDisposed) + { + return Task.CompletedTask; + } + + try + { + if (outputStream != null) + { + outputStream.Close(); + } + } + finally + { + CleanupBuffer(); + } + + return Task.CompletedTask; + } + + void CleanupBuffer() + { + byte[] bufferToRecycleSnapshot = Interlocked.Exchange(ref this.bufferToRecycle, null); + if (bufferToRecycleSnapshot != null) + { + bufferManager.ReturnBuffer(bufferToRecycleSnapshot); + } + + isDisposed = true; + } + + protected abstract void AddMimeVersion(string version); + protected abstract void AddHeader(string name, string value); + protected abstract void SetContentType(string contentType); + protected abstract void SetContentEncoding(string contentEncoding); + protected abstract void SetStatusCode(HttpStatusCode statusCode); + protected abstract void SetStatusDescription(string statusDescription); + protected virtual bool CleanupChannelBinding { get { return true; } } + protected virtual void SetContentLength(int contentLength) + { + } + + protected virtual string HttpMethod { get { return null; } } + + public virtual ChannelBinding TakeChannelBinding() + { + return null; + } + + private void ApplyChannelBinding() + { + if (this.IsChannelBindingSupportEnabled) + { + ChannelBindingUtility.TryAddToMessage(this.ChannelBinding, this.message, this.CleanupChannelBinding); + } + } + + protected abstract Stream GetOutputStream(); + + protected virtual bool WillGetOutputStreamCompleteSynchronously + { + get { return true; } + } + + protected bool CanSendCompressedResponses + { + get { return this.canSendCompressedResponses; } + } + + protected virtual bool PrepareHttpSend(Message message) + { + string action = message.Headers.Action; + + if (message.Version.Addressing == AddressingVersion.None) + { + message.Headers.Action = null; + message.Headers.To = null; + } + + string contentType = null; + + if (message.Version == MessageVersion.None) + { + object property = null; + if (message.Properties.TryGetValue(HttpResponseMessageProperty.Name, out property)) + { + HttpResponseMessageProperty responseProperty = (HttpResponseMessageProperty)property; + if (!string.IsNullOrEmpty(responseProperty.Headers[HttpResponseHeader.ContentType])) + { + contentType = responseProperty.Headers[HttpResponseHeader.ContentType]; + if (!messageEncoder.IsContentTypeSupported(contentType)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new ProtocolException(SR.Format(SR.ResponseContentTypeNotSupported, + contentType))); + } + } + } + } + + if (string.IsNullOrEmpty(contentType)) + { + //MtomMessageEncoder mtomMessageEncoder = messageEncoder as MtomMessageEncoder; + //if (mtomMessageEncoder == null) + //{ + contentType = messageEncoder.ContentType; + //} + //else + //{ + // contentType = mtomMessageEncoder.GetContentType(out this.mtomBoundary); + // // For MTOM messages, add a MIME version header + // AddMimeVersion("1.0"); + //} + } + + SetContentType(contentType); + return message is NullMessage; + } + + ArraySegment SerializeBufferedMessage(Message message) + { + // by default, the HttpOutput should own the buffer and clean it up + return SerializeBufferedMessage(message, true); + } + + ArraySegment SerializeBufferedMessage(Message message, bool shouldRecycleBuffer) + { + ArraySegment result; + + //MtomMessageEncoder mtomMessageEncoder = messageEncoder as MtomMessageEncoder; + //if (mtomMessageEncoder == null) + //{ + result = messageEncoder.WriteMessage(message, int.MaxValue, bufferManager); + //} + //else + //{ + // result = mtomMessageEncoder.WriteMessage(message, int.MaxValue, bufferManager, 0, this.mtomBoundary); + //} + + if (shouldRecycleBuffer) + { + // Only set this.bufferToRecycle if the HttpOutput owns the buffer, we will clean it up upon httpOutput.Close() + // Otherwise, caller of SerializeBufferedMessage assumes responsiblity for returning the buffer to the buffer pool + this.bufferToRecycle = result.Array; + } + return result; + } + + Stream GetWrappedOutputStream() + { + const int ChunkSize = 32768; // buffer size used for synchronous writes + // const int BufferSize = 16384; // buffer size used for asynchronous writes + // const int BufferCount = 4; // buffer count used for asynchronous writes + + // Writing an HTTP request chunk has a high fixed cost, so use BufferedStream to avoid writing + // small ones. + // TODO: Evaluate whether we need to buffer the output stream and if concurrent io is supported + //return supportsConcurrentIO ? (Stream)new BufferedOutputAsyncStream(outputStream, BufferSize, BufferCount) : new BufferedStream(outputStream, ChunkSize); + //return this.supportsConcurrentIO ? (Stream)new BufferedOutputAsyncStream(this.outputStream, BufferSize, BufferCount) : new BufferedStream(this.outputStream, ChunkSize); + return new BufferedStream(outputStream, ChunkSize); + } + + private async Task WriteStreamedMessageAsync(CancellationToken token) + { + this.outputStream = GetWrappedOutputStream(); + + // Since HTTP streams don't support timeouts, we can't just use TimeoutStream here. + // Rather, we need to run a timer to bound the overall operation + if (onStreamSendTimeout == null) + { + onStreamSendTimeout = OnStreamSendTimeout; + } + + // TODO: Verify that the cancellation token is honored for timeout + //IOThreadTimer sendTimer = new IOThreadTimer(onStreamSendTimeout, this, true); + //sendTimer.Set(timeout); + + try + { + //MtomMessageEncoder mtomMessageEncoder = messageEncoder as MtomMessageEncoder; + //if (mtomMessageEncoder == null) + //{ + await messageEncoder.WriteMessageAsync(this.message, this.outputStream); + //} + //else + //{ + // mtomMessageEncoder.WriteMessage(this.message, this.outputStream, this.mtomBoundary); + //} + + //if (this.supportsConcurrentIO) + //{ + // this.outputStream.Close(); + //} + } + finally + { + //sendTimer.Cancel(); + } + } + + static void OnStreamSendTimeout(object state) + { + HttpOutput thisPtr = (HttpOutput)state; + thisPtr.Abort(HttpAbortReason.TimedOut); + } + + public async Task SendAsync(CancellationToken token) + { + bool suppressEntityBody = PrepareHttpSend(message); + + if (suppressEntityBody) + { + // requests can't always support an output stream (for GET, etc) + if (!isRequest) + { + outputStream = GetOutputStream(); + } + else + { + this.SetContentLength(0); + } + } + else if (streamed) + { + outputStream = GetOutputStream(); + ApplyChannelBinding(); + await WriteStreamedMessageAsync(token); + } + else + { + if (this.IsChannelBindingSupportEnabled) + { + //need to get the Channel binding token (CBT), apply channel binding info to the message and then write the message + //CBT is only enabled when message security is in the stack, which also requires an HTTP entity body, so we + //should be safe to always get the stream. + outputStream = GetOutputStream(); + + ApplyChannelBinding(); + + ArraySegment buffer = SerializeBufferedMessage(message); + + Fx.Assert(buffer.Count != 0, "We should always have an entity body in this case..."); + await outputStream.WriteAsync(buffer.Array, buffer.Offset, buffer.Count); + } + else + { + ArraySegment buffer = SerializeBufferedMessage(message); + SetContentLength(buffer.Count); + + // requests can't always support an output stream (for GET, etc) + if (!isRequest || buffer.Count > 0) + { + outputStream = GetOutputStream(); + await outputStream.WriteAsync(buffer.Array, buffer.Offset, buffer.Count); + } + } + } + } + + internal static HttpOutput CreateHttpOutput(HttpContext httpContext, IHttpTransportFactorySettings settings, Message message, string httpMethod) + { + return new AspNetCoreHttpOutput(httpContext, settings, message, httpMethod); + } + + class AspNetCoreHttpOutput : HttpOutput + { + private HttpResponse httpResponse; + private HttpContext httpContext; + private string httpMethod; + + public AspNetCoreHttpOutput(HttpContext httpContext, IHttpTransportFactorySettings settings, Message message, string httpMethod) + : base(settings, message, false, true) + { + this.httpResponse = httpContext.Response; + this.httpContext = httpContext; + this.httpMethod = httpMethod; + + if (message.IsFault) + { + this.SetStatusCode(HttpStatusCode.InternalServerError); + } + else + { + this.SetStatusCode(HttpStatusCode.OK); + } + } + + protected override string HttpMethod => httpMethod; + + public override void Abort(HttpAbortReason abortReason) + { + httpContext.Abort(); + base.Abort(abortReason); + } + + protected override void AddMimeVersion(string version) + { + httpResponse.Headers[HttpChannelUtilities.MIMEVersionHeader] = version; + } + + protected override bool PrepareHttpSend(Message message) + { + bool result = base.PrepareHttpSend(message); + + if (this.CanSendCompressedResponses) + { + string contentType = httpResponse.ContentType; + string contentEncoding; + if (HttpChannelUtilities.GetHttpResponseTypeAndEncodingForCompression(ref contentType, out contentEncoding)) + { + if (contentType != this.httpResponse.ContentType) + { + this.SetContentType(contentType); + } + this.SetContentEncoding(contentEncoding); + } + } + + message.Properties.TryGetValue(HttpResponseMessageProperty.Name, out object responsePropertyObj); + HttpResponseMessageProperty responseProperty = (HttpResponseMessageProperty)responsePropertyObj; + bool httpResponseMessagePropertyFound = responseProperty != null; + bool httpMethodIsHead = string.Compare(httpMethod, "HEAD", StringComparison.OrdinalIgnoreCase) == 0; + + if (httpMethodIsHead || + httpResponseMessagePropertyFound && responseProperty.SuppressEntityBody) + { + result = true; + SetContentLength(0); + SetContentType(null); + // TODO: Validate what to do about httpResponse.SendChunked property + //httpResponse.SendChunked = false; + } + + if (httpResponseMessagePropertyFound) + { + this.SetStatusCode(responseProperty.StatusCode); + if (responseProperty.StatusDescription != null) + { + this.SetStatusDescription(responseProperty.StatusDescription); + } + + WebHeaderCollection responseHeaders = responseProperty.Headers; + for (int i = 0; i < responseHeaders.Count; i++) + { + string name = responseHeaders.Keys[i]; + string value = responseHeaders[i]; + if (string.Compare(name, "content-length", StringComparison.OrdinalIgnoreCase) == 0) + { + int contentLength = -1; + if (httpMethodIsHead && + int.TryParse(value, out contentLength)) + { + this.SetContentLength(contentLength); + } + } + else if (string.Compare(name, "content-type", StringComparison.OrdinalIgnoreCase) == 0) + { + if (httpMethodIsHead || + !responseProperty.SuppressEntityBody) + { + this.SetContentType(value); + } + } + else + { + this.AddHeader(name, value); + } + } + } + + return result; + } + + protected override void AddHeader(string name, string value) + { + if (string.Compare(name, "WWW-Authenticate", StringComparison.OrdinalIgnoreCase) == 0) + { + httpResponse.Headers[name] = value; + } + else + { + if(httpResponse.Headers.ContainsKey(name)) + { + var previousValue = httpResponse.Headers[name]; + httpResponse.Headers[name] = previousValue + ", " + value; + } + else + { + httpResponse.Headers[name] = value; + } + } + } + + protected override void SetContentType(string contentType) + { + httpResponse.ContentType = contentType; + } + + protected override void SetContentEncoding(string contentEncoding) + { + httpResponse.Headers[HttpChannelUtilities.ContentEncodingHeader] = contentEncoding; + } + + protected override void SetContentLength(int contentLength) + { + httpResponse.ContentLength = contentLength; + } + + protected override void SetStatusCode(HttpStatusCode statusCode) + { + httpResponse.StatusCode = (int)statusCode; + } + + protected override void SetStatusDescription(string statusDescription) + { + httpResponse.HttpContext.Features.Get().ReasonPhrase = statusDescription; + } + + protected override Stream GetOutputStream() + { + return httpResponse.Body; + } + } + } + + enum HttpAbortReason + { + None, + Aborted, + TimedOut + } + + + static class HttpChannelUtilities + { + internal static class StatusDescriptionStrings + { + internal const string HttpContentTypeMissing = "Missing Content Type"; + internal const string HttpContentTypeMismatch = "Cannot process the message because the content type '{0}' was not the expected type '{1}'."; + } + + internal const string HttpStatusCodeKey = "HttpStatusCode"; + internal const string HttpStatusCodeExceptionKey = "CoreWCF.Channels.HttpInput.HttpStatusCode"; + internal const string HttpStatusDescriptionExceptionKey = "CoreWCF.Channels.HttpInput.HttpStatusDescription"; + + internal const string MIMEVersionHeader = "MIME-Version"; + + internal const string ContentEncodingHeader = "Content-Encoding"; + internal const string AcceptEncodingHeader = "Accept-Encoding"; + + public static Exception CreateCommunicationException(Exception exception) + { + return new CommunicationException(exception.Message, exception); + } + + // public static void EnsureHttpRequestMessageContentNotNull(HttpRequestMessage httpRequestMessage) + // { + // if (httpRequestMessage.Content == null) + // { + // httpRequestMessage.Content = new ByteArrayContent(EmptyArray.Instance); + // } + // } + + // public static void EnsureHttpResponseMessageContentNotNull(HttpResponseMessage httpResponseMessage) + // { + // if (httpResponseMessage.Content == null) + // { + // httpResponseMessage.Content = new ByteArrayContent(EmptyArray.Instance); + // } + // } + + // public static bool IsEmpty(HttpResponseMessage httpResponseMessage) + // { + // return httpResponseMessage.Content == null + // || (httpResponseMessage.Content.Headers.ContentLength.HasValue && httpResponseMessage.Content.Headers.ContentLength.Value == 0); + // } + + // internal static void HandleContinueWithTask(Task task) + // { + // HandleContinueWithTask(task, null); + // } + + // internal static void HandleContinueWithTask(Task task, Action exceptionHandler) + // { + // if (task.IsFaulted) + // { + // if (exceptionHandler == null) + // { + // throw FxTrace.Exception.AsError(task.Exception); + // } + // else + // { + // exceptionHandler.Invoke(task.Exception); + // } + // } + // else if (task.IsCanceled) + // { + // throw FxTrace.Exception.AsError(new TimeoutException(SR.GetString(SR.TaskCancelledError))); + // } + // } + + // public static void AbortRequest(HttpWebRequest request) + // { + // request.Abort(); + // } + + // public static void SetRequestTimeout(HttpWebRequest request, TimeSpan timeout) + // { + // int millisecondsTimeout = TimeoutHelper.ToMilliseconds(timeout); + // if (millisecondsTimeout == 0) + // { + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new TimeoutException(SR.GetString( + // SR.HttpRequestTimedOut, request.RequestUri, timeout))); + // } + // request.Timeout = millisecondsTimeout; + // request.ReadWriteTimeout = millisecondsTimeout; + // } + + // public static void AddReplySecurityProperty(HttpChannelFactory factory, HttpWebRequest webRequest, + // HttpWebResponse webResponse, Message replyMessage) + // { + // SecurityMessageProperty securityProperty = factory.CreateReplySecurityProperty(webRequest, webResponse); + // if (securityProperty != null) + // { + // replyMessage.Properties.Security = securityProperty; + // } + // } + + // public static void CopyHeaders(HttpRequestMessage request, AddHeaderDelegate addHeader) + // { + // HttpChannelUtilities.CopyHeaders(request.Headers, addHeader); + // if (request.Content != null) + // { + // HttpChannelUtilities.CopyHeaders(request.Content.Headers, addHeader); + // } + // } + + // public static void CopyHeaders(HttpResponseMessage response, AddHeaderDelegate addHeader) + // { + // HttpChannelUtilities.CopyHeaders(response.Headers, addHeader); + // if (response.Content != null) + // { + // HttpChannelUtilities.CopyHeaders(response.Content.Headers, addHeader); + // } + // } + + // static void CopyHeaders(HttpHeaders headers, AddHeaderDelegate addHeader) + // { + // foreach (KeyValuePair> header in headers) + // { + // foreach (string value in header.Value) + // { + // TryAddToCollection(addHeader, header.Key, value); + // } + // } + // } + + // public static void CopyHeaders(NameValueCollection headers, AddHeaderDelegate addHeader) + // { + // //this nested loop logic was copied from NameValueCollection.Add(NameValueCollection) + // int count = headers.Count; + // for (int i = 0; i < count; i++) + // { + // string key = headers.GetKey(i); + + // string[] values = headers.GetValues(i); + // if (values != null) + // { + // for (int j = 0; j < values.Length; j++) + // { + // TryAddToCollection(addHeader, key, values[j]); + // } + // } + // else + // { + // addHeader(key, null); + // } + // } + // } + + // public static void CopyHeadersToNameValueCollection(NameValueCollection headers, NameValueCollection destination) + // { + // CopyHeaders(headers, destination.Add); + // } + + // [System.Diagnostics.CodeAnalysis.SuppressMessage(FxCop.Category.ReliabilityBasic, "Reliability104", + // Justification = "The exceptions are traced already.")] + // static void TryAddToCollection(AddHeaderDelegate addHeader, string headerName, string value) + // { + // try + // { + // addHeader(headerName, value); + // } + // catch (ArgumentException ex) + // { + // string encodedValue = null; + // if (TryEncodeHeaderValueAsUri(headerName, value, out encodedValue)) + // { + // //note: if the hosthame of a referer header contains illegal chars, we will still throw from here + // //because Uri will not fix this up for us, which is ok. The request will get rejected in the error code path. + // addHeader(headerName, encodedValue); + // } + // else + // { + // // In self-hosted scenarios, some of the headers like Content-Length cannot be added directly. + // // It will throw ArgumentException instead. + // FxTrace.Exception.AsInformation(ex); + // } + // } + // } + + // static bool TryEncodeHeaderValueAsUri(string headerName, string value, out string result) + // { + // result = null; + // //Internet Explorer will send the referrer header on the wire in unicode without encoding it + // //this will cause errors when added to a WebHeaderCollection. This is a workaround for sharepoint, + // //but will only work for WebHosted Scenarios. + // if (String.Compare(headerName, "Referer", StringComparison.OrdinalIgnoreCase) == 0) + // { + // Uri uri; + // if (Uri.TryCreate(value, UriKind.RelativeOrAbsolute, out uri)) + // { + // if (uri.IsAbsoluteUri) + // { + // result = uri.AbsoluteUri; + // } + // else + // { + // result = uri.GetComponents(UriComponents.SerializationInfoString, UriFormat.UriEscaped); + // } + // return true; + // } + // } + // return false; + // } + + // //TODO, weixi, CSDMain 231775: Refactor the code for GetType logic in System.ServiceModel.dll and System.ServiceModel.Activation.dll + // internal static Type GetTypeFromAssembliesInCurrentDomain(string typeString) + // { + // Type type = Type.GetType(typeString, false); + // if (null == type) + // { + // if (!allReferencedAssembliesLoaded) + // { + // allReferencedAssembliesLoaded = true; + // AspNetEnvironment.Current.EnsureAllReferencedAssemblyLoaded(); + // } + + // Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); + // for (int i = 0; i < assemblies.Length; i++) + // { + // type = assemblies[i].GetType(typeString, false); + // if (null != type) + // { + // break; + // } + // } + // } + + // return type; + // } + + // public static NetworkCredential GetCredential(AuthenticationSchemes authenticationScheme, + // SecurityTokenProviderContainer credentialProvider, TimeSpan timeout, + // out TokenImpersonationLevel impersonationLevel, out AuthenticationLevel authenticationLevel) + // { + // impersonationLevel = TokenImpersonationLevel.None; + // authenticationLevel = AuthenticationLevel.None; + + // NetworkCredential result = null; + + // if (authenticationScheme != AuthenticationSchemes.Anonymous) + // { + // result = GetCredentialCore(authenticationScheme, credentialProvider, timeout, out impersonationLevel, out authenticationLevel); + // } + + // return result; + // } + + // [MethodImpl(MethodImplOptions.NoInlining)] + // static NetworkCredential GetCredentialCore(AuthenticationSchemes authenticationScheme, + // SecurityTokenProviderContainer credentialProvider, TimeSpan timeout, + // out TokenImpersonationLevel impersonationLevel, out AuthenticationLevel authenticationLevel) + // { + // impersonationLevel = TokenImpersonationLevel.None; + // authenticationLevel = AuthenticationLevel.None; + + // NetworkCredential result = null; + + // switch (authenticationScheme) + // { + // case AuthenticationSchemes.Basic: + // result = TransportSecurityHelpers.GetUserNameCredential(credentialProvider, timeout); + // impersonationLevel = TokenImpersonationLevel.Delegation; + // break; + + // case AuthenticationSchemes.Digest: + // result = TransportSecurityHelpers.GetSspiCredential(credentialProvider, timeout, + // out impersonationLevel, out authenticationLevel); + + // HttpChannelUtilities.ValidateDigestCredential(ref result, impersonationLevel); + // break; + + // case AuthenticationSchemes.Negotiate: + // result = TransportSecurityHelpers.GetSspiCredential(credentialProvider, timeout, + // out impersonationLevel, out authenticationLevel); + // break; + + // case AuthenticationSchemes.Ntlm: + // result = TransportSecurityHelpers.GetSspiCredential(credentialProvider, timeout, + // out impersonationLevel, out authenticationLevel); + // if (authenticationLevel == AuthenticationLevel.MutualAuthRequired) + // { + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + // new InvalidOperationException(SR.GetString(SR.CredentialDisallowsNtlm))); + // } + // break; + + // default: + // // The setter for this property should prevent this. + // throw Fx.AssertAndThrow("GetCredential: Invalid authentication scheme"); + // } + + // return result; + // } + + + // public static HttpWebResponse ProcessGetResponseWebException(WebException webException, HttpWebRequest request, HttpAbortReason abortReason) + // { + // HttpWebResponse response = null; + + // if (webException.Status == WebExceptionStatus.Success || + // webException.Status == WebExceptionStatus.ProtocolError) + // { + // response = (HttpWebResponse)webException.Response; + // } + + // if (response == null) + // { + // Exception convertedException = ConvertWebException(webException, request, abortReason); + + // if (convertedException != null) + // { + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(convertedException); + // } + + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(webException.Message, + // webException)); + // } + + // if (response.StatusCode == HttpStatusCode.NotFound) + // { + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new EndpointNotFoundException(SR.GetString(SR.EndpointNotFound, request.RequestUri.AbsoluteUri), webException)); + // } + + // if (response.StatusCode == HttpStatusCode.ServiceUnavailable) + // { + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ServerTooBusyException(SR.GetString(SR.HttpServerTooBusy, request.RequestUri.AbsoluteUri), webException)); + // } + + // if (response.StatusCode == HttpStatusCode.UnsupportedMediaType) + // { + // string statusDescription = response.StatusDescription; + // if (!string.IsNullOrEmpty(statusDescription)) + // { + // if (string.Compare(statusDescription, HttpChannelUtilities.StatusDescriptionStrings.HttpContentTypeMissing, StringComparison.OrdinalIgnoreCase) == 0) + // { + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ProtocolException(SR.GetString(SR.MissingContentType, request.RequestUri), webException)); + // } + // } + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ProtocolException(SR.GetString(SR.FramingContentTypeMismatch, request.ContentType, request.RequestUri), webException)); + // } + + // if (response.StatusCode == HttpStatusCode.GatewayTimeout) + // { + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new TimeoutException(webException.Message, webException)); + // } + + // // if http.sys has a request queue on the TCP port, then if the path fails to match it will send + // // back "

Bad Request (Invalid Hostname)

" in the body of a 400 response. + // // See code at \\index1\sddnsrv\net\http\sys\httprcv.c for details + // if (response.StatusCode == HttpStatusCode.BadRequest) + // { + // const string httpSysRequestQueueNotFound = "

Bad Request (Invalid Hostname)

"; + // const string httpSysRequestQueueNotFoundVista = "\r\nBad Request\r\n\r\n

Bad Request - Invalid Hostname

\r\n

HTTP Error 400. The request hostname is invalid.

\r\n\r\n"; + // string notFoundTestString = null; + + // if (response.ContentLength == httpSysRequestQueueNotFound.Length) + // { + // notFoundTestString = httpSysRequestQueueNotFound; + // } + // else if (response.ContentLength == httpSysRequestQueueNotFoundVista.Length) + // { + // notFoundTestString = httpSysRequestQueueNotFoundVista; + // } + + // if (notFoundTestString != null) + // { + // Stream responseStream = response.GetResponseStream(); + // byte[] responseBytes = new byte[notFoundTestString.Length]; + // int bytesRead = responseStream.Read(responseBytes, 0, responseBytes.Length); + + // // since the response is buffered by System.Net (it's an error response), we should have read + // // the amount we were expecting + // if (bytesRead == notFoundTestString.Length + // && notFoundTestString == UTF8Encoding.ASCII.GetString(responseBytes)) + // { + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new EndpointNotFoundException(SR.GetString(SR.EndpointNotFound, request.RequestUri.AbsoluteUri), webException)); + // } + // } + // } + + // return response; + // } + + // public static Exception ConvertWebException(WebException webException, HttpWebRequest request, HttpAbortReason abortReason) + // { + // switch (webException.Status) + // { + // case WebExceptionStatus.ConnectFailure: + // case WebExceptionStatus.NameResolutionFailure: + // case WebExceptionStatus.ProxyNameResolutionFailure: + // return new EndpointNotFoundException(SR.GetString(SR.EndpointNotFound, request.RequestUri.AbsoluteUri), webException); + // case WebExceptionStatus.SecureChannelFailure: + // return new SecurityNegotiationException(SR.GetString(SR.SecureChannelFailure, request.RequestUri.Authority), webException); + // case WebExceptionStatus.TrustFailure: + // return new SecurityNegotiationException(SR.GetString(SR.TrustFailure, request.RequestUri.Authority), webException); + // case WebExceptionStatus.Timeout: + // return new TimeoutException(CreateRequestTimedOutMessage(request), webException); + // case WebExceptionStatus.ReceiveFailure: + // return new CommunicationException(SR.GetString(SR.HttpReceiveFailure, request.RequestUri), webException); + // case WebExceptionStatus.SendFailure: + // return new CommunicationException(SR.GetString(SR.HttpSendFailure, request.RequestUri), webException); + // case WebExceptionStatus.RequestCanceled: + // return CreateRequestCanceledException(webException, request, abortReason); + // case WebExceptionStatus.ProtocolError: + // HttpWebResponse response = (HttpWebResponse)webException.Response; + // Fx.Assert(response != null, "'response' MUST NOT be NULL for WebExceptionStatus=='ProtocolError'."); + // if (response.StatusCode == HttpStatusCode.InternalServerError && + // string.Compare(response.StatusDescription, HttpChannelUtilities.StatusDescriptionStrings.HttpStatusServiceActivationException, StringComparison.OrdinalIgnoreCase) == 0) + // { + // return new ServiceActivationException(SR.GetString(SR.Hosting_ServiceActivationFailed, request.RequestUri)); + // } + // else + // { + // return null; + // } + // default: + // return null; + // } + // } + + // public static Exception CreateResponseIOException(IOException ioException, TimeSpan receiveTimeout) + // { + // if (ioException.InnerException is SocketException) + // { + // return SocketConnection.ConvertTransferException((SocketException)ioException.InnerException, receiveTimeout, ioException); + // } + + // return new CommunicationException(SR.GetString(SR.HttpTransferError, ioException.Message), ioException); + // } + + // public static Exception CreateResponseWebException(WebException webException, HttpWebResponse response) + // { + // switch (webException.Status) + // { + // case WebExceptionStatus.RequestCanceled: + // return TraceResponseException(new CommunicationObjectAbortedException(SR.GetString(SR.HttpRequestAborted, response.ResponseUri), webException)); + // case WebExceptionStatus.ConnectionClosed: + // return TraceResponseException(new CommunicationException(webException.Message, webException)); + // case WebExceptionStatus.Timeout: + // return TraceResponseException(new TimeoutException(SR.GetString(SR.HttpResponseTimedOut, response.ResponseUri, + // TimeSpan.FromMilliseconds(response.GetResponseStream().ReadTimeout)), webException)); + // default: + // return CreateUnexpectedResponseException(webException, response); + // } + // } + + // public static Exception CreateRequestCanceledException(Exception webException, HttpWebRequest request, HttpAbortReason abortReason) + // { + // switch (abortReason) + // { + // case HttpAbortReason.Aborted: + // return new CommunicationObjectAbortedException(SR.GetString(SR.HttpRequestAborted, request.RequestUri), webException); + // case HttpAbortReason.TimedOut: + // return new TimeoutException(CreateRequestTimedOutMessage(request), webException); + // default: + // return new CommunicationException(SR.GetString(SR.HttpTransferError, webException.Message), webException); + // } + // } + + // public static Exception CreateRequestIOException(IOException ioException, HttpWebRequest request) + // { + // return CreateRequestIOException(ioException, request, null); + // } + + // public static Exception CreateRequestIOException(IOException ioException, HttpWebRequest request, Exception originalException) + // { + // Exception exception = originalException == null ? ioException : originalException; + + // if (ioException.InnerException is SocketException) + // { + // return SocketConnection.ConvertTransferException((SocketException)ioException.InnerException, TimeSpan.FromMilliseconds(request.Timeout), exception); + // } + + // return new CommunicationException(SR.GetString(SR.HttpTransferError, exception.Message), exception); + // } + + // static string CreateRequestTimedOutMessage(HttpWebRequest request) + // { + // return SR.GetString(SR.HttpRequestTimedOut, request.RequestUri, TimeSpan.FromMilliseconds(request.Timeout)); + // } + + // public static Exception CreateRequestWebException(WebException webException, HttpWebRequest request, HttpAbortReason abortReason) + // { + // Exception convertedException = ConvertWebException(webException, request, abortReason); + + // if (webException.Response != null) + // { + // //free the connection for use by another request + // webException.Response.Close(); + // } + + // if (convertedException != null) + // { + // return convertedException; + // } + + // if (webException.InnerException is IOException) + // { + // return CreateRequestIOException((IOException)webException.InnerException, request, webException); + // } + + // if (webException.InnerException is SocketException) + // { + // return SocketConnectionInitiator.ConvertConnectException((SocketException)webException.InnerException, request.RequestUri, TimeSpan.MaxValue, webException); + // } + + // return new EndpointNotFoundException(SR.GetString(SR.EndpointNotFound, request.RequestUri.AbsoluteUri), webException); + // } + + // static Exception CreateUnexpectedResponseException(WebException responseException, HttpWebResponse response) + // { + // string statusDescription = response.StatusDescription; + // if (string.IsNullOrEmpty(statusDescription)) + // statusDescription = response.StatusCode.ToString(); + + // return TraceResponseException( + // new ProtocolException(SR.GetString(SR.UnexpectedHttpResponseCode, + // (int)response.StatusCode, statusDescription), responseException)); + // } + + // public static Exception CreateNullReferenceResponseException(NullReferenceException nullReferenceException) + // { + // return TraceResponseException( + // new ProtocolException(SR.GetString(SR.NullReferenceOnHttpResponse), nullReferenceException)); + // } + + // static string GetResponseStreamString(HttpWebResponse webResponse, out int bytesRead) + // { + // Stream responseStream = webResponse.GetResponseStream(); + + // long bufferSize = webResponse.ContentLength; + + // if (bufferSize < 0 || bufferSize > ResponseStreamExcerptSize) + // { + // bufferSize = ResponseStreamExcerptSize; + // } + + // byte[] responseBuffer = DiagnosticUtility.Utility.AllocateByteArray(checked((int)bufferSize)); + // bytesRead = responseStream.Read(responseBuffer, 0, (int)bufferSize); + // responseStream.Close(); + + // return System.Text.Encoding.UTF8.GetString(responseBuffer, 0, bytesRead); + // } + + // static Exception TraceResponseException(Exception exception) + // { + // if (DiagnosticUtility.ShouldTraceError) + // { + // TraceUtility.TraceEvent(TraceEventType.Error, TraceCode.HttpChannelUnexpectedResponse, SR.GetString(SR.TraceCodeHttpChannelUnexpectedResponse), (object)null, exception); + // } + + // return exception; + // } + + // static bool ValidateEmptyContent(HttpWebResponse response) + // { + // bool responseIsEmpty = true; + + // if (response.ContentLength > 0) + // { + // responseIsEmpty = false; + // } + // else if (response.ContentLength == -1) // chunked + // { + // Stream responseStream = response.GetResponseStream(); + // byte[] testBuffer = new byte[1]; + // responseIsEmpty = (responseStream.Read(testBuffer, 0, 1) != 1); + // } + + // return responseIsEmpty; + // } + + // static void ValidateAuthentication(HttpWebRequest request, HttpWebResponse response, + // WebException responseException, HttpChannelFactory factory) + // { + // if (response.StatusCode == HttpStatusCode.Unauthorized) + // { + // string message = SR.GetString(SR.HttpAuthorizationFailed, factory.AuthenticationScheme, + // response.Headers[HttpResponseHeader.WwwAuthenticate]); + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + // TraceResponseException(new MessageSecurityException(message, responseException))); + // } + + // if (response.StatusCode == HttpStatusCode.Forbidden) + // { + // string message = SR.GetString(SR.HttpAuthorizationForbidden, factory.AuthenticationScheme); + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + // TraceResponseException(new MessageSecurityException(message, responseException))); + // } + + // if ((request.AuthenticationLevel == AuthenticationLevel.MutualAuthRequired) && + // !response.IsMutuallyAuthenticated) + // { + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + // TraceResponseException(new SecurityNegotiationException(SR.GetString(SR.HttpMutualAuthNotSatisfied), + // responseException))); + // } + // } + + // public static void ValidateDigestCredential(ref NetworkCredential credential, TokenImpersonationLevel impersonationLevel) + // { + // // this is a work-around to VSWhidbey#470545 (Since the service always uses Impersonation, + // // we mitigate EOP by preemtively not allowing Identification) + // if (!SecurityUtils.IsDefaultNetworkCredential(credential)) + // { + // // With a non-default credential, Digest will not honor a client impersonation constraint of + // // TokenImpersonationLevel.Identification. + // if (!TokenImpersonationLevelHelper.IsGreaterOrEqual(impersonationLevel, + // TokenImpersonationLevel.Impersonation)) + // { + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString( + // SR.DigestExplicitCredsImpersonationLevel, impersonationLevel))); + // } + // } + // } + + // // only valid response codes are 500 (if it's a fault) or 200 (iff it's a response message) + // public static HttpInput ValidateRequestReplyResponse(HttpWebRequest request, HttpWebResponse response, + // HttpChannelFactory factory, WebException responseException, ChannelBinding channelBinding) + // { + // ValidateAuthentication(request, response, responseException, factory); + + // HttpInput httpInput = null; + + // // We will close the HttpWebResponse if we got an error code betwen 200 and 300 and + // // 1) an exception was thrown out or + // // 2) it's an empty message and we are using SOAP. + // // For responses with status code above 300, System.Net will close the underlying connection so we don't need to worry about that. + // if ((200 <= (int)response.StatusCode && (int)response.StatusCode < 300) || response.StatusCode == HttpStatusCode.InternalServerError) + // { + // if (response.StatusCode == HttpStatusCode.InternalServerError + // && string.Compare(response.StatusDescription, HttpChannelUtilities.StatusDescriptionStrings.HttpStatusServiceActivationException, StringComparison.OrdinalIgnoreCase) == 0) + // { + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ServiceActivationException(SR.GetString(SR.Hosting_ServiceActivationFailed, request.RequestUri))); + // } + // else + // { + // bool throwing = true; + // try + // { + // if (string.IsNullOrEmpty(response.ContentType)) + // { + // if (!ValidateEmptyContent(response)) + // { + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(TraceResponseException( + // new ProtocolException( + // SR.GetString(SR.HttpContentTypeHeaderRequired), + // responseException))); + // } + // } + // else if (response.ContentLength != 0) + // { + // MessageEncoder encoder = factory.MessageEncoderFactory.Encoder; + // if (!encoder.IsContentTypeSupported(response.ContentType)) + // { + // int bytesRead; + // String responseExcerpt = GetResponseStreamString(response, out bytesRead); + + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(TraceResponseException( + // new ProtocolException( + // SR.GetString( + // SR.ResponseContentTypeMismatch, + // response.ContentType, + // encoder.ContentType, + // bytesRead, + // responseExcerpt), responseException))); + + // } + + // httpInput = HttpInput.CreateHttpInput(response, factory, channelBinding); + // httpInput.WebException = responseException; + // } + + // throwing = false; + // } + // finally + // { + // if (throwing) + // { + // response.Close(); + // } + // } + // } + + // if (httpInput == null) + // { + // if (factory.MessageEncoderFactory.MessageVersion == MessageVersion.None) + // { + // httpInput = HttpInput.CreateHttpInput(response, factory, channelBinding); + // httpInput.WebException = responseException; + // } + // else + // { + // // In this case, we got a response with + // // 1) status code between 200 and 300 + // // 2) Non-empty Content Type string + // // 3) Zero content length + // // Since we are trying to use SOAP here, the message seems to be malicious and we should + // // just close the response directly. + // response.Close(); + // } + // } + // } + // else + // { + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateUnexpectedResponseException(responseException, response)); + // } + + // return httpInput; + // } + + public static bool GetHttpResponseTypeAndEncodingForCompression(ref string contentType, out string contentEncoding) + { + contentEncoding = null; + bool isSession = false; + bool isDeflate = false; + + if (string.Equals(BinaryVersion.GZipVersion1.ContentType, contentType, StringComparison.OrdinalIgnoreCase) || + (isSession = string.Equals(BinaryVersion.GZipVersion1.SessionContentType, contentType, StringComparison.OrdinalIgnoreCase)) || + (isDeflate = (string.Equals(BinaryVersion.DeflateVersion1.ContentType, contentType, StringComparison.OrdinalIgnoreCase) || + (isSession = string.Equals(BinaryVersion.DeflateVersion1.SessionContentType, contentType, StringComparison.OrdinalIgnoreCase))))) + { + contentType = isSession ? BinaryVersion.Version1.SessionContentType : BinaryVersion.Version1.ContentType; + contentEncoding = isDeflate ? MessageEncoderCompressionHandler.DeflateContentEncoding : MessageEncoderCompressionHandler.GZipContentEncoding; + return true; + } + return false; + } + } + + //abstract class HttpDelayedAcceptStream : DetectEofStream + //{ + // HttpOutput httpOutput; + // bool isHttpOutputClosed; + + // /// + // /// Indicates whether the HttpOutput should be closed when this stream is closed. In the streamed case, + // /// we�ll leave the HttpOutput opened (and it will be closed by the HttpRequestContext, so we won't leak it). + // /// + // bool closeHttpOutput; + + // // sometimes we can't flush the HTTP output until we're done reading the end of the + // // incoming stream of the HTTP input + // protected HttpDelayedAcceptStream(Stream stream) + // : base(stream) + // { + // } + + // public bool EnableDelayedAccept(HttpOutput output, bool closeHttpOutput) + // { + // if (IsAtEof) + // { + // return false; + // } + + // this.closeHttpOutput = closeHttpOutput; + // this.httpOutput = output; + // return true; + // } + + // protected override void OnReceivedEof() + // { + // if (this.closeHttpOutput) + // { + // CloseHttpOutput(); + // } + // } + + // public override void Close() + // { + // if (this.closeHttpOutput) + // { + // CloseHttpOutput(); + // } + + // base.Close(); + // } + + // void CloseHttpOutput() + // { + // if (this.httpOutput != null && !this.isHttpOutputClosed) + // { + // this.httpOutput.Close(); + // this.isHttpOutputClosed = true; + // } + // } + //} + + //abstract class BytesReadPositionStream : DelegatingStream + //{ + // int bytesSent = 0; + + // protected BytesReadPositionStream(Stream stream) + // : base(stream) + // { + // } + + // public override long Position + // { + // get + // { + // return bytesSent; + // } + // set + // { + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.SeekNotSupported))); + // } + // } + + // public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) + // { + // this.bytesSent += count; + // return BaseStream.BeginWrite(buffer, offset, count, callback, state); + // } + + // public override void Write(byte[] buffer, int offset, int count) + // { + // BaseStream.Write(buffer, offset, count); + // this.bytesSent += count; + // } + + // public override void WriteByte(byte value) + // { + // BaseStream.WriteByte(value); + // this.bytesSent++; + // } + //} + + class PreReadStream : DelegatingStream + { + byte[] preReadBuffer; + + public PreReadStream(Stream stream, byte[] preReadBuffer) + : base(stream) + { + this.preReadBuffer = preReadBuffer; + } + + bool ReadFromBuffer(byte[] buffer, int offset, int count, out int bytesRead) + { + if (this.preReadBuffer != null) + { + if (buffer == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(buffer)); + } + + if (offset >= buffer.Length) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(offset), offset, + SR.Format(SR.OffsetExceedsBufferBound, buffer.Length - 1))); + } + + if (count < 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(count), count, + SR.Format(SR.ValueMustBeNonNegative))); + } + + if (count == 0) + { + bytesRead = 0; + } + else + { + buffer[offset] = this.preReadBuffer[0]; + this.preReadBuffer = null; + bytesRead = 1; + } + + return true; + } + + bytesRead = -1; + return false; + } + + public override int Read(byte[] buffer, int offset, int count) + { + int bytesRead; + if (ReadFromBuffer(buffer, offset, count, out bytesRead)) + { + return bytesRead; + } + + return base.Read(buffer, offset, count); + } + + public override int ReadByte() + { + if (this.preReadBuffer != null) + { + byte[] tempBuffer = new byte[1]; + int bytesRead; + if (ReadFromBuffer(tempBuffer, 0, 1, out bytesRead)) + { + return tempBuffer[0]; + } + } + return base.ReadByte(); + } + } + + //class HttpRequestMessageHttpInput : HttpInput, HttpRequestMessageProperty.IHttpHeaderProvider + //{ + // const string SoapAction = "SOAPAction"; + // HttpRequestMessage httpRequestMessage; + // ChannelBinding channelBinding; + + // public HttpRequestMessageHttpInput(HttpRequestMessage httpRequestMessage, IHttpTransportFactorySettings settings, bool enableChannelBinding, ChannelBinding channelBinding) + // : base(settings, true, enableChannelBinding) + // { + // this.httpRequestMessage = httpRequestMessage; + // this.channelBinding = channelBinding; + // } + + // public override long ContentLength + // { + // get + // { + // if (this.httpRequestMessage.Content.Headers.ContentLength == null) + // { + // // Chunked transfer mode + // return -1; + // } + + // return this.httpRequestMessage.Content.Headers.ContentLength.Value; + // } + // } + + // protected override ChannelBinding ChannelBinding + // { + // get + // { + // return this.channelBinding; + // } + // } + + // public HttpRequestMessage HttpRequestMessage + // { + // get { return this.httpRequestMessage; } + // } + + // protected override bool HasContent + // { + // get + // { + // // In Chunked transfer mode, the ContentLength header is null + // // Otherwise we just rely on the ContentLength header + // return this.httpRequestMessage.Content.Headers.ContentLength == null || this.httpRequestMessage.Content.Headers.ContentLength.Value > 0; + // } + // } + + // protected override string ContentTypeCore + // { + // get + // { + // if (!this.HasContent) + // { + // return null; + // } + + // return this.httpRequestMessage.Content.Headers.ContentType == null ? null : this.httpRequestMessage.Content.Headers.ContentType.MediaType; + // } + // } + + // public override void ConfigureHttpRequestMessage(HttpRequestMessage message) + // { + // throw FxTrace.Exception.AsError(new InvalidOperationException()); + // } + + // protected override Stream GetInputStream() + // { + // if (this.httpRequestMessage.Content == null) + // { + // return Stream.Null; + // } + + // return this.httpRequestMessage.Content.ReadAsStreamAsync().Result; + // } + + // protected override void AddProperties(Message message) + // { + // HttpRequestMessageProperty requestProperty = new HttpRequestMessageProperty(this.httpRequestMessage); + // message.Properties.Add(HttpRequestMessageProperty.Name, requestProperty); + // message.Properties.Via = this.httpRequestMessage.RequestUri; + + // foreach (KeyValuePair property in this.httpRequestMessage.Properties) + // { + // message.Properties.Add(property.Key, property.Value); + // } + + // this.httpRequestMessage.Properties.Clear(); + // } + + // protected override string SoapActionHeader + // { + // get + // { + // IEnumerable values; + // if (this.httpRequestMessage.Headers.TryGetValues(SoapAction, out values)) + // { + // foreach (string headerValue in values) + // { + // return headerValue; + // } + // } + + // return null; + // } + // } + + // public void CopyHeaders(WebHeaderCollection headers) + // { + // // No special-casing for the "WWW-Authenticate" header required here, + // // because this method is only called for the incoming request + // // and the WWW-Authenticate header is a header only applied to responses. + // HttpChannelUtilities.CopyHeaders(this.httpRequestMessage, headers.Add); + // } + + // internal void SetHttpRequestMessage(HttpRequestMessage httpRequestMessage) + // { + // Fx.Assert(httpRequestMessage != null, "httpRequestMessage should not be null."); + // this.httpRequestMessage = httpRequestMessage; + // } + //} +} + diff --git a/src/CoreWCF.Http/src/CoreWCF/Channels/HttpReplyChannel.cs b/src/CoreWCF.Http/src/CoreWCF/Channels/HttpReplyChannel.cs new file mode 100644 index 000000000..bc521737d --- /dev/null +++ b/src/CoreWCF.Http/src/CoreWCF/Channels/HttpReplyChannel.cs @@ -0,0 +1,76 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace CoreWCF.Channels +{ + internal class HttpReplyChannel : IReplyChannel + { + private EndpointAddress endpointAddress; + + public HttpReplyChannel(EndpointAddress endpointAddress) + { + this.endpointAddress = endpointAddress; + } + + public EndpointAddress LocalAddress => throw new NotImplementedException(); + + public CommunicationState State => throw new NotImplementedException(); + + public event EventHandler Closed; + public event EventHandler Closing; + public event EventHandler Faulted; + public event EventHandler Opened; + public event EventHandler Opening; + + public void Abort() + { + throw new NotImplementedException(); + } + + public Task CloseAsync() + { + throw new NotImplementedException(); + } + + public Task CloseAsync(CancellationToken token) + { + throw new NotImplementedException(); + } + + public T GetProperty() where T : class + { + throw new NotImplementedException(); + } + + public Task OpenAsync() + { + throw new NotImplementedException(); + } + + public Task OpenAsync(CancellationToken token) + { + throw new NotImplementedException(); + } + + public Task ReceiveRequestAsync() + { + throw new NotImplementedException(); + } + + public Task ReceiveRequestAsync(CancellationToken token) + { + throw new NotImplementedException(); + } + + public Task> TryReceiveRequestAsync(CancellationToken token) + { + throw new NotImplementedException(); + } + + public Task WaitForRequestAsync(CancellationToken token) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Http/src/CoreWCF/Channels/HttpRequestContext.cs b/src/CoreWCF.Http/src/CoreWCF/Channels/HttpRequestContext.cs new file mode 100644 index 000000000..92b645e38 --- /dev/null +++ b/src/CoreWCF.Http/src/CoreWCF/Channels/HttpRequestContext.cs @@ -0,0 +1,573 @@ +using Microsoft.AspNetCore.Http; +using CoreWCF.Runtime; +using CoreWCF.Security; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Net; +using System.Security.Authentication.ExtendedProtection; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Xml; + +namespace CoreWCF.Channels +{ + abstract class HttpRequestContext : RequestContextBase + { + HttpOutput httpOutput; + bool errorGettingHttpInput; + //HttpChannelListener listener; + SecurityMessageProperty securityProperty; + //EventTraceActivity eventTraceActivity; + //HttpPipeline httpPipeline; + //ServerWebSocketTransportDuplexSessionChannel webSocketChannel; + + protected HttpRequestContext(IHttpTransportFactorySettings settings, Message requestMessage) + : base(requestMessage, settings.CloseTimeout, settings.SendTimeout) + { + HttpTransportSettings = settings; + } + + public bool KeepAliveEnabled + { + get + { + return HttpTransportSettings.KeepAliveEnabled; + } + } + + public abstract string HttpMethod { get; } + public abstract bool IsWebSocketRequest { get; } + + //internal ServerWebSocketTransportDuplexSessionChannel WebSocketChannel + //{ + // get + // { + // return this.webSocketChannel; + // } + + // set + // { + // Fx.Assert(this.webSocketChannel == null, "webSocketChannel should not be set twice."); + // this.webSocketChannel = value; + // } + //} + + internal IHttpTransportFactorySettings HttpTransportSettings { get; } + //internal HttpChannelListener Listener + //{ + // get { return this.listener; } + //} + + //internal EventTraceActivity EventTraceActivity + //{ + // get + // { + // return this.eventTraceActivity; + // } + //} + + // Note: This method will return null in the case where throwOnError is false, and a non-fatal error occurs. + // Please exercice caution when passing in throwOnError = false. This should basically only be done in error + // code paths, or code paths where there is very good reason that you would not want this method to throw. + // When passing in throwOnError = false, please handle the case where this method returns null. + public HttpInput GetHttpInput(bool throwOnError) + { + HttpInput httpInput = null; + if (throwOnError || !this.errorGettingHttpInput) + { + try + { + httpInput = GetHttpInput(); + this.errorGettingHttpInput = false; + } + catch (Exception e) + { + this.errorGettingHttpInput = true; + if (throwOnError || Fx.IsFatal(e)) + { + throw; + } + + DiagnosticUtility.TraceHandledException(e, TraceEventType.Warning); + } + } + + return httpInput; + } + + internal static HttpRequestContext CreateContext(IHttpTransportFactorySettings settings, HttpContext httpContext) + { + return new AspNetCoreHttpContext(settings, httpContext); + } + + protected abstract SecurityMessageProperty OnProcessAuthentication(); + + public abstract HttpOutput GetHttpOutput(Message message); + + protected abstract HttpInput GetHttpInput(); + + public HttpOutput GetHttpOutputCore(Message message) + { + if (this.httpOutput != null) + { + return this.httpOutput; + } + + return this.GetHttpOutput(message); + } + + protected override void OnAbort() + { + if (this.httpOutput != null) + { + this.httpOutput.Abort(HttpAbortReason.Aborted); + } + + this.Cleanup(); + } + + protected override async Task OnCloseAsync(CancellationToken token) + { + try + { + if (this.httpOutput != null) + { + await httpOutput.CloseAsync(); ; + } + } + finally + { + this.Cleanup(); + } + } + + protected virtual void Cleanup() + { + + } + + internal void SetMessage(Message message, Exception requestException) + { + if ((message == null) && (requestException == null)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new ProtocolException(SR.MessageXmlProtocolError, + new XmlException(SR.MessageIsEmpty))); + } + + this.TraceHttpMessageReceived(message); + + if (requestException != null) + { + base.SetRequestMessage(requestException); + message.Close(); + } + else + { + message.Properties.Security = (this.securityProperty != null) ? (SecurityMessageProperty)this.securityProperty.CreateCopy() : null; + base.SetRequestMessage(message); + } + } + + void TraceHttpMessageReceived(Message message) + { + } + + protected abstract HttpStatusCode ValidateAuthentication(); + + bool PrepareReply(ref Message message) + { + bool closeOnReceivedEof = false; + + // null means we're done + if (message == null) + { + // A null message means either a one-way request or that the service operation returned null and + // hence we can close the HttpOutput. By default we keep the HttpOutput open to allow the writing to the output + // even after the HttpInput EOF is received and the HttpOutput will be closed only on close of the HttpRequestContext. + closeOnReceivedEof = true; + message = CreateAckMessage(HttpStatusCode.Accepted, string.Empty); + } + + if (!HttpTransportSettings.ManualAddressing) + { + if (message.Version.Addressing == AddressingVersion.WSAddressingAugust2004) + { + if (message.Headers.To == null || + (HttpTransportSettings.AnonymousUriPrefixMatcher as HttpAnonymousUriPrefixMatcher) == null || + !(HttpTransportSettings.AnonymousUriPrefixMatcher as HttpAnonymousUriPrefixMatcher).IsAnonymousUri(message.Headers.To)) + { + message.Headers.To = message.Version.Addressing.AnonymousUri(); + } + } + else if (message.Version.Addressing == AddressingVersion.WSAddressing10 + || message.Version.Addressing == AddressingVersion.None) + { + if (message.Headers.To != null && + (HttpTransportSettings.AnonymousUriPrefixMatcher as HttpAnonymousUriPrefixMatcher == null || + !(HttpTransportSettings.AnonymousUriPrefixMatcher as HttpAnonymousUriPrefixMatcher).IsAnonymousUri(message.Headers.To))) + { + message.Headers.To = null; + } + } + else + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new ProtocolException(SR.Format(SR.AddressingVersionNotSupported, message.Version.Addressing))); + } + } + + message.Properties.AllowOutputBatching = false; + this.httpOutput = GetHttpOutputCore(message); + + return closeOnReceivedEof; + } + + protected override async Task OnReplyAsync(Message message, CancellationToken token) + { + Message responseMessage = message; + + try + { + bool closeOutputAfterReply = PrepareReply(ref responseMessage); + httpOutput = this.GetHttpOutput(message); + await httpOutput.SendAsync(token); + + if (closeOutputAfterReply) + { + await httpOutput.CloseAsync(); + } + } + finally + { + if (message != null && + !object.ReferenceEquals(message, responseMessage)) + { + responseMessage.Close(); + } + } + } + + public async Task ProcessAuthenticationAsync() + { + HttpStatusCode statusCode = ValidateAuthentication(); + + if (statusCode == HttpStatusCode.OK) + { + bool authenticationSucceeded = false; + statusCode = HttpStatusCode.Forbidden; + try + { + this.securityProperty = OnProcessAuthentication(); + authenticationSucceeded = true; + return true; + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + + if (e.Data.Contains(HttpChannelUtilities.HttpStatusCodeKey)) + { + if (e.Data[HttpChannelUtilities.HttpStatusCodeKey] is HttpStatusCode) + { + statusCode = (HttpStatusCode)e.Data[HttpChannelUtilities.HttpStatusCodeKey]; + } + } + + throw; + } + finally + { + if (!authenticationSucceeded) + { + await SendResponseAndCloseAsync(statusCode); + } + } + } + else + { + await SendResponseAndCloseAsync(statusCode); + return false; + } + } + + internal Task SendResponseAndCloseAsync(HttpStatusCode statusCode) + { + return SendResponseAndCloseAsync(statusCode, string.Empty); + } + + internal async Task SendResponseAndCloseAsync(HttpStatusCode statusCode, string statusDescription) + { + if (ReplyInitiated) + { + await CloseAsync(); + return; + } + + using (Message ackMessage = CreateAckMessage(statusCode, statusDescription)) + { + await ReplyAsync(ackMessage); + } + + await CloseAsync(); + } + + Message CreateAckMessage(HttpStatusCode statusCode, string statusDescription) + { + Message ackMessage = new NullMessage(); + HttpResponseMessageProperty httpResponseProperty = new HttpResponseMessageProperty(); + httpResponseProperty.StatusCode = statusCode; + httpResponseProperty.SuppressEntityBody = true; + if (statusDescription.Length > 0) + { + httpResponseProperty.StatusDescription = statusDescription; + } + + ackMessage.Properties.Add(HttpResponseMessageProperty.Name, httpResponseProperty); + + return ackMessage; + } + + class AspNetCoreHttpContext : HttpRequestContext + { + HttpContext _aspNetContext; + // byte[] webSocketInternalBuffer; + + public AspNetCoreHttpContext(IHttpTransportFactorySettings settings, HttpContext aspNetContext) + : base(settings, null) + { + _aspNetContext = aspNetContext; + } + + public override string HttpMethod => _aspNetContext.Request.Method; + + public override bool IsWebSocketRequest => false; + + protected override HttpInput GetHttpInput() + { + return new AspNetCoreHttpInput(this); + } + public override HttpOutput GetHttpOutput(Message message) + { + // TODO: Enable KeepAlive setting + //if (!_httpBindingElement.KeepAlive) + //{ + // aspNetContext.Response.Headers["Connection"] = "close"; + //} + + ICompressedMessageEncoder compressedMessageEncoder = HttpTransportSettings.MessageEncoderFactory.Encoder as ICompressedMessageEncoder; + if (compressedMessageEncoder != null && compressedMessageEncoder.CompressionEnabled) + { + string acceptEncoding = _aspNetContext.Request.Headers[HttpChannelUtilities.AcceptEncodingHeader]; + compressedMessageEncoder.AddCompressedMessageProperties(message, acceptEncoding); + } + + return HttpOutput.CreateHttpOutput(_aspNetContext, HttpTransportSettings, message, HttpMethod); + } + + protected override SecurityMessageProperty OnProcessAuthentication() + { + // TODO: Wire up authentication for ASP.Net Core + //return Listener.ProcessAuthentication(listenerContext); + return null; + } + + protected override HttpStatusCode ValidateAuthentication() + { + // TODO: Wire up authentication for ASP.Net Core + return HttpStatusCode.OK; + //return Listener.ValidateAuthentication(listenerContext); + } + + protected override void OnAbort() + { + _aspNetContext.Abort(); + this.Cleanup(); + } + + protected override Task OnCloseAsync(CancellationToken token) + { + return base.OnCloseAsync(token); + //try + //{ + // // TODO: Work out how to close the HttpContext + // // Most likely will be some mechanism to compelte the Task returned by the RequestDelegate + // aspNetContext.Response.Close(); + //} + //catch (HttpListenerException listenerException) + //{ + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + // HttpChannelUtilities.CreateCommunicationException(listenerException)); + //} + } + + class AspNetCoreHttpInput : HttpInput + { + AspNetCoreHttpContext _aspNetCoreHttpContext; + string cachedContentType; // accessing the header in System.Net involves a native transition + byte[] preReadBuffer; + + // TODO: ChannelBindingSupport + public AspNetCoreHttpInput(AspNetCoreHttpContext aspNetCoreHttpContext) + : base(aspNetCoreHttpContext.HttpTransportSettings, true, false /* ChannelBindingSupportEnabled */) + { + _aspNetCoreHttpContext = aspNetCoreHttpContext; + if (!this._aspNetCoreHttpContext._aspNetContext.Request.ContentLength.HasValue) + { + // TODO: Look into useing PipeReader with look-ahead + this.preReadBuffer = new byte[1]; + if (_aspNetCoreHttpContext._aspNetContext.Request.Body.Read(preReadBuffer, 0, 1) == 0) + { + this.preReadBuffer = null; + } + } + } + + // TODO: Switch to nullable + public override long ContentLength => _aspNetCoreHttpContext._aspNetContext.Request.ContentLength ?? -1; + + protected override string ContentTypeCore + { + get + { + if (cachedContentType == null) + { + cachedContentType = _aspNetCoreHttpContext._aspNetContext.Request.ContentType; + } + + return cachedContentType; + } + } + + protected override bool HasContent => preReadBuffer != null || ContentLength > 0; + + protected override string SoapActionHeader => _aspNetCoreHttpContext._aspNetContext.Request.Headers["SOAPAction"]; + + + protected override ChannelBinding ChannelBinding + { + get + { + throw new PlatformNotSupportedException("Shouldn't be able to request CBT"); // TODO: ChannelBindingToken + // return ChannelBindingUtility.GetToken(this.listenerHttpContext.listenerContext.Request.TransportContext); + } + } + + protected override void AddProperties(Message message) + { + var request = _aspNetCoreHttpContext._aspNetContext.Request; + HttpRequestMessageProperty requestProperty = new HttpRequestMessageProperty(); + requestProperty.Method = request.Method; + foreach (var header in request.Headers) + { + requestProperty.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray()); + } + + // TODO: Uri.Query always includes the '?', check if the same is true for ASP.NET Core + if (request.QueryString.HasValue) + { + requestProperty.QueryString = request.QueryString.Value.Substring(1); + } + + message.Properties.Add(HttpRequestMessageProperty.Name, requestProperty); + // TODO: Test the Via code + message.Properties.Via = new Uri(string.Concat( + request.Scheme, + "://", + request.Host.ToUriComponent(), + request.PathBase.ToUriComponent(), + request.Path.ToUriComponent(), + request.QueryString.ToUriComponent())); + + var remoteIPAddress = request.HttpContext.Connection.RemoteIpAddress; + var remotePort = request.HttpContext.Connection.RemotePort; + RemoteEndpointMessageProperty remoteEndpointProperty = new RemoteEndpointMessageProperty(new IPEndPoint(remoteIPAddress, remotePort)); + message.Properties.Add(RemoteEndpointMessageProperty.Name, remoteEndpointProperty); + } + + protected override Stream GetInputStream() + { + if (preReadBuffer != null) + { + return new AspNetCoreInputStream(_aspNetCoreHttpContext, preReadBuffer); + } + else + { + return new AspNetCoreInputStream(_aspNetCoreHttpContext); + } + } + + class AspNetCoreInputStream : DetectEofStream + { + public AspNetCoreInputStream(AspNetCoreHttpContext aspNetCoreHttpContext) + : base(aspNetCoreHttpContext._aspNetContext.Request.Body) + { + } + + public AspNetCoreInputStream(AspNetCoreHttpContext aspNetCoreHttpContext, byte[] preReadBuffer) + : base(new PreReadStream(aspNetCoreHttpContext._aspNetContext.Request.Body, preReadBuffer)) + { + } + + public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state) + { + try + { + return base.BeginRead(buffer, offset, count, callback, state); + } + catch (Exception exception) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + HttpChannelUtilities.CreateCommunicationException(exception)); + } + } + + public override int EndRead(IAsyncResult result) + { + try + { + return base.EndRead(result); + } + catch (Exception exception) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + HttpChannelUtilities.CreateCommunicationException(exception)); + } + } + + public override int Read(byte[] buffer, int offset, int count) + { + try + { + return base.Read(buffer, offset, count); + } + catch (Exception exception) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + HttpChannelUtilities.CreateCommunicationException(exception)); + } + } + + public override int ReadByte() + { + try + { + return base.ReadByte(); + } + catch (Exception exception) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + HttpChannelUtilities.CreateCommunicationException(exception)); + } + } + } + } + } + } +} diff --git a/src/CoreWCF.Http/src/CoreWCF/Channels/HttpRequestMessageProperty.cs b/src/CoreWCF.Http/src/CoreWCF/Channels/HttpRequestMessageProperty.cs new file mode 100644 index 000000000..109348e3c --- /dev/null +++ b/src/CoreWCF.Http/src/CoreWCF/Channels/HttpRequestMessageProperty.cs @@ -0,0 +1,48 @@ +using System.Net.Http.Headers; + +namespace CoreWCF.Channels +{ + public sealed class HttpRequestMessageProperty : IMessageProperty + { + private string _method; + + private string _queryString; + + public HttpHeaders Headers { get; } = new ServiceModelHttpHeaders(); + + public string Method + { + get { return _method; } + set + { + if (value == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(value)); + } + + _method = value; + } + } + + public static string Name => "httpRequest"; + + public string QueryString + { + get { return _queryString; } + set + { + if (value == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(value)); + } + + _queryString = value; + } + } + + IMessageProperty IMessageProperty.CreateCopy() + { + return this; + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Http/src/CoreWCF/Channels/HttpResponseMessageProperty.cs b/src/CoreWCF.Http/src/CoreWCF/Channels/HttpResponseMessageProperty.cs new file mode 100644 index 000000000..03c994684 --- /dev/null +++ b/src/CoreWCF.Http/src/CoreWCF/Channels/HttpResponseMessageProperty.cs @@ -0,0 +1,85 @@ +using System; +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; + +namespace CoreWCF.Channels +{ + public sealed class HttpResponseMessageProperty : IMessageProperty + { + public const HttpStatusCode DefaultStatusCode = HttpStatusCode.OK; + public const string DefaultStatusDescription = null; // null means use description from status code + + private WebHeaderCollection _originalHeaders; + private HttpStatusCode _statusCode; + private WebHeaderCollection _headers; + + public HttpResponseMessageProperty() + : this((WebHeaderCollection)null) + { + } + + internal HttpResponseMessageProperty(WebHeaderCollection originalHeaders) + { + _originalHeaders = originalHeaders; + _statusCode = DefaultStatusCode; + StatusDescription = DefaultStatusDescription; + } + + public static string Name + { + get { return "httpResponse"; } + } + + public WebHeaderCollection Headers + { + get + { + if (_headers == null) + { + _headers = new WebHeaderCollection(); + if (_originalHeaders != null) + { + _headers.Add(_originalHeaders); + _originalHeaders = null; + } + } + + return _headers; + } + } + + public HttpStatusCode StatusCode + { + get + { + return _statusCode; + } + set + { + int valueInt = (int)value; + if (valueInt < 100 || valueInt > 599) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(value), value, + SR.Format(SR.ValueMustBeInRange, 100, 599))); + } + + _statusCode = value; + HasStatusCodeBeenSet = true; + } + } + + internal bool HasStatusCodeBeenSet { get; private set; } + + public string StatusDescription { get; set; } + + public bool SuppressEntityBody { get; set; } + + public bool SuppressPreamble { get; set; } + + IMessageProperty IMessageProperty.CreateCopy() + { + return this; + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Http/src/CoreWCF/Channels/HttpTransportBindingElement.cs b/src/CoreWCF.Http/src/CoreWCF/Channels/HttpTransportBindingElement.cs new file mode 100644 index 000000000..868be2a17 --- /dev/null +++ b/src/CoreWCF.Http/src/CoreWCF/Channels/HttpTransportBindingElement.cs @@ -0,0 +1,123 @@ +using System; +using System.ComponentModel; +using System.Net; + +namespace CoreWCF.Channels +{ + public class HttpTransportBindingElement : TransportBindingElement + { + //HttpAnonymousUriPrefixMatcher _anonymousUriPrefixMatcher; + + public HttpTransportBindingElement() + { + MaxBufferSize = TransportDefaults.MaxBufferSize; + KeepAliveEnabled = HttpTransportDefaults.KeepAliveEnabled; + TransferMode = HttpTransportDefaults.TransferMode; + } + + protected HttpTransportBindingElement(HttpTransportBindingElement elementToBeCloned) : base(elementToBeCloned) + { + MaxBufferSize = elementToBeCloned.MaxBufferSize; + TransferMode = elementToBeCloned.TransferMode; + } + // [System.ComponentModel.DefaultValueAttribute(false)] + // public bool AllowCookies { get { return default(bool); } set { } } + // [System.ComponentModel.DefaultValueAttribute((System.Net.AuthenticationSchemes)(32768))] + // public System.Net.AuthenticationSchemes AuthenticationScheme { get { return default(System.Net.AuthenticationSchemes); } set { } } + public int MaxBufferSize { get; set; } + public bool KeepAliveEnabled { get; set; } + public override string Scheme { get { return "http"; } } + // [System.ComponentModel.DefaultValueAttribute((System.ServiceModel.TransferMode)(0))] + public TransferMode TransferMode { get; set; } + // public System.ServiceModel.Channels.WebSocketTransportSettings WebSocketSettings { get { return default(System.ServiceModel.Channels.WebSocketTransportSettings); } set { } } + // public override System.ServiceModel.Channels.IChannelFactory BuildChannelFactory(System.ServiceModel.Channels.BindingContext context) { return default(System.ServiceModel.Channels.IChannelFactory); } + public override bool CanBuildChannelListener(BindingContext context) + { + if (typeof(TChannel) == typeof(IReplyChannel)) + { + return true; + //return this.WebSocketSettings.TransportUsage != WebSocketTransportUsage.Always; + } + //else if (typeof(TChannel) == typeof(IDuplexSessionChannel)) + //{ + // return this.WebSocketSettings.TransportUsage != WebSocketTransportUsage.Never; + //} + return false; + } + + public override IChannelListener BuildChannelListener(BindingContext context) + { + if (context == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("context"); + } + + if (!CanBuildChannelListener(context)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument( + "TChannel", SR.Format(SR.CouldnTCreateChannelForChannelType2, context.Binding.Name, typeof(TChannel))); + } + + return null; + } + + public override BindingElement Clone() + { + return new HttpTransportBindingElement(this); + } + + public override T GetProperty(BindingContext context) + { + if (context == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(context)); + } + //if (typeof(T) == typeof(ISecurityCapabilities)) + //{ + // AuthenticationSchemes effectiveAuthenticationSchemes = HttpTransportBindingElement.GetEffectiveAuthenticationSchemes(this.AuthenticationScheme, + // context.BindingParameters); + + // return (T)(object)new SecurityCapabilities(this.GetSupportsClientAuthenticationImpl(effectiveAuthenticationSchemes), + // effectiveAuthenticationSchemes == AuthenticationSchemes.Negotiate, + // this.GetSupportsClientWindowsIdentityImpl(effectiveAuthenticationSchemes), + // ProtectionLevel.None, + // ProtectionLevel.None); + //} + //else if (typeof(T) == typeof(IBindingDeliveryCapabilities)) + //{ + // return (T)(object)new BindingDeliveryCapabilitiesHelper(); + //} + /*else*/ if (typeof(T) == typeof(TransferMode)) + { + return (T)(object)TransferMode; + } + //else if (typeof(T) == typeof(ExtendedProtectionPolicy)) + //{ + // return (T)(object)this.ExtendedProtectionPolicy; + //} + //else if (typeof(T) == typeof(IAnonymousUriPrefixMatcher)) + //{ + // if (_anonymousUriPrefixMatcher == null) + // { + // _anonymousUriPrefixMatcher = new HttpAnonymousUriPrefixMatcher(); + // } + + // return (T)(object)_anonymousUriPrefixMatcher; + //} + //else if (typeof(T) == typeof(ITransportCompressionSupport)) + //{ + // return (T)(object)new TransportCompressionSupportHelper(); + //} + else + { + if (context.BindingParameters.Find() == null) + { + context.BindingParameters.Add(new TextMessageEncodingBindingElement()); + } + return base.GetProperty(context); + } + } + + public override Type MiddlewareType => typeof(ServiceModelHttpMiddleware); + } +} \ No newline at end of file diff --git a/src/CoreWCF.Http/src/CoreWCF/Channels/HttpTransportSettings.cs b/src/CoreWCF.Http/src/CoreWCF/Channels/HttpTransportSettings.cs new file mode 100644 index 000000000..764b2165d --- /dev/null +++ b/src/CoreWCF.Http/src/CoreWCF/Channels/HttpTransportSettings.cs @@ -0,0 +1,21 @@ +using System; + +namespace CoreWCF.Channels +{ + internal class HttpTransportSettings : IHttpTransportFactorySettings + { + public TimeSpan CloseTimeout { get; set; } + public TimeSpan OpenTimeout { get; set; } + public TimeSpan ReceiveTimeout { get; set; } + public TimeSpan SendTimeout { get; set; } + public bool ManualAddressing { get; set; } + public BufferManager BufferManager { get; set; } + public long MaxReceivedMessageSize { get; set; } + public MessageEncoderFactory MessageEncoderFactory { get; set; } + public MessageVersion MessageVersion => MessageEncoderFactory.MessageVersion; + public int MaxBufferSize { get; set; } + public TransferMode TransferMode { get; set; } + public bool KeepAliveEnabled { get; set; } + public IAnonymousUriPrefixMatcher AnonymousUriPrefixMatcher { get; set; } + } +} diff --git a/src/CoreWCF.Http/src/CoreWCF/Channels/ITransportFactorySettings.cs b/src/CoreWCF.Http/src/CoreWCF/Channels/ITransportFactorySettings.cs new file mode 100644 index 000000000..fe9655e21 --- /dev/null +++ b/src/CoreWCF.Http/src/CoreWCF/Channels/ITransportFactorySettings.cs @@ -0,0 +1,10 @@ +namespace CoreWCF.Channels +{ + interface IHttpTransportFactorySettings : ITransportFactorySettings + { + int MaxBufferSize { get; } + TransferMode TransferMode { get; } + bool KeepAliveEnabled { get; set; } + IAnonymousUriPrefixMatcher AnonymousUriPrefixMatcher { get; } + } +} diff --git a/src/CoreWCF.Http/src/CoreWCF/Channels/MessageEncoderCompressionHandler.cs b/src/CoreWCF.Http/src/CoreWCF/Channels/MessageEncoderCompressionHandler.cs new file mode 100644 index 000000000..6b3af440e --- /dev/null +++ b/src/CoreWCF.Http/src/CoreWCF/Channels/MessageEncoderCompressionHandler.cs @@ -0,0 +1,8 @@ +namespace CoreWCF.Channels +{ + internal static class MessageEncoderCompressionHandler + { + internal const string GZipContentEncoding = "gzip"; + internal const string DeflateContentEncoding = "deflate"; + } +} diff --git a/src/CoreWCF.Http/src/CoreWCF/Channels/RequestDelegateHandler.cs b/src/CoreWCF.Http/src/CoreWCF/Channels/RequestDelegateHandler.cs new file mode 100644 index 000000000..37968a013 --- /dev/null +++ b/src/CoreWCF.Http/src/CoreWCF/Channels/RequestDelegateHandler.cs @@ -0,0 +1,79 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using CoreWCF.Runtime; +using CoreWCF.Configuration; +using System; +using System.Threading; +using System.Threading.Tasks; +using System.Xml; + +namespace CoreWCF.Channels +{ + internal class RequestDelegateHandler + { + internal const long DefaultMaxBufferPoolSize = 512 * 1024; + private IServiceDispatcher _dispatcher; + private IDefaultCommunicationTimeouts _timeouts; + private IServiceScopeFactory _servicesScopeFactory; + private HttpTransportSettings _httpSettings; + private IReplyChannel _replyChannel; + + public RequestDelegateHandler(IServiceDispatcher dispatcher, IServiceScopeFactory servicesScopeFactory) + { + _dispatcher = dispatcher; + _timeouts = _dispatcher.Binding; + _servicesScopeFactory = servicesScopeFactory; + BuildHandler(); + } + + private void BuildHandler() + { + var be = _dispatcher.Binding.CreateBindingElements(); + var mebe = be.Find(); + if (mebe == null) + { + throw new ArgumentException("Must provide a MessageEncodingBindingElement", nameof(_dispatcher.Binding)); + } + + var tbe = be.Find(); + if (tbe == null) + { + throw new ArgumentException("Must provide a HttpTransportBindingElement", nameof(_dispatcher.Binding)); + } + + var httpSettings = new HttpTransportSettings(); + httpSettings.BufferManager = BufferManager.CreateBufferManager(DefaultMaxBufferPoolSize, tbe.MaxBufferSize); + httpSettings.OpenTimeout = _dispatcher.Binding.OpenTimeout; + httpSettings.ReceiveTimeout = _dispatcher.Binding.ReceiveTimeout; + httpSettings.SendTimeout = _dispatcher.Binding.SendTimeout; + httpSettings.CloseTimeout = _dispatcher.Binding.CloseTimeout; + httpSettings.MaxBufferSize = tbe.MaxBufferSize; + httpSettings.MaxReceivedMessageSize = tbe.MaxReceivedMessageSize; + httpSettings.MessageEncoderFactory = mebe.CreateMessageEncoderFactory(); + httpSettings.ManualAddressing = tbe.ManualAddressing; + httpSettings.TransferMode = tbe.TransferMode; + httpSettings.KeepAliveEnabled = tbe.KeepAliveEnabled; + httpSettings.AnonymousUriPrefixMatcher = new HttpAnonymousUriPrefixMatcher(); + _httpSettings = httpSettings; + var scope = _servicesScopeFactory.CreateScope(); + _replyChannel = new AspNetCoreReplyChannel(_servicesScopeFactory.CreateScope().ServiceProvider); + } + + internal Task HandleRequest(HttpContext context) + { + var requestContext = HttpRequestContext.CreateContext(_httpSettings, context); + var httpInput = requestContext.GetHttpInput(true); + Exception requestException; + Message requestMessage = httpInput.ParseIncomingMessage(out requestException); + if ((requestMessage == null) && (requestException == null)) + { + throw Fx.Exception.AsError( + new ProtocolException( + SR.MessageXmlProtocolError, + new XmlException(SR.MessageIsEmpty))); + } + requestContext.SetMessage(requestMessage, requestException); + return _dispatcher.DispatchAsync(requestContext, _replyChannel, context.RequestAborted); + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Http/src/CoreWCF/Channels/ServiceModelHttpHeaders.cs b/src/CoreWCF.Http/src/CoreWCF/Channels/ServiceModelHttpHeaders.cs new file mode 100644 index 000000000..5d3f10736 --- /dev/null +++ b/src/CoreWCF.Http/src/CoreWCF/Channels/ServiceModelHttpHeaders.cs @@ -0,0 +1,10 @@ +using System.Net.Http.Headers; + +namespace CoreWCF.Channels +{ + // This type allows the use of a generally typed HttpHeaders property on Http{Request|Response}MessageProperty + internal class ServiceModelHttpHeaders : HttpHeaders + { + + } +} \ No newline at end of file diff --git a/src/CoreWCF.Http/src/CoreWCF/Channels/ServiceModelHttpMiddleware.cs b/src/CoreWCF.Http/src/CoreWCF/Channels/ServiceModelHttpMiddleware.cs new file mode 100644 index 000000000..a95c5b311 --- /dev/null +++ b/src/CoreWCF.Http/src/CoreWCF/Channels/ServiceModelHttpMiddleware.cs @@ -0,0 +1,89 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting.Server.Features; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using CoreWCF.Configuration; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; + +namespace CoreWCF.Channels +{ + public partial class ServiceModelHttpMiddleware + { + private IServiceBuilder _serviceBuilder; + private RequestDelegate _next; + private readonly RequestDelegate _branch; + + public ServiceModelHttpMiddleware(RequestDelegate next, IApplicationBuilder app, IServiceBuilder serviceBuilder, IDispatcherBuilder dispatcherBuilder) + { + _serviceBuilder = serviceBuilder; + _next = next; + _branch = BuildBranch(app, _serviceBuilder, dispatcherBuilder); + } + + public Task InvokeAsync(HttpContext context) + { + return _branch(context); + } + + private RequestDelegate BuildBranch(IApplicationBuilder app, IServiceBuilder serviceBuilder, IDispatcherBuilder dispatcherBuilder) + { + var branchApp = app.New(); + var serverAddresses = app.ServerFeatures.Get(); + foreach (var address in serverAddresses.Addresses) + { + serviceBuilder.BaseAddresses.Add(new Uri(address)); + } + + foreach (var serviceType in serviceBuilder.Services) + { + var dispatchers = dispatcherBuilder.BuildDispatchers(serviceType); + foreach (var dispatcher in dispatchers) + { + if (dispatcher.BaseAddress == null) + { + // TODO: Should we throw? Ignore? + continue; + } + + var scheme = dispatcher.BaseAddress?.Scheme; + if (!"http".Equals(scheme, StringComparison.OrdinalIgnoreCase) && + !"https".Equals(scheme, StringComparison.OrdinalIgnoreCase)) + { + continue; // Not an HTTP(S) dispatcher + } + + bool matching = false; + foreach (var serverAddress in serverAddresses.Addresses) + { + // TODO: Might not be necessary to compare paths + var serverUri = new Uri(serverAddress); + var serverAddressNormalized = string.Join(':', + serverUri.GetComponents(UriComponents.Port | UriComponents.Path, UriFormat.SafeUnescaped)); + var dispatcherAddressNormalized = string.Join(':', + dispatcher.BaseAddress.GetComponents(UriComponents.Port | UriComponents.Path, UriFormat.SafeUnescaped)); + if (dispatcherAddressNormalized.StartsWith(serverAddressNormalized, StringComparison.OrdinalIgnoreCase)) + { + matching = true; + break; // Dispatcher address is based on server listening address; + } + } + + if (matching) + { + branchApp.Map(dispatcher.BaseAddress.AbsolutePath, wcfApp => + { + var servicesScopeFactory = wcfApp.ApplicationServices.GetRequiredService(); + wcfApp.Run(new RequestDelegateHandler(dispatcher, servicesScopeFactory).HandleRequest); + }); + } + } + } + + branchApp.Use(_ => { return context => _next(context); }); + return branchApp.Build(); + } + } +} diff --git a/src/CoreWCF.Http/src/CoreWCF/Channels/TransportDefaults.cs b/src/CoreWCF.Http/src/CoreWCF/Channels/TransportDefaults.cs new file mode 100644 index 000000000..d877c485b --- /dev/null +++ b/src/CoreWCF.Http/src/CoreWCF/Channels/TransportDefaults.cs @@ -0,0 +1,26 @@ +using System; + +namespace CoreWCF.Channels +{ + public static class TransportDefaults + { + internal const long MaxReceivedMessageSize = 65536; + internal const int MaxBufferSize = (int)MaxReceivedMessageSize; + } + + public static class BasicHttpBindingDefaults + { + public const WSMessageEncoding MessageEncoding = WSMessageEncoding.Text; + } + + internal static class HttpTransportDefaults + { + internal const TransferMode TransferMode = CoreWCF.TransferMode.Buffered; + internal const bool KeepAliveEnabled = true; + } + + internal static class ConnectionOrientedTransportDefaults + { + internal const int ConnectionBufferSize = 8192; + } +} \ No newline at end of file diff --git a/src/CoreWCF.Http/src/CoreWCF/DiagnosticUtility.cs b/src/CoreWCF.Http/src/CoreWCF/DiagnosticUtility.cs new file mode 100644 index 000000000..15840c0e4 --- /dev/null +++ b/src/CoreWCF.Http/src/CoreWCF/DiagnosticUtility.cs @@ -0,0 +1,188 @@ +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using CoreWCF.Runtime; + +namespace CoreWCF +{ + internal class DiagnosticUtility + { + private static ExceptionUtility exceptionUtility = (ExceptionUtility)null; + private static object lockObject = new object(); + + internal static ExceptionUtility ExceptionUtility + { + get + { + return exceptionUtility ?? GetExceptionUtility(); + } + } + + private static ExceptionUtility GetExceptionUtility() + { + lock (lockObject) + { + if (exceptionUtility == null) + // TODO: Make this generic shared code used by multiple assemblies + //exceptionUtility = new ExceptionUtility("System.ServiceModel", "System.ServiceModel 4.0.0.0", (object)DiagnosticUtility.diagnosticTrace, (object)FxTrace.Exception); + exceptionUtility = new ExceptionUtility(); + } + + return exceptionUtility; + } + + internal static void TraceHandledException(Exception exception, TraceEventType traceEventType) + { + //FxTrace.Exception.TraceHandledException(exception, traceEventType); + } + + [Conditional("DEBUG")] + internal static void DebugAssert(bool condition, string message) + { + if (!condition) + { + DebugAssert(message); + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + [Conditional("DEBUG")] + internal static void DebugAssert(string message) + { + Fx.Assert(message); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + internal static Exception FailFast(string message) + { + try + { + try + { + ExceptionUtility.TraceFailFast(message); + } + finally + { + Environment.FailFast(message); + } + } + catch + { + } + Environment.FailFast(message); + return (Exception)null; + } + } + + internal class ExceptionUtility + { + internal ArgumentException ThrowHelperArgument(string message) + { + return (ArgumentException)ThrowHelperError(new ArgumentException(message)); + } + + internal ArgumentException ThrowHelperArgument(string paramName, string message) + { + return (ArgumentException)ThrowHelperError(new ArgumentException(message, paramName)); + } + + internal ArgumentNullException ThrowHelperArgumentNull(string paramName) + { + return (ArgumentNullException)ThrowHelperError(new ArgumentNullException(paramName)); + } + + //internal Exception ThrowHelperFatal(string message, Exception innerException) + //{ + // return ThrowHelperError(new FatalException(message, innerException)); + //} + + internal Exception ThrowHelperError(Exception exception) + { + return ThrowHelper(exception, TraceEventType.Error); + } + + internal Exception ThrowHelperWarning(Exception exception) + { + return ThrowHelper(exception, TraceEventType.Warning); + } + + internal Exception ThrowHelper(Exception exception, TraceEventType eventType) + { + return ThrowHelper(exception, eventType, null); + } + + internal Exception ThrowHelper(Exception exception, TraceEventType eventType, TraceRecord extendedData) + { + //if ((_diagnosticTrace == null ? 0 : (_diagnosticTrace.ShouldTrace(eventType) ? 1 : 0)) != 0) + //{ + // using ( + // ExceptionUtility.useStaticActivityId + // ? Activity.CreateActivity(ExceptionUtility.activityId) + // : (Activity)null) + // _diagnosticTrace.TraceEvent(eventType, 131075, + // LegacyDiagnosticTrace.GenerateMsdnTraceCode("System.ServiceModel.Diagnostics", + // "ThrowingException"), TraceSR.Format("ThrowingException"), extendedData, exception, + // (object)null); + // IDictionary data = exception.Data; + // if (data != null && !data.IsReadOnly && !data.IsFixedSize) + // { + // object obj = + // data[(object)"System.ServiceModel.Diagnostics.ExceptionUtility.ExceptionStackAsString"]; + // string str1 = obj == null ? "" : obj as string; + // if (str1 != null) + // { + // string stackTrace = exception.StackTrace; + // if (!string.IsNullOrEmpty(stackTrace)) + // { + // string str2 = str1 + (str1.Length == 0 ? "" : Environment.NewLine) + "throw" + + // Environment.NewLine + stackTrace + Environment.NewLine + "catch" + + // Environment.NewLine; + // data[(object)"System.ServiceModel.Diagnostics.ExceptionUtility.ExceptionStackAsString"] + // = (object)str2; + // } + // } + // } + //} + //this.exceptionTrace.TraceEtwException(exception, eventType); + return exception; + } + + internal Exception ThrowHelperCallback(Exception innerException) + { + return ThrowHelperCallback(TraceSR.GenericCallbackException, innerException); + } + + internal Exception ThrowHelperCallback(string message, Exception innerException) + { + return ThrowHelperCritical(new CallbackException(message, innerException)); + } + + internal Exception ThrowHelperCritical(Exception exception) + { + return ThrowHelper(exception, TraceEventType.Critical); + } + + internal class TraceRecord + { + } + + [MethodImpl(MethodImplOptions.NoInlining)] + internal void TraceFailFast(string message) + { + //Microsoft.Runtime.Diagnostics.EventLogger logger = null; + //try + //{ + // logger = new Microsoft.Runtime.Diagnostics.EventLogger(this.eventSourceName, this.diagnosticTrace); + //} + //finally + //{ + // TraceFailFast(message, logger); + //} + } + + internal Exception ThrowHelperArgumentNull(string paramName, string message) + { + return (ArgumentNullException)ThrowHelperError(new ArgumentNullException(paramName, message)); + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Http/src/CoreWCF/Diagnostics/TraceUtility.cs b/src/CoreWCF.Http/src/CoreWCF/Diagnostics/TraceUtility.cs new file mode 100644 index 000000000..7579f7228 --- /dev/null +++ b/src/CoreWCF.Http/src/CoreWCF/Diagnostics/TraceUtility.cs @@ -0,0 +1,62 @@ +using System; +using System.Reflection; +using CoreWCF.Runtime; +using CoreWCF.Channels; +using CoreWCF.Dispatcher; + +namespace CoreWCF.Diagnostics +{ + // TODO: Work out how TraceUtility fits in with all the other exception and tracing classes + internal static class TraceUtility + { + internal static Exception ThrowHelperError(Exception exception, Message message) + { + // If the message is closed, we won't get an activity + //Guid activityId = TraceUtility.ExtractActivityId(message); + //if (DiagnosticUtility.ShouldTraceError) + //{ + // DiagnosticUtility.DiagnosticTrace.TraceEvent(TraceEventType.Error, TraceCode.ThrowingException, GenerateMsdnTraceCode(TraceCode.ThrowingException), + // TraceSR.Format(TraceSR.ThrowingException), null, exception, activityId, null); + //} + return exception; + } + + internal static Exception ThrowHelperError(Exception exception, Guid activityId, object source) + { + //if (DiagnosticUtility.ShouldTraceError) + //{ + // DiagnosticUtility.DiagnosticTrace.TraceEvent(TraceEventType.Error, TraceCode.ThrowingException, GenerateMsdnTraceCode(TraceCode.ThrowingException), + // TraceSR.Format(TraceSR.ThrowingException), null, exception, activityId, source); + //} + return exception; + } + + internal static void TraceUserCodeException(Exception e, MethodInfo method) + { + //if (DiagnosticUtility.ShouldTraceWarning) + //{ + // StringTraceRecord record = new StringTraceRecord("Comment", + // SR.Format(SR.SFxUserCodeThrewException, method.DeclaringType.FullName, method.Name)); + // DiagnosticUtility.DiagnosticTrace.TraceEvent(TraceEventType.Warning, + // TraceCode.UnhandledExceptionInUserOperation, GenerateMsdnTraceCode(TraceCode.UnhandledExceptionInUserOperation), + // SR.Format(SR.TraceCodeUnhandledExceptionInUserOperation, method.DeclaringType.FullName, method.Name), + // record, + // e, null); + //} + } + + internal static void TraceDroppedMessage(Message requestMessage, EndpointDispatcher endpoint) + { + //if (DiagnosticUtility.ShouldTraceInformation) + //{ + // EndpointAddress endpointAddress = null; + // if (dispatcher != null) + // { + // endpointAddress = dispatcher.EndpointAddress; + // } + // TraceUtility.TraceEvent(TraceEventType.Information, TraceCode.DroppedAMessage, + // SR.Format(SR.TraceCodeDroppedAMessage), new MessageDroppedTraceRecord(message, endpointAddress)); + //} + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Http/src/CoreWCF/DummyTransportBindingElement.cs b/src/CoreWCF.Http/src/CoreWCF/DummyTransportBindingElement.cs new file mode 100644 index 000000000..6e9717186 --- /dev/null +++ b/src/CoreWCF.Http/src/CoreWCF/DummyTransportBindingElement.cs @@ -0,0 +1,18 @@ +using CoreWCF.Channels; + +namespace CoreWCF +{ + internal class DummyTransportBindingElement : TransportBindingElement + { + public DummyTransportBindingElement() + { + } + + public override BindingElement Clone() + { + return this; + } + + public override string Scheme { get { return "dummy"; } } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Http/src/CoreWCF/HttpBindingBase.cs b/src/CoreWCF.Http/src/CoreWCF/HttpBindingBase.cs new file mode 100644 index 000000000..df795bd28 --- /dev/null +++ b/src/CoreWCF.Http/src/CoreWCF/HttpBindingBase.cs @@ -0,0 +1,99 @@ +using System; +using System.ComponentModel; +using System.Diagnostics.Contracts; +using System.Text; +using System.Xml; +using CoreWCF.Channels; + +namespace CoreWCF +{ + public abstract class HttpBindingBase : Binding + { + private HttpTransportBindingElement _httpTransport; + private TextMessageEncodingBindingElement _textEncoding; + + internal HttpBindingBase() + { + _httpTransport = new HttpTransportBindingElement(); + _textEncoding = new TextMessageEncodingBindingElement(); + _textEncoding.MessageVersion = MessageVersion.Soap11; + } + // [System.ComponentModel.DefaultValueAttribute(false)] + // public bool AllowCookies { get { return default(bool); } set { } } + public EnvelopeVersion EnvelopeVersion { get { return default(EnvelopeVersion); } } + // [System.ComponentModel.DefaultValueAttribute((long)524288)] + // public long MaxBufferPoolSize { get { return default(long); } set { } } + // [System.ComponentModel.DefaultValueAttribute(65536)] + // public int MaxBufferSize { get { return default(int); } set { } } + [DefaultValue((long)65536)] + public long MaxReceivedMessageSize { get { return default(long); } set { } } + public XmlDictionaryReaderQuotas ReaderQuotas { get { return default(XmlDictionaryReaderQuotas); } set { } } + + public override string Scheme { get { return GetTransport().Scheme; } } + public Encoding TextEncoding { get { return default(Encoding); } set { } } + // [System.ComponentModel.DefaultValueAttribute((System.ServiceModel.TransferMode)(0))] + // public System.ServiceModel.TransferMode TransferMode { get { return default(System.ServiceModel.TransferMode); } set { } } + + internal TextMessageEncodingBindingElement TextMessageEncodingBindingElement + { + get + { + return _textEncoding; + } + } + + internal TransportBindingElement GetTransport() + { + //Contract.Assert(this.BasicHttpSecurity != null, "this.BasicHttpSecurity should not return null from a derived class."); + + //BasicHttpSecurity basicHttpSecurity = this.BasicHttpSecurity; + //if (basicHttpSecurity.Mode == BasicHttpSecurityMode.Transport || basicHttpSecurity.Mode == BasicHttpSecurityMode.TransportWithMessageCredential) + //{ + // basicHttpSecurity.EnableTransportSecurity(_httpsTransport); + // return _httpsTransport; + //} + //else if (basicHttpSecurity.Mode == BasicHttpSecurityMode.TransportCredentialOnly) + //{ + // basicHttpSecurity.EnableTransportAuthentication(_httpTransport); + // return _httpTransport; + //} + //else + //{ + // // ensure that there is no transport security + // basicHttpSecurity.DisableTransportAuthentication(_httpTransport); + // return _httpTransport; + //} + return _httpTransport; + } + + internal virtual void CheckSettings() + { + //BasicHttpSecurity security = this.BasicHttpSecurity; + //if (security == null) + //{ + // return; + //} + + //BasicHttpSecurityMode mode = security.Mode; + //if (mode == BasicHttpSecurityMode.None) + //{ + // return; + //} + //else if (mode == BasicHttpSecurityMode.Message || mode == BasicHttpSecurityMode.TransportWithMessageCredential) + //{ + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.Format(SR.UnsupportedSecuritySetting, "Mode", mode))); + //} + + //// Transport.ClientCredentialType = InheritedFromHost are not supported. + //Fx.Assert( + // (mode == BasicHttpSecurityMode.Transport) || (mode == BasicHttpSecurityMode.TransportCredentialOnly), + // "Unexpected BasicHttpSecurityMode value: " + mode); + //HttpTransportSecurity transport = security.Transport; + //if (transport != null && transport.ClientCredentialType == HttpClientCredentialType.InheritedFromHost) + //{ + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.Format(SR.UnsupportedSecuritySetting, "Transport.ClientCredentialType", transport.ClientCredentialType))); + //} + } + + } +} \ No newline at end of file diff --git a/src/CoreWCF.Http/src/CoreWCF/Runtime/Diagnostics/EtwDiagnosticTrace.cs b/src/CoreWCF.Http/src/CoreWCF/Runtime/Diagnostics/EtwDiagnosticTrace.cs new file mode 100644 index 000000000..dcab9bbc3 --- /dev/null +++ b/src/CoreWCF.Http/src/CoreWCF/Runtime/Diagnostics/EtwDiagnosticTrace.cs @@ -0,0 +1,74 @@ +using System; + +namespace CoreWCF.Runtime.Diagnostics +{ + internal sealed class EtwDiagnosticTrace + { + static readonly public Guid ImmutableDefaultEtwProviderId = new Guid("{c651f5f6-1c0d-492e-8ae1-b4efd7c9d503}"); + private static Guid s_defaultEtwProviderId = ImmutableDefaultEtwProviderId; + + static public Guid DefaultEtwProviderId + { + get + { + return s_defaultEtwProviderId; + } + set + { + s_defaultEtwProviderId = value; + } + } + + public EtwDiagnosticTrace(string traceSourceName, Guid etwProviderId) + //: base(traceSourceName) + { +// try +// { +// this.TraceSourceName = traceSourceName; +// this.EventSourceName = string.Concat(this.TraceSourceName, " ", EventSourceVersion); +// CreateTraceSource(); +// } +// catch (Exception exception) +// { +// if (Fx.IsFatal(exception)) +// { +// throw; +// } + +//#pragma warning disable 618 +// EventLogger logger = new EventLogger(this.EventSourceName, null); +// logger.LogEvent(TraceEventType.Error, TracingEventLogCategory, (uint)System.Runtime.Diagnostics.EventLogEventId.FailedToSetupTracing, false, +// exception.ToString()); +//#pragma warning restore 618 +// } + +// try +// { +// CreateEtwProvider(etwProviderId); +// } +// catch (Exception exception) +// { +// if (Fx.IsFatal(exception)) +// { +// throw; +// } + +// this.etwProvider = null; +//#pragma warning disable 618 +// EventLogger logger = new EventLogger(this.EventSourceName, null); +// logger.LogEvent(TraceEventType.Error, TracingEventLogCategory, (uint)System.Runtime.Diagnostics.EventLogEventId.FailedToSetupTracing, false, +// exception.ToString()); +//#pragma warning restore 618 + +// } + +// if (this.TracingEnabled || this.EtwTracingEnabled) +// { +//#pragma warning disable 618 +// this.AddDomainEventHandlersForCleanup(); +//#pragma warning restore 618 +// } + } + + } +} \ No newline at end of file diff --git a/src/CoreWCF.Http/src/CoreWCF/Runtime/UrlUtility.cs b/src/CoreWCF.Http/src/CoreWCF/Runtime/UrlUtility.cs new file mode 100644 index 000000000..cf8fd302e --- /dev/null +++ b/src/CoreWCF.Http/src/CoreWCF/Runtime/UrlUtility.cs @@ -0,0 +1,175 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoreWCF.Runtime +{ + internal static class UrlUtility + { + public static string UrlDecode(string str, Encoding e) + { + if (str == null) + { + return null; + } + return UrlDecodeStringFromStringInternal(str, e); + } + + static string UrlDecodeStringFromStringInternal(string s, Encoding e) + { + int count = s.Length; + UrlDecoder helper = new UrlDecoder(count, e); + + // go through the string's chars collapsing %XX and %uXXXX and + // appending each char as char, with exception of %XX constructs + // that are appended as bytes + + for (int pos = 0; pos < count; pos++) + { + char ch = s[pos]; + + if (ch == '+') + { + ch = ' '; + } + else if (ch == '%' && pos < count - 2) + { + if (s[pos + 1] == 'u' && pos < count - 5) + { + int h1 = HexToInt(s[pos + 2]); + int h2 = HexToInt(s[pos + 3]); + int h3 = HexToInt(s[pos + 4]); + int h4 = HexToInt(s[pos + 5]); + + if (h1 >= 0 && h2 >= 0 && h3 >= 0 && h4 >= 0) + { // valid 4 hex chars + ch = (char)((h1 << 12) | (h2 << 8) | (h3 << 4) | h4); + pos += 5; + + // only add as char + helper.AddChar(ch); + continue; + } + } + else + { + int h1 = HexToInt(s[pos + 1]); + int h2 = HexToInt(s[pos + 2]); + + if (h1 >= 0 && h2 >= 0) + { // valid 2 hex chars + byte b = (byte)((h1 << 4) | h2); + pos += 2; + + // don't add as char + helper.AddByte(b); + continue; + } + } + } + + if ((ch & 0xFF80) == 0) + { + helper.AddByte((byte)ch); // 7 bit have to go as bytes because of Unicode + } + else + { + helper.AddChar(ch); + } + } + + return helper.GetString(); + } + + // Private helpers for URL encoding/decoding + static int HexToInt(char h) + { + return (h >= '0' && h <= '9') ? h - '0' : + (h >= 'a' && h <= 'f') ? h - 'a' + 10 : + (h >= 'A' && h <= 'F') ? h - 'A' + 10 : + -1; + } + + // Internal class to facilitate URL decoding -- keeps char buffer and byte buffer, allows appending of either chars or bytes + class UrlDecoder + { + int _bufferSize; + + // Accumulate characters in a special array + int _numChars; + char[] _charBuffer; + + // Accumulate bytes for decoding into characters in a special array + int _numBytes; + byte[] _byteBuffer; + + // Encoding to convert chars to bytes + Encoding _encoding; + + void FlushBytes() + { + if (_numBytes > 0) + { + _numChars += _encoding.GetChars(_byteBuffer, 0, _numBytes, _charBuffer, _numChars); + _numBytes = 0; + } + } + + internal UrlDecoder(int bufferSize, Encoding encoding) + { + _bufferSize = bufferSize; + _encoding = encoding; + + _charBuffer = new char[bufferSize]; + // byte buffer created on demand + } + + internal void AddChar(char ch) + { + if (_numBytes > 0) + { + FlushBytes(); + } + + _charBuffer[_numChars++] = ch; + } + + internal void AddByte(byte b) + { + // if there are no pending bytes treat 7 bit bytes as characters + // this optimization is temp disable as it doesn't work for some encodings + + //if (_numBytes == 0 && ((b & 0x80) == 0)) { + // AddChar((char)b); + //} + //else + + { + if (_byteBuffer == null) + { + _byteBuffer = new byte[_bufferSize]; + } + + _byteBuffer[_numBytes++] = b; + } + } + + internal string GetString() + { + if (_numBytes > 0) + { + FlushBytes(); + } + + if (_numChars > 0) + { + return new String(_charBuffer, 0, _numChars); + } + else + { + return string.Empty; + } + } + } + } +} diff --git a/src/CoreWCF.Http/src/CoreWCF/ServiceModelDictionary.cs b/src/CoreWCF.Http/src/CoreWCF/ServiceModelDictionary.cs new file mode 100644 index 000000000..2dc0a73d1 --- /dev/null +++ b/src/CoreWCF.Http/src/CoreWCF/ServiceModelDictionary.cs @@ -0,0 +1,111 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Xml; + +namespace CoreWCF +{ + class ServiceModelDictionary : IXmlDictionary + { + static public readonly ServiceModelDictionary Version1 = new ServiceModelDictionary(new ServiceModelStringsVersion1()); + ServiceModelStrings strings; + int count; + XmlDictionaryString[] dictionaryStrings1; + XmlDictionaryString[] dictionaryStrings2; + Dictionary dictionary; + XmlDictionaryString[] versionedDictionaryStrings; + + public ServiceModelDictionary(ServiceModelStrings strings) + { + this.strings = strings; + this.count = strings.Count; + } + + static public ServiceModelDictionary CurrentVersion => Version1; + + public XmlDictionaryString CreateString(string value, int key) => new XmlDictionaryString(this, value, key); + + public bool TryLookup(string key, out XmlDictionaryString value) + { + if (key == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException(nameof(key))); + if (this.dictionary == null) + { + Dictionary dictionary = new Dictionary(count); + for (int i = 0; i < count; i++) + dictionary.Add(strings[i], i); + this.dictionary = dictionary; + } + int id; + if (this.dictionary.TryGetValue(key, out id)) + return TryLookup(id, out value); + value = null; + return false; + } + + public bool TryLookup(int key, out XmlDictionaryString value) + { + const int keyThreshold = 32; + if (key < 0 || key >= count) + { + value = null; + return false; + } + XmlDictionaryString s; + if (key < keyThreshold) + { + if (dictionaryStrings1 == null) + dictionaryStrings1 = new XmlDictionaryString[keyThreshold]; + s = dictionaryStrings1[key]; + if (s == null) + { + s = CreateString(strings[key], key); + dictionaryStrings1[key] = s; + } + } + else + { + if (dictionaryStrings2 == null) + dictionaryStrings2 = new XmlDictionaryString[count - keyThreshold]; + s = dictionaryStrings2[key - keyThreshold]; + if (s == null) + { + s = CreateString(strings[key], key); + dictionaryStrings2[key - keyThreshold] = s; + } + } + value = s; + return true; + } + + public bool TryLookup(XmlDictionaryString key, out XmlDictionaryString value) + { + if (key == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("key")); + if (key.Dictionary == this) + { + value = key; + return true; + } + if (key.Dictionary == CurrentVersion) + { + if (versionedDictionaryStrings == null) + versionedDictionaryStrings = new XmlDictionaryString[CurrentVersion.count]; + XmlDictionaryString s = versionedDictionaryStrings[key.Key]; + if (s == null) + { + if (!TryLookup(key.Value, out s)) + { + value = null; + return false; + } + versionedDictionaryStrings[key.Key] = s; + } + value = s; + return true; + } + value = null; + return false; + } + } +} diff --git a/src/CoreWCF.Http/src/CoreWCF/ServiceModelStrings.cs b/src/CoreWCF.Http/src/CoreWCF/ServiceModelStrings.cs new file mode 100644 index 000000000..a8dc78e8d --- /dev/null +++ b/src/CoreWCF.Http/src/CoreWCF/ServiceModelStrings.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoreWCF +{ + abstract class ServiceModelStrings + { + public abstract int Count { get; } + public abstract string this[int index] { get; } + } +} diff --git a/src/CoreWCF.Http/src/CoreWCF/ServiceModelStringsVersion1.cs b/src/CoreWCF.Http/src/CoreWCF/ServiceModelStringsVersion1.cs new file mode 100644 index 000000000..460a995c3 --- /dev/null +++ b/src/CoreWCF.Http/src/CoreWCF/ServiceModelStringsVersion1.cs @@ -0,0 +1,998 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoreWCF +{ + class ServiceModelStringsVersion1 : ServiceModelStrings + { + public const string String0 = "mustUnderstand"; + public const string String1 = "Envelope"; + public const string String2 = "http://www.w3.org/2003/05/soap-envelope"; + public const string String3 = "http://www.w3.org/2005/08/addressing"; + public const string String4 = "Header"; + public const string String5 = "Action"; + public const string String6 = "To"; + public const string String7 = "Body"; + public const string String8 = "Algorithm"; + public const string String9 = "RelatesTo"; + public const string String10 = "http://www.w3.org/2005/08/addressing/anonymous"; + public const string String11 = "URI"; + public const string String12 = "Reference"; + public const string String13 = "MessageID"; + public const string String14 = "Id"; + public const string String15 = "Identifier"; + public const string String16 = "http://schemas.xmlsoap.org/ws/2005/02/rm"; + public const string String17 = "Transforms"; + public const string String18 = "Transform"; + public const string String19 = "DigestMethod"; + public const string String20 = "DigestValue"; + public const string String21 = "Address"; + public const string String22 = "ReplyTo"; + public const string String23 = "SequenceAcknowledgement"; + public const string String24 = "AcknowledgementRange"; + public const string String25 = "Upper"; + public const string String26 = "Lower"; + public const string String27 = "BufferRemaining"; + public const string String28 = "http://schemas.microsoft.com/ws/2006/05/rm"; + public const string String29 = "http://schemas.xmlsoap.org/ws/2005/02/rm/SequenceAcknowledgement"; + public const string String30 = "SecurityTokenReference"; + public const string String31 = "Sequence"; + public const string String32 = "MessageNumber"; + public const string String33 = "http://www.w3.org/2000/09/xmldsig#"; + public const string String34 = "http://www.w3.org/2000/09/xmldsig#enveloped-signature"; + public const string String35 = "KeyInfo"; + public const string String36 = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"; + public const string String37 = "http://www.w3.org/2001/04/xmlenc#"; + public const string String38 = "http://schemas.xmlsoap.org/ws/2005/02/sc"; + public const string String39 = "DerivedKeyToken"; + public const string String40 = "Nonce"; + public const string String41 = "Signature"; + public const string String42 = "SignedInfo"; + public const string String43 = "CanonicalizationMethod"; + public const string String44 = "SignatureMethod"; + public const string String45 = "SignatureValue"; + public const string String46 = "DataReference"; + public const string String47 = "EncryptedData"; + public const string String48 = "EncryptionMethod"; + public const string String49 = "CipherData"; + public const string String50 = "CipherValue"; + public const string String51 = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"; + public const string String52 = "Security"; + public const string String53 = "Timestamp"; + public const string String54 = "Created"; + public const string String55 = "Expires"; + public const string String56 = "Length"; + public const string String57 = "ReferenceList"; + public const string String58 = "ValueType"; + public const string String59 = "Type"; + public const string String60 = "EncryptedHeader"; + public const string String61 = "http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd"; + public const string String62 = "RequestSecurityTokenResponseCollection"; + public const string String63 = "http://schemas.xmlsoap.org/ws/2005/02/trust"; + public const string String64 = "http://schemas.xmlsoap.org/ws/2005/02/trust#BinarySecret"; + public const string String65 = "http://schemas.microsoft.com/ws/2006/02/transactions"; + public const string String66 = "s"; + public const string String67 = "Fault"; + public const string String68 = "MustUnderstand"; + public const string String69 = "role"; + public const string String70 = "relay"; + public const string String71 = "Code"; + public const string String72 = "Reason"; + public const string String73 = "Text"; + public const string String74 = "Node"; + public const string String75 = "Role"; + public const string String76 = "Detail"; + public const string String77 = "Value"; + public const string String78 = "Subcode"; + public const string String79 = "NotUnderstood"; + public const string String80 = "qname"; + public const string String81 = ""; + public const string String82 = "From"; + public const string String83 = "FaultTo"; + public const string String84 = "EndpointReference"; + public const string String85 = "PortType"; + public const string String86 = "ServiceName"; + public const string String87 = "PortName"; + public const string String88 = "ReferenceProperties"; + public const string String89 = "RelationshipType"; + public const string String90 = "Reply"; + public const string String91 = "a"; + public const string String92 = "http://schemas.xmlsoap.org/ws/2006/02/addressingidentity"; + public const string String93 = "Identity"; + public const string String94 = "Spn"; + public const string String95 = "Upn"; + public const string String96 = "Rsa"; + public const string String97 = "Dns"; + public const string String98 = "X509v3Certificate"; + public const string String99 = "http://www.w3.org/2005/08/addressing/fault"; + public const string String100 = "ReferenceParameters"; + public const string String101 = "IsReferenceParameter"; + public const string String102 = "http://www.w3.org/2005/08/addressing/reply"; + public const string String103 = "http://www.w3.org/2005/08/addressing/none"; + public const string String104 = "Metadata"; + public const string String105 = "http://schemas.xmlsoap.org/ws/2004/08/addressing"; + public const string String106 = "http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous"; + public const string String107 = "http://schemas.xmlsoap.org/ws/2004/08/addressing/fault"; + public const string String108 = "http://schemas.xmlsoap.org/ws/2004/06/addressingex"; + public const string String109 = "RedirectTo"; + public const string String110 = "Via"; + public const string String111 = "http://www.w3.org/2001/10/xml-exc-c14n#"; + public const string String112 = "PrefixList"; + public const string String113 = "InclusiveNamespaces"; + public const string String114 = "ec"; + public const string String115 = "SecurityContextToken"; + public const string String116 = "Generation"; + public const string String117 = "Label"; + public const string String118 = "Offset"; + public const string String119 = "Properties"; + public const string String120 = "Cookie"; + public const string String121 = "wsc"; + public const string String122 = "http://schemas.xmlsoap.org/ws/2004/04/sc"; + public const string String123 = "http://schemas.xmlsoap.org/ws/2004/04/security/sc/dk"; + public const string String124 = "http://schemas.xmlsoap.org/ws/2004/04/security/sc/sct"; + public const string String125 = "http://schemas.xmlsoap.org/ws/2004/04/security/trust/RST/SCT"; + public const string String126 = "http://schemas.xmlsoap.org/ws/2004/04/security/trust/RSTR/SCT"; + public const string String127 = "RenewNeeded"; + public const string String128 = "BadContextToken"; + public const string String129 = "c"; + public const string String130 = "http://schemas.xmlsoap.org/ws/2005/02/sc/dk"; + public const string String131 = "http://schemas.xmlsoap.org/ws/2005/02/sc/sct"; + public const string String132 = "http://schemas.xmlsoap.org/ws/2005/02/trust/RST/SCT"; + public const string String133 = "http://schemas.xmlsoap.org/ws/2005/02/trust/RSTR/SCT"; + public const string String134 = "http://schemas.xmlsoap.org/ws/2005/02/trust/RST/SCT/Renew"; + public const string String135 = "http://schemas.xmlsoap.org/ws/2005/02/trust/RSTR/SCT/Renew"; + public const string String136 = "http://schemas.xmlsoap.org/ws/2005/02/trust/RST/SCT/Cancel"; + public const string String137 = "http://schemas.xmlsoap.org/ws/2005/02/trust/RSTR/SCT/Cancel"; + public const string String138 = "http://www.w3.org/2001/04/xmlenc#aes128-cbc"; + public const string String139 = "http://www.w3.org/2001/04/xmlenc#kw-aes128"; + public const string String140 = "http://www.w3.org/2001/04/xmlenc#aes192-cbc"; + public const string String141 = "http://www.w3.org/2001/04/xmlenc#kw-aes192"; + public const string String142 = "http://www.w3.org/2001/04/xmlenc#aes256-cbc"; + public const string String143 = "http://www.w3.org/2001/04/xmlenc#kw-aes256"; + public const string String144 = "http://www.w3.org/2001/04/xmlenc#des-cbc"; + public const string String145 = "http://www.w3.org/2000/09/xmldsig#dsa-sha1"; + public const string String146 = "http://www.w3.org/2001/10/xml-exc-c14n#WithComments"; + public const string String147 = "http://www.w3.org/2000/09/xmldsig#hmac-sha1"; + public const string String148 = "http://www.w3.org/2001/04/xmldsig-more#hmac-sha256"; + public const string String149 = "http://schemas.xmlsoap.org/ws/2005/02/sc/dk/p_sha1"; + public const string String150 = "http://www.w3.org/2001/04/xmlenc#ripemd160"; + public const string String151 = "http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p"; + public const string String152 = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; + public const string String153 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"; + public const string String154 = "http://www.w3.org/2001/04/xmlenc#rsa-1_5"; + public const string String155 = "http://www.w3.org/2000/09/xmldsig#sha1"; + public const string String156 = "http://www.w3.org/2001/04/xmlenc#sha256"; + public const string String157 = "http://www.w3.org/2001/04/xmlenc#sha512"; + public const string String158 = "http://www.w3.org/2001/04/xmlenc#tripledes-cbc"; + public const string String159 = "http://www.w3.org/2001/04/xmlenc#kw-tripledes"; + public const string String160 = "http://schemas.xmlsoap.org/2005/02/trust/tlsnego#TLS_Wrap"; + public const string String161 = "http://schemas.xmlsoap.org/2005/02/trust/spnego#GSS_Wrap"; + public const string String162 = "http://schemas.microsoft.com/ws/2006/05/security"; + public const string String163 = "dnse"; + public const string String164 = "o"; + public const string String165 = "Password"; + public const string String166 = "PasswordText"; + public const string String167 = "Username"; + public const string String168 = "UsernameToken"; + public const string String169 = "BinarySecurityToken"; + public const string String170 = "EncodingType"; + public const string String171 = "KeyIdentifier"; + public const string String172 = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary"; + public const string String173 = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#HexBinary"; + public const string String174 = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Text"; + public const string String175 = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509SubjectKeyIdentifier"; + public const string String176 = "http://docs.oasis-open.org/wss/oasis-wss-kerberos-token-profile-1.1#GSS_Kerberosv5_AP_REQ"; + public const string String177 = "http://docs.oasis-open.org/wss/oasis-wss-kerberos-token-profile-1.1#GSS_Kerberosv5_AP_REQ1510"; + public const string String178 = "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.0#SAMLAssertionID"; + public const string String179 = "Assertion"; + public const string String180 = "urn:oasis:names:tc:SAML:1.0:assertion"; + public const string String181 = "http://docs.oasis-open.org/wss/oasis-wss-rel-token-profile-1.0.pdf#license"; + public const string String182 = "FailedAuthentication"; + public const string String183 = "InvalidSecurityToken"; + public const string String184 = "InvalidSecurity"; + public const string String185 = "k"; + public const string String186 = "SignatureConfirmation"; + public const string String187 = "TokenType"; + public const string String188 = "http://docs.oasis-open.org/wss/oasis-wss-soap-message-security-1.1#ThumbprintSHA1"; + public const string String189 = "http://docs.oasis-open.org/wss/oasis-wss-soap-message-security-1.1#EncryptedKey"; + public const string String190 = "http://docs.oasis-open.org/wss/oasis-wss-soap-message-security-1.1#EncryptedKeySHA1"; + public const string String191 = "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1"; + public const string String192 = "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0"; + public const string String193 = "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLID"; + public const string String194 = "AUTH-HASH"; + public const string String195 = "RequestSecurityTokenResponse"; + public const string String196 = "KeySize"; + public const string String197 = "RequestedTokenReference"; + public const string String198 = "AppliesTo"; + public const string String199 = "Authenticator"; + public const string String200 = "CombinedHash"; + public const string String201 = "BinaryExchange"; + public const string String202 = "Lifetime"; + public const string String203 = "RequestedSecurityToken"; + public const string String204 = "Entropy"; + public const string String205 = "RequestedProofToken"; + public const string String206 = "ComputedKey"; + public const string String207 = "RequestSecurityToken"; + public const string String208 = "RequestType"; + public const string String209 = "Context"; + public const string String210 = "BinarySecret"; + public const string String211 = "http://schemas.xmlsoap.org/ws/2005/02/trust/spnego"; + public const string String212 = " http://schemas.xmlsoap.org/ws/2005/02/trust/tlsnego"; + public const string String213 = "wst"; + public const string String214 = "http://schemas.xmlsoap.org/ws/2004/04/trust"; + public const string String215 = "http://schemas.xmlsoap.org/ws/2004/04/security/trust/RST/Issue"; + public const string String216 = "http://schemas.xmlsoap.org/ws/2004/04/security/trust/RSTR/Issue"; + public const string String217 = "http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue"; + public const string String218 = "http://schemas.xmlsoap.org/ws/2004/04/security/trust/CK/PSHA1"; + public const string String219 = "http://schemas.xmlsoap.org/ws/2004/04/security/trust/SymmetricKey"; + public const string String220 = "http://schemas.xmlsoap.org/ws/2004/04/security/trust/Nonce"; + public const string String221 = "KeyType"; + public const string String222 = "http://schemas.xmlsoap.org/ws/2004/04/trust/SymmetricKey"; + public const string String223 = "http://schemas.xmlsoap.org/ws/2004/04/trust/PublicKey"; + public const string String224 = "Claims"; + public const string String225 = "InvalidRequest"; + public const string String226 = "RequestFailed"; + public const string String227 = "SignWith"; + public const string String228 = "EncryptWith"; + public const string String229 = "EncryptionAlgorithm"; + public const string String230 = "CanonicalizationAlgorithm"; + public const string String231 = "ComputedKeyAlgorithm"; + public const string String232 = "UseKey"; + public const string String233 = "http://schemas.microsoft.com/net/2004/07/secext/WS-SPNego"; + public const string String234 = "http://schemas.microsoft.com/net/2004/07/secext/TLSNego"; + public const string String235 = "t"; + public const string String236 = "http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue"; + public const string String237 = "http://schemas.xmlsoap.org/ws/2005/02/trust/RSTR/Issue"; + public const string String238 = "http://schemas.xmlsoap.org/ws/2005/02/trust/Issue"; + public const string String239 = "http://schemas.xmlsoap.org/ws/2005/02/trust/SymmetricKey"; + public const string String240 = "http://schemas.xmlsoap.org/ws/2005/02/trust/CK/PSHA1"; + public const string String241 = "http://schemas.xmlsoap.org/ws/2005/02/trust/Nonce"; + public const string String242 = "RenewTarget"; + public const string String243 = "CancelTarget"; + public const string String244 = "RequestedTokenCancelled"; + public const string String245 = "RequestedAttachedReference"; + public const string String246 = "RequestedUnattachedReference"; + public const string String247 = "IssuedTokens"; + public const string String248 = "http://schemas.xmlsoap.org/ws/2005/02/trust/Renew"; + public const string String249 = "http://schemas.xmlsoap.org/ws/2005/02/trust/Cancel"; + public const string String250 = "http://schemas.xmlsoap.org/ws/2005/02/trust/PublicKey"; + public const string String251 = "Access"; + public const string String252 = "AccessDecision"; + public const string String253 = "Advice"; + public const string String254 = "AssertionID"; + public const string String255 = "AssertionIDReference"; + public const string String256 = "Attribute"; + public const string String257 = "AttributeName"; + public const string String258 = "AttributeNamespace"; + public const string String259 = "AttributeStatement"; + public const string String260 = "AttributeValue"; + public const string String261 = "Audience"; + public const string String262 = "AudienceRestrictionCondition"; + public const string String263 = "AuthenticationInstant"; + public const string String264 = "AuthenticationMethod"; + public const string String265 = "AuthenticationStatement"; + public const string String266 = "AuthorityBinding"; + public const string String267 = "AuthorityKind"; + public const string String268 = "AuthorizationDecisionStatement"; + public const string String269 = "Binding"; + public const string String270 = "Condition"; + public const string String271 = "Conditions"; + public const string String272 = "Decision"; + public const string String273 = "DoNotCacheCondition"; + public const string String274 = "Evidence"; + public const string String275 = "IssueInstant"; + public const string String276 = "Issuer"; + public const string String277 = "Location"; + public const string String278 = "MajorVersion"; + public const string String279 = "MinorVersion"; + public const string String280 = "NameIdentifier"; + public const string String281 = "Format"; + public const string String282 = "NameQualifier"; + public const string String283 = "Namespace"; + public const string String284 = "NotBefore"; + public const string String285 = "NotOnOrAfter"; + public const string String286 = "saml"; + public const string String287 = "Statement"; + public const string String288 = "Subject"; + public const string String289 = "SubjectConfirmation"; + public const string String290 = "SubjectConfirmationData"; + public const string String291 = "ConfirmationMethod"; + public const string String292 = "urn:oasis:names:tc:SAML:1.0:cm:holder-of-key"; + public const string String293 = "urn:oasis:names:tc:SAML:1.0:cm:sender-vouches"; + public const string String294 = "SubjectLocality"; + public const string String295 = "DNSAddress"; + public const string String296 = "IPAddress"; + public const string String297 = "SubjectStatement"; + public const string String298 = "urn:oasis:names:tc:SAML:1.0:am:unspecified"; + public const string String299 = "xmlns"; + public const string String300 = "Resource"; + public const string String301 = "UserName"; + public const string String302 = "urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName"; + public const string String303 = "EmailName"; + public const string String304 = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"; + public const string String305 = "u"; + public const string String306 = "ChannelInstance"; + public const string String307 = "http://schemas.microsoft.com/ws/2005/02/duplex"; + public const string String308 = "Encoding"; + public const string String309 = "MimeType"; + public const string String310 = "CarriedKeyName"; + public const string String311 = "Recipient"; + public const string String312 = "EncryptedKey"; + public const string String313 = "KeyReference"; + public const string String314 = "e"; + public const string String315 = "http://www.w3.org/2001/04/xmlenc#Element"; + public const string String316 = "http://www.w3.org/2001/04/xmlenc#Content"; + public const string String317 = "KeyName"; + public const string String318 = "MgmtData"; + public const string String319 = "KeyValue"; + public const string String320 = "RSAKeyValue"; + public const string String321 = "Modulus"; + public const string String322 = "Exponent"; + public const string String323 = "X509Data"; + public const string String324 = "X509IssuerSerial"; + public const string String325 = "X509IssuerName"; + public const string String326 = "X509SerialNumber"; + public const string String327 = "X509Certificate"; + public const string String328 = "AckRequested"; + public const string String329 = "http://schemas.xmlsoap.org/ws/2005/02/rm/AckRequested"; + public const string String330 = "AcksTo"; + public const string String331 = "Accept"; + public const string String332 = "CreateSequence"; + public const string String333 = "http://schemas.xmlsoap.org/ws/2005/02/rm/CreateSequence"; + public const string String334 = "CreateSequenceRefused"; + public const string String335 = "CreateSequenceResponse"; + public const string String336 = "http://schemas.xmlsoap.org/ws/2005/02/rm/CreateSequenceResponse"; + public const string String337 = "FaultCode"; + public const string String338 = "InvalidAcknowledgement"; + public const string String339 = "LastMessage"; + public const string String340 = "http://schemas.xmlsoap.org/ws/2005/02/rm/LastMessage"; + public const string String341 = "LastMessageNumberExceeded"; + public const string String342 = "MessageNumberRollover"; + public const string String343 = "Nack"; + public const string String344 = "netrm"; + public const string String345 = "Offer"; + public const string String346 = "r"; + public const string String347 = "SequenceFault"; + public const string String348 = "SequenceTerminated"; + public const string String349 = "TerminateSequence"; + public const string String350 = "http://schemas.xmlsoap.org/ws/2005/02/rm/TerminateSequence"; + public const string String351 = "UnknownSequence"; + public const string String352 = "http://schemas.microsoft.com/ws/2006/02/tx/oletx"; + public const string String353 = "oletx"; + public const string String354 = "OleTxTransaction"; + public const string String355 = "PropagationToken"; + public const string String356 = "http://schemas.xmlsoap.org/ws/2004/10/wscoor"; + public const string String357 = "wscoor"; + public const string String358 = "CreateCoordinationContext"; + public const string String359 = "CreateCoordinationContextResponse"; + public const string String360 = "CoordinationContext"; + public const string String361 = "CurrentContext"; + public const string String362 = "CoordinationType"; + public const string String363 = "RegistrationService"; + public const string String364 = "Register"; + public const string String365 = "RegisterResponse"; + public const string String366 = "ProtocolIdentifier"; + public const string String367 = "CoordinatorProtocolService"; + public const string String368 = "ParticipantProtocolService"; + public const string String369 = "http://schemas.xmlsoap.org/ws/2004/10/wscoor/CreateCoordinationContext"; + public const string String370 = "http://schemas.xmlsoap.org/ws/2004/10/wscoor/CreateCoordinationContextResponse"; + public const string String371 = "http://schemas.xmlsoap.org/ws/2004/10/wscoor/Register"; + public const string String372 = "http://schemas.xmlsoap.org/ws/2004/10/wscoor/RegisterResponse"; + public const string String373 = "http://schemas.xmlsoap.org/ws/2004/10/wscoor/fault"; + public const string String374 = "ActivationCoordinatorPortType"; + public const string String375 = "RegistrationCoordinatorPortType"; + public const string String376 = "InvalidState"; + public const string String377 = "InvalidProtocol"; + public const string String378 = "InvalidParameters"; + public const string String379 = "NoActivity"; + public const string String380 = "ContextRefused"; + public const string String381 = "AlreadyRegistered"; + public const string String382 = "http://schemas.xmlsoap.org/ws/2004/10/wsat"; + public const string String383 = "wsat"; + public const string String384 = "http://schemas.xmlsoap.org/ws/2004/10/wsat/Completion"; + public const string String385 = "http://schemas.xmlsoap.org/ws/2004/10/wsat/Durable2PC"; + public const string String386 = "http://schemas.xmlsoap.org/ws/2004/10/wsat/Volatile2PC"; + public const string String387 = "Prepare"; + public const string String388 = "Prepared"; + public const string String389 = "ReadOnly"; + public const string String390 = "Commit"; + public const string String391 = "Rollback"; + public const string String392 = "Committed"; + public const string String393 = "Aborted"; + public const string String394 = "Replay"; + public const string String395 = "http://schemas.xmlsoap.org/ws/2004/10/wsat/Commit"; + public const string String396 = "http://schemas.xmlsoap.org/ws/2004/10/wsat/Rollback"; + public const string String397 = "http://schemas.xmlsoap.org/ws/2004/10/wsat/Committed"; + public const string String398 = "http://schemas.xmlsoap.org/ws/2004/10/wsat/Aborted"; + public const string String399 = "http://schemas.xmlsoap.org/ws/2004/10/wsat/Prepare"; + public const string String400 = "http://schemas.xmlsoap.org/ws/2004/10/wsat/Prepared"; + public const string String401 = "http://schemas.xmlsoap.org/ws/2004/10/wsat/ReadOnly"; + public const string String402 = "http://schemas.xmlsoap.org/ws/2004/10/wsat/Replay"; + public const string String403 = "http://schemas.xmlsoap.org/ws/2004/10/wsat/fault"; + public const string String404 = "CompletionCoordinatorPortType"; + public const string String405 = "CompletionParticipantPortType"; + public const string String406 = "CoordinatorPortType"; + public const string String407 = "ParticipantPortType"; + public const string String408 = "InconsistentInternalState"; + public const string String409 = "mstx"; + public const string String410 = "Enlistment"; + public const string String411 = "protocol"; + public const string String412 = "LocalTransactionId"; + public const string String413 = "IsolationLevel"; + public const string String414 = "IsolationFlags"; + public const string String415 = "Description"; + public const string String416 = "Loopback"; + public const string String417 = "RegisterInfo"; + public const string String418 = "ContextId"; + public const string String419 = "TokenId"; + public const string String420 = "AccessDenied"; + public const string String421 = "InvalidPolicy"; + public const string String422 = "CoordinatorRegistrationFailed"; + public const string String423 = "TooManyEnlistments"; + public const string String424 = "Disabled"; + public const string String425 = "ActivityId"; + public const string String426 = "http://schemas.microsoft.com/2004/09/ServiceModel/Diagnostics"; + public const string String427 = "http://docs.oasis-open.org/wss/oasis-wss-kerberos-token-profile-1.1#Kerberosv5APREQSHA1"; + public const string String428 = "http://schemas.xmlsoap.org/ws/2002/12/policy"; + public const string String429 = "FloodMessage"; + public const string String430 = "LinkUtility"; + public const string String431 = "Hops"; + public const string String432 = "http://schemas.microsoft.com/net/2006/05/peer/HopCount"; + public const string String433 = "PeerVia"; + public const string String434 = "http://schemas.microsoft.com/net/2006/05/peer"; + public const string String435 = "PeerFlooder"; + public const string String436 = "PeerTo"; + public const string String437 = "http://schemas.microsoft.com/ws/2005/05/routing"; + public const string String438 = "PacketRoutable"; + public const string String439 = "http://schemas.microsoft.com/ws/2005/05/addressing/none"; + public const string String440 = "http://schemas.microsoft.com/ws/2005/05/envelope/none"; + public const string String441 = "http://www.w3.org/2001/XMLSchema-instance"; + public const string String442 = "http://www.w3.org/2001/XMLSchema"; + public const string String443 = "nil"; + public const string String444 = "type"; + public const string String445 = "char"; + public const string String446 = "boolean"; + public const string String447 = "byte"; + public const string String448 = "unsignedByte"; + public const string String449 = "short"; + public const string String450 = "unsignedShort"; + public const string String451 = "int"; + public const string String452 = "unsignedInt"; + public const string String453 = "long"; + public const string String454 = "unsignedLong"; + public const string String455 = "float"; + public const string String456 = "double"; + public const string String457 = "decimal"; + public const string String458 = "dateTime"; + public const string String459 = "string"; + public const string String460 = "base64Binary"; + public const string String461 = "anyType"; + public const string String462 = "duration"; + public const string String463 = "guid"; + public const string String464 = "anyURI"; + public const string String465 = "QName"; + public const string String466 = "time"; + public const string String467 = "date"; + public const string String468 = "hexBinary"; + public const string String469 = "gYearMonth"; + public const string String470 = "gYear"; + public const string String471 = "gMonthDay"; + public const string String472 = "gDay"; + public const string String473 = "gMonth"; + public const string String474 = "integer"; + public const string String475 = "positiveInteger"; + public const string String476 = "negativeInteger"; + public const string String477 = "nonPositiveInteger"; + public const string String478 = "nonNegativeInteger"; + public const string String479 = "normalizedString"; + public const string String480 = "ConnectionLimitReached"; + public const string String481 = "http://schemas.xmlsoap.org/soap/envelope/"; + public const string String482 = "actor"; + public const string String483 = "faultcode"; + public const string String484 = "faultstring"; + public const string String485 = "faultactor"; + public const string String486 = "detail"; + + public override int Count { get { return 487; } } + + public override string this[int index] + { + get + { + DiagnosticUtility.DebugAssert(index >= 0 && index < this.Count, "check index"); + switch (index) + { + case 0: return String0; + case 1: return String1; + case 2: return String2; + case 3: return String3; + case 4: return String4; + case 5: return String5; + case 6: return String6; + case 7: return String7; + case 8: return String8; + case 9: return String9; + case 10: return String10; + case 11: return String11; + case 12: return String12; + case 13: return String13; + case 14: return String14; + case 15: return String15; + case 16: return String16; + case 17: return String17; + case 18: return String18; + case 19: return String19; + case 20: return String20; + case 21: return String21; + case 22: return String22; + case 23: return String23; + case 24: return String24; + case 25: return String25; + case 26: return String26; + case 27: return String27; + case 28: return String28; + case 29: return String29; + case 30: return String30; + case 31: return String31; + case 32: return String32; + case 33: return String33; + case 34: return String34; + case 35: return String35; + case 36: return String36; + case 37: return String37; + case 38: return String38; + case 39: return String39; + case 40: return String40; + case 41: return String41; + case 42: return String42; + case 43: return String43; + case 44: return String44; + case 45: return String45; + case 46: return String46; + case 47: return String47; + case 48: return String48; + case 49: return String49; + case 50: return String50; + case 51: return String51; + case 52: return String52; + case 53: return String53; + case 54: return String54; + case 55: return String55; + case 56: return String56; + case 57: return String57; + case 58: return String58; + case 59: return String59; + case 60: return String60; + case 61: return String61; + case 62: return String62; + case 63: return String63; + case 64: return String64; + case 65: return String65; + case 66: return String66; + case 67: return String67; + case 68: return String68; + case 69: return String69; + case 70: return String70; + case 71: return String71; + case 72: return String72; + case 73: return String73; + case 74: return String74; + case 75: return String75; + case 76: return String76; + case 77: return String77; + case 78: return String78; + case 79: return String79; + case 80: return String80; + case 81: return String81; + case 82: return String82; + case 83: return String83; + case 84: return String84; + case 85: return String85; + case 86: return String86; + case 87: return String87; + case 88: return String88; + case 89: return String89; + case 90: return String90; + case 91: return String91; + case 92: return String92; + case 93: return String93; + case 94: return String94; + case 95: return String95; + case 96: return String96; + case 97: return String97; + case 98: return String98; + case 99: return String99; + case 100: return String100; + case 101: return String101; + case 102: return String102; + case 103: return String103; + case 104: return String104; + case 105: return String105; + case 106: return String106; + case 107: return String107; + case 108: return String108; + case 109: return String109; + case 110: return String110; + case 111: return String111; + case 112: return String112; + case 113: return String113; + case 114: return String114; + case 115: return String115; + case 116: return String116; + case 117: return String117; + case 118: return String118; + case 119: return String119; + case 120: return String120; + case 121: return String121; + case 122: return String122; + case 123: return String123; + case 124: return String124; + case 125: return String125; + case 126: return String126; + case 127: return String127; + case 128: return String128; + case 129: return String129; + case 130: return String130; + case 131: return String131; + case 132: return String132; + case 133: return String133; + case 134: return String134; + case 135: return String135; + case 136: return String136; + case 137: return String137; + case 138: return String138; + case 139: return String139; + case 140: return String140; + case 141: return String141; + case 142: return String142; + case 143: return String143; + case 144: return String144; + case 145: return String145; + case 146: return String146; + case 147: return String147; + case 148: return String148; + case 149: return String149; + case 150: return String150; + case 151: return String151; + case 152: return String152; + case 153: return String153; + case 154: return String154; + case 155: return String155; + case 156: return String156; + case 157: return String157; + case 158: return String158; + case 159: return String159; + case 160: return String160; + case 161: return String161; + case 162: return String162; + case 163: return String163; + case 164: return String164; + case 165: return String165; + case 166: return String166; + case 167: return String167; + case 168: return String168; + case 169: return String169; + case 170: return String170; + case 171: return String171; + case 172: return String172; + case 173: return String173; + case 174: return String174; + case 175: return String175; + case 176: return String176; + case 177: return String177; + case 178: return String178; + case 179: return String179; + case 180: return String180; + case 181: return String181; + case 182: return String182; + case 183: return String183; + case 184: return String184; + case 185: return String185; + case 186: return String186; + case 187: return String187; + case 188: return String188; + case 189: return String189; + case 190: return String190; + case 191: return String191; + case 192: return String192; + case 193: return String193; + case 194: return String194; + case 195: return String195; + case 196: return String196; + case 197: return String197; + case 198: return String198; + case 199: return String199; + case 200: return String200; + case 201: return String201; + case 202: return String202; + case 203: return String203; + case 204: return String204; + case 205: return String205; + case 206: return String206; + case 207: return String207; + case 208: return String208; + case 209: return String209; + case 210: return String210; + case 211: return String211; + case 212: return String212; + case 213: return String213; + case 214: return String214; + case 215: return String215; + case 216: return String216; + case 217: return String217; + case 218: return String218; + case 219: return String219; + case 220: return String220; + case 221: return String221; + case 222: return String222; + case 223: return String223; + case 224: return String224; + case 225: return String225; + case 226: return String226; + case 227: return String227; + case 228: return String228; + case 229: return String229; + case 230: return String230; + case 231: return String231; + case 232: return String232; + case 233: return String233; + case 234: return String234; + case 235: return String235; + case 236: return String236; + case 237: return String237; + case 238: return String238; + case 239: return String239; + case 240: return String240; + case 241: return String241; + case 242: return String242; + case 243: return String243; + case 244: return String244; + case 245: return String245; + case 246: return String246; + case 247: return String247; + case 248: return String248; + case 249: return String249; + case 250: return String250; + case 251: return String251; + case 252: return String252; + case 253: return String253; + case 254: return String254; + case 255: return String255; + case 256: return String256; + case 257: return String257; + case 258: return String258; + case 259: return String259; + case 260: return String260; + case 261: return String261; + case 262: return String262; + case 263: return String263; + case 264: return String264; + case 265: return String265; + case 266: return String266; + case 267: return String267; + case 268: return String268; + case 269: return String269; + case 270: return String270; + case 271: return String271; + case 272: return String272; + case 273: return String273; + case 274: return String274; + case 275: return String275; + case 276: return String276; + case 277: return String277; + case 278: return String278; + case 279: return String279; + case 280: return String280; + case 281: return String281; + case 282: return String282; + case 283: return String283; + case 284: return String284; + case 285: return String285; + case 286: return String286; + case 287: return String287; + case 288: return String288; + case 289: return String289; + case 290: return String290; + case 291: return String291; + case 292: return String292; + case 293: return String293; + case 294: return String294; + case 295: return String295; + case 296: return String296; + case 297: return String297; + case 298: return String298; + case 299: return String299; + case 300: return String300; + case 301: return String301; + case 302: return String302; + case 303: return String303; + case 304: return String304; + case 305: return String305; + case 306: return String306; + case 307: return String307; + case 308: return String308; + case 309: return String309; + case 310: return String310; + case 311: return String311; + case 312: return String312; + case 313: return String313; + case 314: return String314; + case 315: return String315; + case 316: return String316; + case 317: return String317; + case 318: return String318; + case 319: return String319; + case 320: return String320; + case 321: return String321; + case 322: return String322; + case 323: return String323; + case 324: return String324; + case 325: return String325; + case 326: return String326; + case 327: return String327; + case 328: return String328; + case 329: return String329; + case 330: return String330; + case 331: return String331; + case 332: return String332; + case 333: return String333; + case 334: return String334; + case 335: return String335; + case 336: return String336; + case 337: return String337; + case 338: return String338; + case 339: return String339; + case 340: return String340; + case 341: return String341; + case 342: return String342; + case 343: return String343; + case 344: return String344; + case 345: return String345; + case 346: return String346; + case 347: return String347; + case 348: return String348; + case 349: return String349; + case 350: return String350; + case 351: return String351; + case 352: return String352; + case 353: return String353; + case 354: return String354; + case 355: return String355; + case 356: return String356; + case 357: return String357; + case 358: return String358; + case 359: return String359; + case 360: return String360; + case 361: return String361; + case 362: return String362; + case 363: return String363; + case 364: return String364; + case 365: return String365; + case 366: return String366; + case 367: return String367; + case 368: return String368; + case 369: return String369; + case 370: return String370; + case 371: return String371; + case 372: return String372; + case 373: return String373; + case 374: return String374; + case 375: return String375; + case 376: return String376; + case 377: return String377; + case 378: return String378; + case 379: return String379; + case 380: return String380; + case 381: return String381; + case 382: return String382; + case 383: return String383; + case 384: return String384; + case 385: return String385; + case 386: return String386; + case 387: return String387; + case 388: return String388; + case 389: return String389; + case 390: return String390; + case 391: return String391; + case 392: return String392; + case 393: return String393; + case 394: return String394; + case 395: return String395; + case 396: return String396; + case 397: return String397; + case 398: return String398; + case 399: return String399; + case 400: return String400; + case 401: return String401; + case 402: return String402; + case 403: return String403; + case 404: return String404; + case 405: return String405; + case 406: return String406; + case 407: return String407; + case 408: return String408; + case 409: return String409; + case 410: return String410; + case 411: return String411; + case 412: return String412; + case 413: return String413; + case 414: return String414; + case 415: return String415; + case 416: return String416; + case 417: return String417; + case 418: return String418; + case 419: return String419; + case 420: return String420; + case 421: return String421; + case 422: return String422; + case 423: return String423; + case 424: return String424; + case 425: return String425; + case 426: return String426; + case 427: return String427; + case 428: return String428; + case 429: return String429; + case 430: return String430; + case 431: return String431; + case 432: return String432; + case 433: return String433; + case 434: return String434; + case 435: return String435; + case 436: return String436; + case 437: return String437; + case 438: return String438; + case 439: return String439; + case 440: return String440; + case 441: return String441; + case 442: return String442; + case 443: return String443; + case 444: return String444; + case 445: return String445; + case 446: return String446; + case 447: return String447; + case 448: return String448; + case 449: return String449; + case 450: return String450; + case 451: return String451; + case 452: return String452; + case 453: return String453; + case 454: return String454; + case 455: return String455; + case 456: return String456; + case 457: return String457; + case 458: return String458; + case 459: return String459; + case 460: return String460; + case 461: return String461; + case 462: return String462; + case 463: return String463; + case 464: return String464; + case 465: return String465; + case 466: return String466; + case 467: return String467; + case 468: return String468; + case 469: return String469; + case 470: return String470; + case 471: return String471; + case 472: return String472; + case 473: return String473; + case 474: return String474; + case 475: return String475; + case 476: return String476; + case 477: return String477; + case 478: return String478; + case 479: return String479; + case 480: return String480; + case 481: return String481; + case 482: return String482; + case 483: return String483; + case 484: return String484; + case 485: return String485; + case 486: return String486; + default: return null; + } + } + } + } +} diff --git a/src/CoreWCF.Http/src/CoreWCF/WSAddressing10ProblemHeaderQNameFault.cs b/src/CoreWCF.Http/src/CoreWCF/WSAddressing10ProblemHeaderQNameFault.cs new file mode 100644 index 000000000..7a65d1fb7 --- /dev/null +++ b/src/CoreWCF.Http/src/CoreWCF/WSAddressing10ProblemHeaderQNameFault.cs @@ -0,0 +1,149 @@ +using CoreWCF.Channels; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Xml; + +namespace CoreWCF +{ + class WSAddressing10ProblemHeaderQNameFault : MessageFault + { + FaultCode code; + FaultReason reason; + string actor; + string node; + string invalidHeaderName; + + public WSAddressing10ProblemHeaderQNameFault(MessageHeaderException e) + { + invalidHeaderName = e.HeaderName; + + if (e.IsDuplicate) + { + code = FaultCode.CreateSenderFaultCode( + new FaultCode(Addressing10Strings.InvalidAddressingHeader, + AddressingVersion.WSAddressing10.Namespace(), + new FaultCode(Addressing10Strings.InvalidCardinality, + AddressingVersion.WSAddressing10.Namespace()))); + } + else + { + code = FaultCode.CreateSenderFaultCode( + new FaultCode(Addressing10Strings.MessageAddressingHeaderRequired, + AddressingVersion.WSAddressing10.Namespace())); + } + + reason = new FaultReason(new FaultReasonText(e.Message, CultureInfo.CurrentCulture.Name)); + actor = ""; + node = ""; + } + + public WSAddressing10ProblemHeaderQNameFault(ActionMismatchAddressingException e) + { + invalidHeaderName = AddressingStrings.Action; + code = FaultCode.CreateSenderFaultCode( + new FaultCode(Addressing10Strings.ActionMismatch, AddressingVersion.WSAddressing10.Namespace())); + reason = new FaultReason(new FaultReasonText(e.Message, CultureInfo.CurrentCulture.Name)); + actor = ""; + node = ""; + } + + public override string Actor + { + get + { + return actor; + } + } + + public override FaultCode Code + { + get + { + return code; + } + } + + public override bool HasDetail + { + get + { + return true; + } + } + + public override string Node + { + get + { + return node; + } + } + + public override FaultReason Reason + { + get + { + return reason; + } + } + + protected override void OnWriteDetail(XmlDictionaryWriter writer, EnvelopeVersion version) + { + if (version == EnvelopeVersion.Soap12) // Soap11 wants the detail in the header + { + OnWriteStartDetail(writer, version); + OnWriteDetailContents(writer); + writer.WriteEndElement(); + } + } + + protected override void OnWriteDetailContents(XmlDictionaryWriter writer) + { + writer.WriteStartElement(Addressing10Strings.ProblemHeaderQName, AddressingVersion.WSAddressing10.Namespace()); + writer.WriteQualifiedName(invalidHeaderName, AddressingVersion.WSAddressing10.Namespace()); + writer.WriteEndElement(); + } + + public void AddHeaders(MessageHeaders headers) + { + if (headers.MessageVersion.Envelope == EnvelopeVersion.Soap11) + { + headers.Add(new WSAddressing10ProblemHeaderQNameHeader(invalidHeaderName)); + } + } + + class WSAddressing10ProblemHeaderQNameHeader : MessageHeader + { + string invalidHeaderName; + + public WSAddressing10ProblemHeaderQNameHeader(string invalidHeaderName) + { + this.invalidHeaderName = invalidHeaderName; + } + + public override string Name + { + get { return Addressing10Strings.FaultDetail; } + } + + public override string Namespace + { + get { return AddressingVersion.WSAddressing10.Namespace(); } + } + + protected override void OnWriteStartHeader(XmlDictionaryWriter writer, MessageVersion messageVersion) + { + writer.WriteStartElement(Name, Namespace); + } + + protected override void OnWriteHeaderContents(XmlDictionaryWriter writer, MessageVersion messageVersion) + { + writer.WriteStartElement(Addressing10Strings.ProblemHeaderQName, Namespace); + writer.WriteQualifiedName(invalidHeaderName, Namespace); + writer.WriteEndElement(); + } + } + } +} diff --git a/src/CoreWCF.Http/src/CoreWCF/WSMessageEncoding.cs b/src/CoreWCF.Http/src/CoreWCF/WSMessageEncoding.cs new file mode 100644 index 000000000..cc5d46d80 --- /dev/null +++ b/src/CoreWCF.Http/src/CoreWCF/WSMessageEncoding.cs @@ -0,0 +1,7 @@ +namespace CoreWCF +{ + public enum WSMessageEncoding + { + Text = 0, + } +} \ No newline at end of file diff --git a/src/CoreWCF.Http/src/CoreWCF/XD.cs b/src/CoreWCF.Http/src/CoreWCF/XD.cs new file mode 100644 index 000000000..d54c071b2 --- /dev/null +++ b/src/CoreWCF.Http/src/CoreWCF/XD.cs @@ -0,0 +1,64 @@ +using System; + +namespace CoreWCF +{ + static class AddressingStrings + { + // Main dictionary strings + public const string Action = ServiceModelStringsVersion1.String5; + public const string To = ServiceModelStringsVersion1.String6; + public const string RelatesTo = ServiceModelStringsVersion1.String9; + public const string MessageId = ServiceModelStringsVersion1.String13; + public const string Address = ServiceModelStringsVersion1.String21; + public const string ReplyTo = ServiceModelStringsVersion1.String22; + public const string Empty = ServiceModelStringsVersion1.String81; + public const string From = ServiceModelStringsVersion1.String82; + public const string FaultTo = ServiceModelStringsVersion1.String83; + public const string EndpointReference = ServiceModelStringsVersion1.String84; + public const string PortType = ServiceModelStringsVersion1.String85; + public const string ServiceName = ServiceModelStringsVersion1.String86; + public const string PortName = ServiceModelStringsVersion1.String87; + public const string ReferenceProperties = ServiceModelStringsVersion1.String88; + public const string RelationshipType = ServiceModelStringsVersion1.String89; + public const string Reply = ServiceModelStringsVersion1.String90; + public const string Prefix = ServiceModelStringsVersion1.String91; + public const string IdentityExtensionNamespace = ServiceModelStringsVersion1.String92; + public const string Identity = ServiceModelStringsVersion1.String93; + public const string Spn = ServiceModelStringsVersion1.String94; + public const string Upn = ServiceModelStringsVersion1.String95; + public const string Rsa = ServiceModelStringsVersion1.String96; + public const string Dns = ServiceModelStringsVersion1.String97; + public const string X509v3Certificate = ServiceModelStringsVersion1.String98; + public const string ReferenceParameters = ServiceModelStringsVersion1.String100; + public const string IsReferenceParameter = ServiceModelStringsVersion1.String101; + // String constants + public const string EndpointUnavailable = "EndpointUnavailable"; + public const string ActionNotSupported = "ActionNotSupported"; + public const string EndpointReferenceType = "EndpointReferenceType"; + public const string Request = "Request"; + public const string DestinationUnreachable = "DestinationUnreachable"; + public const string AnonymousUri = "http://schemas.microsoft.com/2005/12/ServiceModel/Addressing/Anonymous"; + public const string NoneUri = "http://schemas.microsoft.com/2005/12/ServiceModel/Addressing/None"; + public const string IndigoNamespace = "http://schemas.microsoft.com/serviceModel/2004/05/addressing"; + public const string ChannelTerminated = "ChannelTerminated"; + } + + static class Addressing10Strings + { + // Main dictionary strings + public const string Namespace = ServiceModelStringsVersion1.String3; + public const string Anonymous = ServiceModelStringsVersion1.String10; + public const string FaultAction = ServiceModelStringsVersion1.String99; + public const string ReplyRelationship = ServiceModelStringsVersion1.String102; + public const string NoneAddress = ServiceModelStringsVersion1.String103; + public const string Metadata = ServiceModelStringsVersion1.String104; + // String constants + public const string MessageAddressingHeaderRequired = "MessageAddressingHeaderRequired"; + public const string InvalidAddressingHeader = "InvalidAddressingHeader"; + public const string InvalidCardinality = "InvalidCardinality"; + public const string ActionMismatch = "ActionMismatch"; + public const string ProblemHeaderQName = "ProblemHeaderQName"; + public const string FaultDetail = "FaultDetail"; + public const string DefaultFaultAction = "http://www.w3.org/2005/08/addressing/soap/fault"; + } +} diff --git a/src/CoreWCF.Http/src/Resources/Strings.resx b/src/CoreWCF.Http/src/Resources/Strings.resx new file mode 100644 index 000000000..0c1b28131 --- /dev/null +++ b/src/CoreWCF.Http/src/Resources/Strings.resx @@ -0,0 +1,7941 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + No IPEndpoints were found for host {0}. + + + No DNS entries exist for host {0}. + + + Attribute '{0}' is required on element '{1}'. + + + Crypto algorithm {0} not supported in this context. + + + The custom crypto algorithm '{0}' obtained using CryptoConfig is not a valid or supported hash algorithm. + + + The client credential entered was invalid. + + + Either the client credential was invalid or there was an error collecting the client credentials by the SSPI. + + + The custom crypto algorithm '{0}' obtained using CryptoConfig is not a valid or supported asymmetric signature algorithm. + + + The security token serializer must be specified on the security token provider. + + + The key length '{0}' is not a multiple of 8 for symmetric keys. + + + The channel behaviors configured for the issuer address '{0}' cannot contain a behavior of type '{1}'. + + + Operation Action={0} + + + The security token manager cannot create a token provider for requirement '{0}'. + + + The security token manager cannot create a token authenticator for requirement '{0}'. + + + The signature verification failed. Please see inner exception for fault details. + + + The security token manager cannot create a token serializer for security token version '{0}'. + + + The supporting signature is not signed with a derived key. The binding's supporting token parameter '{0}' requires key derivation. + + + The primary signature is not signed with a derived key. The binding's primary token parameter '{0}' requires key derivation. + + + The primary signature is not signed with a key derived from the encrypted key. The binding's token parameter '{0}' requires key derivation. + + + The message is not encrypted with a key derived from the encrypted key. The binding's token parameter '{0}' requires key derivation. + + + The DataProtectionSecurityStateEncoder is unable to decode the byte array. Ensure that a 'UserProfile' is loaded, if this is a 'web farm scenario' ensure all servers are running as the same user with the roaming profiles or provide a custom SecurityStateEncoder'. + + + The DataProtectionSecurityStateEncoder is unable to encode the byte array. Ensure that a 'UserProfile' is loaded, if this is a 'web farm scenario' ensure all servers are running as the same user with the roaming profiles or provide a custom SecurityStateEncoder'. + + + The message is not encrypted with a key derived from the encryption token. The binding's token parameter '{0}' requires key derivation. + + + The security token manager requires the security binding element to be specified in order to create a token authenticator for requirement '{0}'. + + + The security token manager requires the security binding element to be specified in order to create a token provider for requirement '{0}'. + + + The security session received an unexpected close response from the other party. + + + The security session received an unexpected close from the other party. + + + The service was unable to verify the cipher strengths negotiated as part of the SSL handshake. + + + SecurityVersion.WSSecurityJan2004 does not support header encryption. Header with name '{0}' and namespace '{1}' is configured for encryption. Consider using SecurityVersion.WsSecurity11 and above or use transport security to encrypt the full message. + + + The Header ('{0}', '{1}') was encrypted but not signed. All encrypted headers outside the security header should be signed. + + + Unable to obtain XmlDictionaryReaderQuotas from the Binding. If you have specified a custom EncodingBindingElement, verify that the EncodingBindingElement can handle XmlDictionaryReaderQuotas in its GetProperty<T>() method. + + + SecurityVersion.WSSecurityJan2004 does not support header decryption. Use SecurityVersion.WsSecurity11 and above or use transport security to encrypt the full message. + + + Unable to decrypt an encrypted data block. Please verify that the encryption algorithm and keys used by the sender and receiver match. + + + The authenticate method in the ServiceAuthenticationManager returned null. If you do not want to return any authorization policies in the collection then return an empty ReadOnlyCollection instead. + + + There was an error serializing the security token. Please see the inner exception for more details. + + + There was an error creating the security key identifier clause from the security token XML. Please see the inner exception for more details. + + + There was an error deserializing the security token XML. Please see the inner exception for more details. + + + The token requirement '{0}' does not specify the target address. This is required by the token manager for creating the corresponding security token provider. + + + The derived key has not been computed for the security token. + + + The binding ('{0}', '{1}') has been configured with a security algorithm suite '{2}' that is incompatible with the issued token key size '{3}' specified on the binding. + + + The IssuedToken security authentication mode requires the issued token to contain a symmetric key. + + + The binding ('{0}', '{1}') uses an Issued Token with Bearer Key Type in a invalid context. The Issued Token with a Bearer Key Type can only be used as a Signed Supporting token or a Signed Encrypted Supporting token. See the SecurityBindingElement.EndpointSupportingTokenParameters property. + + + Policy for multiple issuer endpoints was retrieved from '{0}' but the relying party's policy does not specify which issuer endpoint to use. One of the endpoints was selected as the issuer endpoint to use. If you are using svcutil, the other endpoints will be available in commented form in the configuration as <alternativeIssuedTokenParameters>. Check the configuration to ensure that the right issuer endpoint was selected. + + + The AuthenticationManager cannot be added to the binding parameters because the binding parameters already contains a AuthenticationManager '{0}'. If you are configuring a custom AuthenticationManager for the service, please first remove any existing AuthenticationManagers from the behaviors collection before adding the custom AuthenticationManager. + + + The AuthenticationSchemes cannot be added to the binding parameters because the binding parameters already contains AuthenticationSchemes '{0}'. If you are configuring custom AuthenticationSchemes for the service, please first remove any existing AuthenticationSchemes from the behaviors collection before adding custom AuthenticationSchemes. + + + Unable to find a SecurityBindingElement. + + + The ServiceCredentials cannot be added to the binding parameters because the binding parameters already contains a SecurityCredentialsManager '{0}'. If you are configuring custom credentials for the service, please first remove any existing ServiceCredentials from the behaviors collection before adding the custom credential. + + + The ClientCredentials cannot be added to the binding parameters because the binding parameters already contains a SecurityCredentialsManager '{0}'. If you are configuring custom credentials for the channel, please first remove any existing ClientCredentials from the behaviors collection before adding the custom credential. + + + The binding ('{0}', '{1}') has been configured with a MutualCertificateDuplexBindingElement that requires a client certificate. The client certificate is currently missing. + + + The binding ('{0}', '{1}') is configured with a security token parameter '{2}' that has an incompatible security token inclusion mode '{3}'. Specify an alternate security token inclusion mode (for example, '{4}'). + + + Unable to create a bi-directional (request-reply or duplex) channel for security negotiation. Please ensure that the binding is capable of creating a bi-directional channel. + + + There are too many active security negotiations or secure conversations at the service. Please retry later. + + + There are too many pending secure conversations on the server. Please retry later. + + + The RequestSecurityToken message does not match the endpoint filters the service '{0}' is expecting incoming messages to match. This may be because the RequestSecurityToken was intended to be sent to a different service. + + + The security session requires a security token authenticator that implements '{0}'. '{1}' does not implement '{0}'. + + + The security session requires a security token resolver that implements '{1}'. The security token resolver '{0}' does not implement '{1}'. + + + The session security token authenticator returned a token of type '{0}'. The token type expected is '{1}'. + + + The session security token provider returned a token of type '{0}'. The token type expected is '{1}'. + + + The security standards manager was not specified on '{0}'. + + + The security negotiation message with action '{0}' is larger than the maximum allowed buffer size '{1}'. If you are using a streamed transport consider increasing the maximum buffer size on the transport. + + + The channel demuxer Open failed previously with exception '{0}'. + + + The security channel listener was not specified on '{0}'. + + + ExtendedProtectionPolicy specified a PolicyEnforcement of 'Always' which is not supported for the authentication mode requested. This prevents the ExtendedProtectionPolicy from being enforced. For StandardBindings use a SecurityMode of TransportWithMessageCredential and a ClientCredential type of Windows. For CustomBindings use SspiNegotiationOverTransport or KerberosOverTransport. Alternatively, specify a PolicyEnforcement of 'Never'. + + + ExtendedProtectionPolicy specified a PolicyEnforcement of 'Always' and a ChannelBinding was not found. This prevents the ExtendedProtectionPolicy from being enforced. Change the binding to make a ChannelBinding available, for StandardBindings use a SecurityMode of TransportWithMessageCredential and a ClientCredential type of Windows. For CustomBindings use SspiNegotiationOverTransport or KerberosOverTransport. Alternatively, specify a PolicyEnforcement of 'Never'. + + + The security settings lifetime manager was not specified on '{0}'. + + + The listener is not accepting new secure conversations because it is closing. + + + The server is not accepting new secure conversations currently because it is closing. Please retry later. + + + The cipher key negotiated by SSL is too small ('{0}' bits). Keys of such lengths are not allowed as they may result in information disclosure. Please configure the initiator machine to negotiate SSL cipher keys that are '{1}' bits or longer. + + + The length ('{0}' bytes) of the derived key's Nonce exceeds the maximum length ('{1}' bytes) allowed. + + + The length ('{0}' bytes) of the derived key's Label exceeds the maximum length ('{1}' bytes) allowed. + + + The derived key's Offset ('{0}' bytes) exceeds the maximum offset ('{1}' bytes) allowed. + + + The derived key's generation ('{0}') and length ('{1}' bytes) result in a key derivation offset that is greater than the maximum offset ('{2}' bytes) allowed. + + + The number of derived keys in the message has exceeded the maximum allowed number '{0}'. + + + The number of encrypted keys in the message has exceeded the maximum allowed number '{0}'. + + + Unable to finish reading Base64 data as the given buffer quota has been exceeded. Buffer quota: {0}. Consider increasing the MaxReceivedMessageSize quota on the TransportBindingElement. Please note that a very high value for MaxReceivedMessageSize will result in buffering a large message and might open the system to DOS attacks. + + + Manual addressing is not supported with message level security. Configure the binding ('{0}', '{1}') to use transport security or to not do manual addressing. + + + The target service address was not specified on '{0}'. + + + The issued token cache was not specified on '{0}'. + + + The security algorithm suite was not specified on '{0}'. + + + A security token ('{0}', '{1}') was found outside the security header. The message may have been altered in transit. + + + The SecurityTokenProvider '{0}' could not resolve the token. + + + A secure conversation cancellation is not allowed by the binding. + + + The security binding element for bootstrap security was not specified on '{0}'. + + + The context for building the issuer channel was not specified on '{0}'. + + + The binding to use to communicate to the federation service at '{0}' is not specified. + + + It is likely that certificate '{0}' may not have a private key that is capable of key exchange or the process may not have access rights for the private key. Please see inner exception for detail. + + + The certificate '{0}' must have a private key. The process must have access rights for the private key. + + + No outgoing EndpointAddress is available to check the identity on a message to be sent. + + + No outgoing EndpointAddress is available to check the identity on a received reply. + + + No signing token is available to do an incoming identity check. + + + The PSHA1 key length '{0}' is invalid. + + + Clone() was not implemented properly by '{0}'. The cloned object was '{1}'. + + + The issued token is of unexpected type '{0}'. Expected token type '{1}'. + + + The service operation '{0}' that belongs to the contract with the '{1}' name and the '{2}' namespace does not allow impersonation. + + + The RequestSecurityTokenResponse has multiple RequestedSecurityToken elements. + + + The RequestSecurityTokenResponse has multiple RequestedProofToken elements. + + + The proof token XML element is not expected in the response. + + + The key length '{0}' requested is invalid. + + + The security token parameters to use for the issued token are not set on '{0}'. + + + The message could not be processed because the action '{0}' is invalid or unrecognized. + + + Token inclusion mode '{0}' is not supported. + + + The policy to import a process cannot import a binding for contract ({0},{1}). The protection requirements for the binding are not compatible with a binding already imported for the contract. You must reconfigure the binding. + + + The symmetric security protocol can either be configured with a symmetric token provider and a symmetric token authenticator or an asymmetric token provider. It cannot be configured with both. + + + ClientCredentialType.None is not valid for the TransportWithMessageCredential security mode. Specify a message credential type or use a different security mode. + + + The security session id '{0}' is already present in the filter table. + + + A supporting token that satisfies parameters '{0}' and attachment mode '{1}' was not provided. + + + The supporting token provided for parameters '{0}' did not endorse the primary signature. + + + The supporting token provided for parameters '{0}' was not signed as part of the primary signature. + + + The supporting token provided for parameters '{0}' was not encrypted. + + + A basic token is not expected in the security header in this context. + + + The request for security token could not be satisfied because authentication failed. + + + The caller was not authenticated by the service. + + + The request for security token has invalid or malformed elements. + + + A signed supporting token is not expected in the security header in this context. + + + Security token parameters must be specified with supporting tokens for each message. + + + The signature token '{0}' is not the same token as the encryption token '{1}'. + + + The reverting operation failed with the exception '{0}'. + + + Unrecognized supporting token '{0}' was encountered. + + + More than one supporting signature was encountered using the same supporting token '{0}'. + + + An unsecured or incorrectly secured fault was received from the other party. See the inner FaultException for the fault code and detail. + + + At least one security token in the message could not be validated. + + + The message could not be processed. This is most likely because the action '{0}' is incorrect or because the message contains an invalid or expired security context token or because there is a mismatch between bindings. The security context token would be invalid if the service aborted the channel due to inactivity. To prevent the service from aborting idle sessions prematurely increase the Receive timeout on the service endpoint's binding. + + + The security context token is expired or is not valid. The message was not processed. + + + Transport security negotiation failed due to an underlying IO error: {0}. + + + The security negotiation with '{0}' cannot be initiated because the confidential endpoint address header ('{1}', '{2}') cannot be encrypted during the course of the negotiation. + + + An error occurred when processing the security tokens in the message. + + + An error occurred when verifying security for the message. + + + The service does not allow you to log on anonymously. + + + Obtaining metadata from issuer '{0}' failed with error '{1}'. + + + Importing metadata from issuer '{0}' failed with error '{1}'. + + + Multiple correlation tokens were found in the security correlation state. + + + No correlation token was found in the security correlation state. + + + Multiple supporting token authenticators with the token parameter type equal to '{0}' cannot be specified. If more than one Supporting Token of the same type is expected in the response, then configure the supporting token collection with just one entry for that SecurityTokenParameters. The SecurityTokenAuthenticator that gets created from the SecurityTokenParameters will be used to authenticate multiple tokens. It is not possible to add SecurityTokenParameters of the same type in the SupportingTokenParameters collection or repeat it across EndpointSupportingTokenParameters and OperationSupportingTokenParameters. + + + A leg of the federated security chain contains multiple IssuedSecurityTokenParameters. The InfoCard system only supports one IssuedSecurityTokenParameters for each leg. + + + An unrecognized token authenticator '{0}' was used for token processing. + + + The SecurityTokenParameters and SecurityToken tuple specified for use in the security header must both be null or must both be non-null. + + + The CloneCore method of {0} type returned an invalid result. + + + Certificate-based client authentication is not supported in TransportCredentialOnly security mode. Select the Transport security mode. + + + BasicHttp binding requires that BasicHttpBinding.Security.Message.ClientCredentialType be equivalent to the BasicHttpMessageCredentialType.Certificate credential type for secure messages. Select Transport or TransportWithMessageCredential security for UserName credentials. + + + The client must provide key entropy in key entropy mode '{0}'. + + + A Proof Token was found in the response that was returned by the Security Token Service for a Bearer Key Type token request. Note that Proof Tokens should not be generated when a Bearer Key Type request is made. + + + Bearer Key Type is not supported with WSFederationHttpBinding. Please use WS2007FederationHttpBinding. + + + Unable to create Key Type element for the Key Type '{0}'. This might be due to a wrong version of MessageSecurityVersion set on the SecurityBindingElement. + + + The issuer cannot provide key entropy or a proof token in key entropy mode '{0}'. + + + The client cannot provide key entropy in key entropy mode '{0}'. + + + The issuer must provide a proof token in key entropy mode '{0}'. + + + The issuer must provide a computed key in key entropy mode '{0}'. + + + The issuer must provide key entropy in key entropy mode '{0}'. + + + The issuer cannot provide a computed key in key entropy mode '{0}'. + + + The computed key algorithm '{0}' is not supported. + + + The ReplayWindow and ClockSkew cannot be the maximum possible value when replay detection is enabled. + + + The session channel must be opened before the session ID can be accessed. + + + The binding ('{0}','{1}') for contract ('{2}','{3}') has been configured with an incompatible security version that does not support unattached references to EncryptedKeys. Use '{4}' or higher as the security version for the binding. + + + The '{0}','{1}' binding for the '{2}','{3}' contract is configured with a security version that does not support external references to X.509 tokens using the certificate's thumbprint value. Use '{4}' or higher as the security version for the binding. + + + The SecurityBinding for the ('{0}','{1}') binding for the ('{2}','{3}') contract only supports the OneWay operation. + + + Cannot map Windows user '{0}' to a UserPrincipalName that can be used for S4U impersonation. + + + Resolving an External reference token requires appropriate SecurityTokenParameters to be specified. + + + The SecurityContextSecurityToken's key needs to be renewed. + + + The client's security session was not able to close its output session within the configured timeout ({0}). + + + Client is unable to finish the security negotiation within the configured timeout ({0}). The current negotiation leg is {1} ({2}). + + + Client is unable to request the security session within the configured timeout ({0}). + + + The service's security session was not able to close its output session within the configured timeout ({0}). + + + The service's security session did not receive a 'close' message from the client within the configured timeout ({0}). + + + The client's security session did not receive a 'close response' message from the service within the configured timeout ({0}). + + + Cannot renew the security session key. + + + Cannot renew the security session key. Session Key Renewal is not supported. + + + Error parsing SecurityContextSecurityToken Cookie XML. + + + The SecurityContextSecurityToken's Cookie element either does not contain '{0}' or has a wrong value for it. + + + Error decoding the Cookie element of SecurityContextSecurityToken. + + + Issuing cookie SecurityContextSecurityToken is not supported. + + + Security policy import failed. The security policy contains supporting token requirements at the operation scope. The contract description does not specify the action for the request message associated with this operation. + + + Signature confirmation is not expected in the security header. + + + The signature confirmation elements cannot occur after the primary signature. + + + Signature confirmation was expected to be present in the security header. + + + The SecurityVersion '{0}' does not support signature confirmation. Use a later SecurityVersion. + + + The protocol factory must support Request/Reply security in order to offer signature confirmation. + + + Not all the signatures in the request message were confirmed in the reply message. + + + The request did not have any signatures but the reply has signature confirmations. + + + There are too many renewed session keys that have not been used. + + + The session key must be renewed before it can secure application messages. + + + The token's crypto collection has multiple objects of type '{0}'. + + + The token's crypto collection does not support algorithm '{0}'. + + + SymmetricSecurityBindingElement cannot build a channel or listener factory. The ProtectionTokenParameters property is required but not set. Binding element configuration: {0} + + + AsymmetricSecurityBindingElement cannot build a channel or listener factory. The InitiatorTokenParameters property is required but not set. Binding element configuration: {0} + + + AsymmetricSecurityBindingElement cannot build a channel or listener factory. The RecipientTokenParameters property is required but not set. Binding element configuration: {0} + + + The service cannot cache the negotiation state as the capacity '{0}' has been reached. Retry the request. + + + Internal SSL error (refer to Win32 status code for details). Check the server certificate to determine if it is capable of key exchange. + + + The key rollover interval cannot be greater than the key renewal interval. + + + The request message must be protected. This is required by an operation of the contract ('{0}','{1}'). The protection must be provided by the binding ('{2}','{3}'). + + + The response message must be protected. This is required by an operation of the contract ('{0}', '{1}'). The protection must be provided by the binding ('{2}', '{3}'). + + + The contract ('{0}','{1}') contains some unknown header ('{2}','{3}') which cannot be secured. Please choose ProtectionLevel.None for this header. + + + The binding ('{0}','{1}') supports streaming which cannot be configured together with message level security. Consider choosing a different transfer mode or choosing the transport level security. + + + The supporting token in the renew message has a different generation '{0}' than the current session token's generation '{1}'. + + + Security Support Provider Interface (SSPI) authentication failed. The server may not be running in an account with identity '{0}'. If the server is running in a service account (Network Service for example), specify the account's ServicePrincipalName as the identity in the EndpointAddress for the server. If the server is running in a user account, specify the account's UserPrincipalName as the identity in the EndpointAddress for the server. + + + For this security protocol, the incoming signing token must be an EncryptedKey. + + + The security session was terminated This may be because no messages were received on the session for too long. + + + No AppliesTo element is present in the deserialized RequestSecurityToken/RequestSecurityTokenResponse. + + + Symmetric Key length {0} is not supported by the algorithm suite '{1}'. + + + For replay detection to be done ProtectionLevel must be Sign or EncryptAndSign. + + + Can't infer an external reference for '{0}' token type. + + + Unable to create Attached or Unattached reference for '{0}'. + + + The configured Trust version does not support sessions. Use WSTrustFeb2005 or above. + + + The configured WS-Trust version does not support issued tokens. WS-Trust February 2005 or later is required. + + + The binding ('{0}','{1}') for contract ('{2}','{3}') supports impersonation only on Windows 2003 Server and newer version of Windows. Use SspiNegotiated authentication and a binding with Secure Conversation with cancellation enabled. + + + Impersonation using the client token is not possible. The binding ('{0}', '{1}') for contract ('{2}', '{3}') uses the Username Security Token for client authentication with a Membership Provider registered. Use a different type of security token for the client. + + + Cannot establish a reliable session without secure conversation. Enable secure conversation. + + + Failed to revert impersonation. {0} + + + In order to flow a transaction, flowing issued tokens must also be supported. + + + The configured SecurityVersion does not support signature confirmation. Use WsSecurity11 or above. + + + The configured SecureConversation version does not support sessions. Use WSSecureConversationFeb2005 or above. + + + SOAP security negotiation failed. See inner exception for more details. + + + SOAP security negotiation with '{0}' for target '{1}' failed. See inner exception for more details. + + + The one-way operation returned a fault message. The reason for the fault was '{0}'. + + + The one-way operation returned a fault message with Action='{0}'. + + + The one-way operation returned a non-null message with Action='{0}'. + + + Cannot find the security session with the ID '{0}'. + + + The SecurityContextSecurityToken with Context-id={0} (generation-id={1}) has expired. + + + The SecurityContextSecurityToken with Context-id={0} (no key generation-id) has expired. + + + Security sessions require all messages to be signed. + + + Required timestamp missing in security header. + + + The request message in the request context received from channel '{0}' is null. + + + The key effective and expiration times must be bounded by the token effective and expiration times. + + + The valid from time is greater than the valid to time. + + + No session token was present in the message. + + + Key length '{0}' is not a multiple of 8 for symmetric keys. + + + Invalid binary representation of an X.509 certificate. + + + Security policy export failed. The binding contains a TransportSecurityBindingElement but no transport binding element that implements ITransportTokenAssertionProvider. Policy export for such a binding is not supported. Make sure the transport binding element in the binding implements the ITransportTokenAssertionProvider interface. + + + Cannot import the security policy. The protection requirements for the secure conversation bootstrap binding are not supported. Protection requirements for the secure conversation bootstrap must require both the request and the response to be signed and encrypted. + + + Cannot import the policy. The value of the attribute '{0}' must be either 'true', 'false', '1' or '0'. The following error occurred: '{1}'. + + + The security policy expert failed. The provided transport token assertion of type '{0}' did not create a transport token assertion to include the sp:TransportBinding security policy assertion. + + + Message security policy for the '{0}' action requires confidentiality without integrity. Confidentiality without integrity is not supported. + + + The primary signature must be encrypted. + + + A symmetric crypto could not be created from token '{0}'. + + + The key size requirements for the '{0}' algorithm suite are not met by the '{1}' token which has key size of '{2}'. + + + The received message does not meet the required message protection order '{0}'. + + + Primary signature must be computed before supporting token signatures. + + + Element to sign must have id. + + + The token Serializer cannot serialize '{0}'. If this is a custom type you must supply a custom serializer. + + + Signing without primary signature requires timestamp. + + + This operation cannot be done after processing is started. + + + The recursive policy fetching limit has been reached. Check to determine if there is a loop in the federation service chain. + + + The ('{0}', '{1}') signed header contains the ('{2}', '{3}') attribute. The expected attribute is ('{4}', '{5}'). + + + The address of the security token issuer is not specified. An explicit issuer address must be specified in the binding for target '{0}' or the local issuer address must be configured in the credentials. + + + More than one SecurityBindingElement found in the binding ('{0}', '{1}) for contract ('{2}', '{3}'). Only one SecurityBindingElement is allowed. + + + ClientCredentials cannot create a local token provider for token requirement {0}. + + + A security policy was imported for the endpoint. The security policy contains requirements that cannot be represented in a Windows Communication Foundation configuration. Look for a comment about the SecurityBindingElement parameters that are required in the configuration file that was generated. Create the correct binding element with code. The binding configuration that is in the configuration file is not secure. + + + The configuration schema is insufficient to describe the non-standard configuration of the following security binding element: + + + The wsdl schema that was used to create this configuration file contained a 'RequireIssuerSerialReference' assertion for a X509Token. This can not be represented in configuration, you will need to programatically adjust the appropriate X509SecurityTokenParameters.X509KeyIdentifierClauseType to X509KeyIdentifierClauseType.IssuerSerial. The default of X509KeyIdentifierClauseType.Thumbprint will be used, which may cause interop issues. + + + The security protocol '{0}' cannot do replay detection. + + + Security processor was unable to find a security header with actor '{0}' in the message. This might be because the message is an unsecured fault or because there is a binding mismatch between the communicating parties. This can occur if the service is configured for security and the client is not using security. + + + Security processor was unable to find a security header in the message. This might be because the message is an unsecured fault or because there is a binding mismatch between the communicating parties. This can occur if the service is configured for security and the client is not using security. + + + No primary signature available for supporting token signature verification. + + + Supporting token signatures not expected. + + + Cannot read the token from the '{0}' element with the '{1}' namespace for BinarySecretSecurityToken, with a '{2}' ValueType. If this element is expected to be valid, ensure that security is configured to consume tokens with the name, namespace and value type specified. + + + Element '{0}' with namespace '{1}' not found. + + + Expected element '{0}' or element '{1}' (from namespace '{2}'). + + + AcceleratedTokenAuthenticator does not expect RequestSecurityTokenResponse from the client. + + + The '{0}' binding with the '{1}' namespace is configured to issue cookie security context tokens. COM+ Integration services does not support cookie security context tokens. + + + The signature must be in the security header. + + + The '{0}' required message part was not signed. + + + The '{0}', '{1}' required message part was not signed. + + + The '{0}' required message part was not encrypted. + + + The '{0}', '{1}' required message part was not encrypted. + + + Signature verification failed. + + + Cannot issue the token type '{0}'. + + + There is no negotiation message to send. + + + The issued token has an invalid key size '{0}'. + + + Cannot determine the key size of the issued token. + + + The negotiation has not yet completed. + + + The negotiation has already completed. + + + Request Message is missing a MessageID header. One is required to correlate a reply. + + + Cannot create a security session. Retry later. + + + The security session with id '{0}' is already pending. + + + No security session with id '{0}' is pending. + + + No security session listener was found for message with action '{0}'. + + + The session token was not closed by the server. + + + '{0}' protocol can only be used by the Initiator. + + + '{0}' protocol can only be used at the Recipient. + + + Unexpected code path for server security application, sending outgoing message on Recipient. + + + Only body return values are supported currently for protection, MessagePartDescription was specified. + + + Unknown token attachment mode: {0}. + + + Security protocol must be '{0}', type is: '{1}'.; + + + The initial request context was already specified. Can not create two for same message. + + + {0}.OnCloseMessageReceived when state == Created. + + + Shutdown request was not received. + + + Unknown filter type: '{0}'. + + + Standards manager of filter does not match that of filter table. Can not have two different filters. + + + Session filter's isStrictMode differs from filter table's isStrictMode. + + + SecuritySessionServerSettings.CreateAcceptor, channelAcceptor must be null, can not create twice. + + + Invalid TransactionFlowOption value. + + + Security token manager could not parse token with name '{0}', namespace '{1}', valueType '{2}'. + + + Security negotiation message has incorrect action '{0}'. + + + The specified key size {0} is invalid. The key size must be between {1} and {2}. + + + Could not get token information (error=0x{0:X}). + + + Unexpected end of file. + + + The security timestamp is invalid because its creation time ('{0}') is greater than or equal to its expiration time ('{1}'). + + + The security timestamp is stale because its expiration time ('{0}') is in the past. Current time is '{1}' and allowed clock skew is '{2}'. + + + The security timestamp is invalid because its creation time ('{0}') is in the future. Current time is '{1}' and allowed clock skew is '{2}'. + + + The security timestamp is stale because its creation time ('{0}') is too far back in the past. Current time is '{1}', maximum timestamp lifetime is '{2}' and allowed clock skew is '{3}'. + + + The nonce is invalid or replayed. + + + Message part specification must be made constant before being set. + + + Issuer entropy is not BinarySecretSecurityToken or WrappedKeySecurityToken. + + + No RequestSecurityTokenResponse elements were found. + + + The SecurityContextSecurityToken does not have a cookie. + + + TokenProvider returned token of incorrect type '{0}'. + + + {0} is not available in deserialized RequestSecurityToken. + + + {0} is only available in a deserialized RequestSecurityToken. + + + {0} is not available in deserialized RequestSecurityTokenResponse. + + + {0} is only available in a deserialized RequestSecurityTokenResponse. + + + The RequestSecurityTokenResponseCollection received has more than one RequestSecurityTokenResponse element. Only one RequestSecurityTokenResponse element was expected. + + + VirtualPathExtension is not allowed to be removed. + + + The protocol '{0}' is not supported. + + + The BaseUriWithWildcard object has invalid fields after deserialization. + + + Registered relativeAddress '{0}' in configuration file is not a valid one. Possible causes could be : You specified an empty addreess or an absolute address (i.e., starting with '/' or '\\'), or the address contains invalid character[s]. The supported relativeAddress formats are "[folder/]filename" or "~/[folder/]filename". + + + '{0}' is an absolute address. The supported relativeAddress formats are "[subfolder/]filename" or "~/[subfolder/]filename". + + + Cannot create security binding element based on the configuration data. When secure conversation authentication mode is selected, the secure conversation bootstrap binding element must also be specified. + + + Setting minFreeMemoryPercentageToActivateService requires full trust privilege. Please change the application's trust level or remove this setting from the configuration file. + + + This service requires ASP.NET compatibility and must be hosted in IIS. Either host the service in IIS with ASP.NET compatibility turned on in web.config or set the AspNetCompatibilityRequirementsAttribute.AspNetCompatibilityRequirementsMode property to a value other than Required. + + + The '{0}' protocol binding '{1}' specifies an invalid port number '{2}'. + + + The protocol binding '{0}' does not conform to the syntax for '{1}'. The following is an example of valid '{1}' protocol bindings: '{2}'. + + + The protocol binding '{0}' is not valid for '{1}'. This might be because the port number is out of range. + + + There is no compatible TransportManager found for URI '{0}'. This may be because you have used an absolute address that points outside of the virtual application. Please use a relative address instead. + + + There is no compatible TransportManager found for URI '{0}'. This may be because you have used an absolute address that points outside of the virtual application, or the binding settings of the endpoint do not match those that have been set by other services or endpoints. Note that all bindings for the same protocol should have the same settings in the same application. + + + '{0}' cannot be invoked within the current hosting environment. This API requires that the calling application be hosted in IIS or WAS. + + + The requested service, '{0}' could not be activated. See the server's diagnostic trace logs for more information. + + + The value for the Service attribute was not provided in the ServiceHost directive. + + + The service endpoint failed to listen on the URI '{0}' because access was denied. Verify that the current user is granted access in the appropriate allowAccounts section of SMSvcHost.exe.config. + + + The service endpoint failed to listen on the URI '{0}' because the shared memory section was not found. Verify that the '{1}' service is running. + + + The TransportManager failed to listen on the supplied URI using the {0} service: {1}. + + + failed to start the service ({0}). Refer to the Event Log for more details + + + failed to start the service because it is disabled. An administrator can enable it by running 'sc.exe config {0} start= demand'. + + + failed to start the service. Refer to the Event Log for more details + + + failed to look up the service process in the SCM ({0}) + + + failed to look up the service SID in the SCM ({0}) + + + failed to read the service's endpoint with native error code {0}. See inner exception for details + + + the service failed the security checks + + + failed to retrieve the UserSid of the service process ({0}) + + + failed to retrieve the UserSid of the current process + + + failed to retrieve the LogonSid of the service process ({0}) + + + failed to establish a data connection to the service + + + failed to create a data connection to the service + + + failed to establish the data connection because of an I/O error + + + the version is not supported by the service + + + failed to grant the PROCESS_DUP_HANDLE access right to the target service's account SID '{0}'. + + + the URI is too long + + + the quota was exceeded + + + the protocol is not supported + + + the URI is already registered with the service + + + the service failed to listen + + + The message could not be dispatched to the service at address '{0}'. Refer to the server Event Log for more details + + + The message could not be dispatched because the service at the endpoint address '{0}' is unavailable for the protocol of the address. + + + The endpoint address for the NT service '{0}' read from shared memory is empty. + + + The message could not be dispatched because the transport manager has been stopped. This can happen if the application is being recycled or disabled. + + + The '{0}' from the '{1}' namespace is empty and does not specify a valid identity claim. + + + '{0}' from namespace '{1}' is not expected. Expecting element '{2}' from namespace '{3}' + + + '{0}' from namespace '{1}' is not expected to appear more than once + + + An unsupported security policy assertion was detected during the security policy import: {0} + + + The extensions cannot contain an Identity if one is supplied as a constructor argument. + + + Value '{0}' provided for '{1}' from namespace '{2}' is an invalid absolute URI. + + + The binding ('{0}','{1}') for contract ('{2}','{3}') is configured with SecureConversation, but the authentication mode is not able to provide the request/reply-based integrity and confidentiality required for the negotiation. + + + The '{0}'.'{1}' binding for the '{2}'.'{3}' contract is configured with an authentication mode that requires transport level integrity and confidentiality. However the transport cannot provide integrity and confidentiality. + + + The contract operation '{0}' requires Windows identity for automatic impersonation. A Windows identity that represents the caller is not provided by binding ('{1}','{2}') for contract ('{3}','{4}'. + + + A listen URI must be specified in order to open this {0}. + + + Channel interface type '{0}' is not supported. + + + This property cannot be changed after the transport manager has been opened. + + + This operation is only valid after the transport manager has been opened. + + + Unrecognized identity type Name='{0}', Namespace='{1}'. + + + Cannot read the Identity element. The Identity type is not supported or the Identity element is empty. + + + Cannot load the X.509 certificate identity specified in the configuration. + + + The ClaimType '{0}' is not recognized. Expected ClaimType '{1}'. + + + An AsyncCallback threw an exception. + + + You cannot Send messages on a channel after CloseOutputSession has been called. + + + The communication object, {0}, cannot be modified while it is in the {1} state. + + + The communication object, {0}, cannot be modified unless it is in the Created state. + + + The communication object, {0}, is in the {1} state. Communication objects cannot be used for communication unless they are in the Opened state. + + + The communication object, {0}, cannot be used for communication because it is in the Faulted state. + + + The communication object, {0}, cannot be used for communication because it is in the Faulted state: {1} + + + The communication object, {0}, cannot be used for communication because it has been Aborted. + + + The communication object, {0}, cannot be used for communication because it has been Aborted: {1} + + + The communication object, {0}, has overridden the virtual function {1} but it does not call version defined in the base class. + + + The communication object, {0}, is not part of WCF and is in an unsupported state '{1}'. This indicates an internal error in the implementation of that communication object. + + + The communication object, {0}, cannot be used due to an error that occurred during close. + + + A call to IChannelFactory.CreateChannel made on an object of type {0} failed because Open has not been called on this object. + + + Cannot modify channel parameters because the {0} is in the {1} state. This operation is only supported in the Created state. + + + Cannot propagate channel parameters because the {0} is in the {1} state. This operation is only supported in the Opening or Opened state when the collection is locked. + + + Binding '{0}' is not configured properly. OneWayBindingElement requires an inner binding element that supports IRequestChannel/IReplyChannel or IDuplexSessionChannel. + + + The specified channel type {0} is not supported by this channel manager. + + + SecurityContext for the UltimateReceiver role is missing from the SecurityContextProperty of the request message with action '{0}'. + + + Cannot start impersonation because the SecurityContext for the UltimateReceiver role from the request message with the '{0}' action is not mapped to a Windows identity. + + + Unexpected internal enum value: {0}. + + + Invalid decoder state machine. + + + Operation property of OperationAttributeGenerationContext is required to generate an attribute based on settings. + + + The username/password Membership provider {0} specified in the configuration is invalid. No such provider was found registered under system.web/membership/providers. + + + The RoleProvider {0} specified in the configuration is invalid. No such provider was found registered under system.web/roleManager/providers. + + + The {0} object has been disposed. + + + The XmlReader used for the body of the message must be positioned on an element. + + + A property with the name '{0}' already exists. + + + A property with the name '{0}' is not present. + + + The message header with name '{0}' and namespace '{1}' is already present in the set of understood headers. + + + The message header with name '{0}' and namespace '{1}' is not present in the set of understood headers. + + + Multiple headers with name '{0}' and namespace '{1}' found. + + + Multiple headers with name '{0}' and namespace '{1}' and role '{2}' found. + + + Multiple RelatesTo headers with relationship '{0}' found. Only one is allowed per relationship. + + + Additional XML content is present in the fault detail element. Only a single element is allowed. + + + The body of the message cannot be read because it is empty. + + + Message is closed. + + + The operation cannot be completed because the stream is closed. + + + The body writer returned from OnCreateBufferedCopy was not buffered. + + + The body writer does not support writing more than once because it is not buffered. + + + KeySize element not present in RequestSecurityTokenResponse. + + + A reply message cannot be created because the request message does not have a MessageID. + + + There is not a header with name {0} and namespace {1} in the message. + + + MessageBuffer is closed. + + + The text encoding '{0}' used in the text message format is not supported. + + + At least one fault reason must be specified. + + + The translation set cannot contain nulls. + + + The fault does not have detail information. + + + Expected XML qualified name, found '{0}'. + + + Unbound prefix used in qualified name '{0}'. + + + ... + + + ... stream ... + + + ... Error reading body: {0}: {1} ... + + + The fault reason does not contain any text translations. + + + Client cannot determine the Service Principal Name based on the identity in the target address '{0}' for the purpose of SspiNegotiation/Kerberos. The target address identity must be a UPN identity (like acmedomain\\alice) or SPN identity (like host/bobs-machine). + + + Required xml:lang attribute value is missing. + + + Unrecognized charSet '{0}' in contentType. + + + Unrecognized contentType ({0}). Expected: {1}. + + + Cannot process contentType. + + + The envelope version of the incoming message ({0}) does not match that of the encoder ({1}). Make sure the binding is configured with the same version as the expected messages. + + + The message version of the outgoing message ({0}) does not match that of the encoder ({1}). Make sure the binding is configured with the same version as the message. + + + MessageVersion '{0}' not supported by MTOM encoder. + + + Read is not supported on this stream. + + + Seek is not supported on this stream. + + + An asynchronous write is pending on the stream. Ensure that there are no uncompleted asynchronous writes before attempting the next write. + + + A newly accepted connection did not receive initialization data from the sender within the configured ChannelInitializationTimeout ({0}). As a result, the connection will be aborted. If you are on a highly congested network, or your sending machine is heavily loaded, consider increasing this value or load-balancing your server. + + + The remote endpoint of the socket ({0}) did not respond to a close request within the allotted timeout ({1}). It is likely that the remote endpoint is not calling Close after receiving the EOF signal (null) from Receive. The time allotted to this operation may have been a portion of a longer timeout. + + + A graceful close was attempted on the socket, but the other side ({0}) is still sending data. + + + The pipe cannot be closed while a write to the pipe is pending. + + + The shutdown indicator could not be written to the pipe. The application on the other end of the pipe may not be listening for it. The pipe will still be closed. + + + The shutdown indicator was not received from the pipe. The application on the other end of the pipe may not have sent it. The pipe will still be closed. + + + The pipe name could not be obtained for the pipe URI: {0} + + + The pipe name could not be obtained for {0}. + + + The pipe was not able to be set to message mode: {0} + + + The pipe could not close gracefully. This may be caused by the application on the other end of the pipe exiting. + + + The pipe cannot be written to because it is already in the process of shutting down. + + + The read from the pipe expected just a signal, but received actual data. + + + The pipe cannot be written to or read from because it is already in the process of being closed. + + + Server cannot accept pipe: {0} + + + Cannot listen on pipe '{0}': {1} + + + Cannot listen on pipe name '{0}' because another pipe endpoint is already listening on that name. + + + Cannot listen on pipe '{0}' because the pipe name could not be reserved: {1} + + + The pipe listener has been disposed. + + + Connections cannot be created until the pipe has started listening. Call Listen() before attempting to accept a connection. + + + A pipe endpoint exists for '{0}', but the connect failed: {1} + + + Cannot connect to endpoint '{0}'. + + + Cannot connect to endpoint '{0}' within the allotted timeout of {1}. The time allotted to this operation may have been a portion of a longer timeout. + + + Cannot connect to endpoint '{0}' within the allotted timeout of {1}. The server has likely reached the MaxConnections quota and is too busy to accept new connections. The time allotted to this operation may have been a portion of a longer timeout. + + + The pipe endpoint '{0}' could not be found on your local machine. + + + URIs used with pipes must use the scheme: 'net.pipe'. + + + The pipe write did not write all the bytes. + + + The operation cannot be completed because the pipe was closed. This may have been caused by the application on the other end of the pipe exiting. + + + The read from the pipe did not complete within the allotted timeout of {0}. The time allotted to this operation may have been a portion of a longer timeout. + + + The write to the pipe did not complete within the allotted timeout of {0}. The time allotted to this operation may have been a portion of a longer timeout. + + + The pipe connection was aborted because an asynchronous read from the pipe did not complete within the allotted timeout of {0}. The time allotted to this operation may have been a portion of a longer timeout. + + + The pipe connection was aborted because an asynchronous write to the pipe did not complete within the allotted timeout of {0}. The time allotted to this operation may have been a portion of a longer timeout. + + + There was an error writing to the pipe: {0}. + + + There was an error reading from the pipe: {0}. + + + Unrecognized error {0} (0x{1}) + + + {0} ({1}, 0x{2}) + + + There is already a write in progress for the pipe. Wait for the first operation to complete before attempting to write again. + + + There is already a read in progress for the pipe. Wait for the first operation to complete before attempting to read again. + + + There was an error duplicating the. + + + The Session value '{0}' is invalid. Please specify 'CurrentSession','ServiceSession' or a valid non-negative Windows Session Id. + + + The package full name '{0}' is invalid. + + + The socket was aborted because an asynchronous receive from the socket did not complete within the allotted timeout of {0}. The time allotted to this operation may have been a portion of a longer timeout. + + + The socket connection was aborted because an asynchronous send to the socket did not complete within the allotted timeout of {0}. The time allotted to this operation may have been a portion of a longer timeout. + + + This operation is not valid until security negotiation is complete. + + + Error while reading message framing format at position {0} of stream (state: {1}) + + + More data was expected, but EOF was reached. + + + Expected record type '{0}', found '{1}'. + + + Framing major version {0} is not supported. + + + Framing mode {0} is not supported. + + + Specified size is too large for this implementation. + + + The framing via size ({0}) exceeds the quota. + + + The framing via ({0}) is not a valid URI. + + + The framing fault size ({0}) exceeds the quota. + + + The framing content type size ({0}) exceeds the quota. + + + The value cannot be accessed because it has not yet been fully decoded. + + + An attempt was made to decode a value after the framing stream was ended. + + + Stream Security is required at {0}, but no security context was negotiated. This is likely caused by the remote endpoint missing a StreamSecurityBindingElement from its binding. + + + The binary encoder session information exceeded the maximum size quota ({0}). To increase this quota, use the MaxSessionSize property on the BinaryMessageEncodingBindingElement. + + + The binary encoder session is not valid. There was an error decoding a previous message. + + + The binary encoder session information is not properly formed. + + + The channel received an unexpected fault input message while closing. The fault reason given is: '{0}' + + + The channel received an unexpected fault input message with Action = '{0}' while closing. You should only close your channel when you are not expecting any more input messages. + + + The channel received an unexpected input message with Action '{0}' while closing. You should only close your channel when you are not expecting any more input messages. + + + The maximum message size quota for incoming messages ({0}) has been exceeded. To increase the quota, use the MaxReceivedMessageSize property on the appropriate binding element. + + + The maximum message size quota for outgoing messages ({0}) has been exceeded. + + + The maximum message size quota for incoming messages has been exceeded for the remote channel. See the server logs for more details. + + + TimeoutStream requires an inner Stream that supports timeouts; its CanTimeout property must be true. + + + The filter already exists in the filter table. + + + An internal error has occurred. Unexpected error modifying filter table. + + + The number of XML infoset nodes inspected by the navigator has exceeded the quota ({0}). + + + Value cannot be negative. + + + The set of actions cannot be empty. + + + The prefix '{0}' is not defined. + + + Multiple filters matched. + + + The type of IMessageFilterTable created for a particular Filter type must always be the same. + + + The MessageFilterTable state is corrupt. The requested lookup cannot be performed. + + + The IMessageFilterTable created for a Filter cannot be a MessageFilterTable or a subclass of MessageFilterTable. + + + NodeQuota must be greater than 0. + + + Parameter value cannot be an empty string. + + + Required inner element '{0}' was not found. + + + Invalid attribute on the XPath. + + + When present, the dialect attribute must have the value '{0}'. + + + Could not compile the XPath expression '{0}' with the given XsltContext. + + + XmlReader not positioned at a start element. + + + The position is not valid for this navigator. + + + Cannot call '{0}' on a non-atomized navigator. + + + XML unique ID not supported. + + + A filter has attempted to access the body of a Message. Use a MessageBuffer instead if body filtering is required. + + + Not allowed to override prefix '{0}'. + + + The function '{0}' is not implemented. + + + XPathNavigator positions cannot be compared. + + + XPathNavigator must be a SeekableXPathNavigator. + + + Context node is not supported in node sequences. + + + IXsltContextFunction return type '{0}' not supported. + + + IXsltContextVariable type '{0}' not supported. + + + IXsltContextVariables cannot return null. + + + The argument to an IXsltContextFunction could not be converted to a string. + + + An internal error has occurred. Item already exists. + + + Positioned before first element. + + + Positioned after last element. + + + The XPathNodeIterator has been invalidated. XPathNodeIterators passed as arguments to IXsltContextFunctions are only valid within the function. They cannot be cached for later use or returned as the result of the function. + + + The string value can't be determined because the XPathNodeIterator has been moved past the first node. + + + {0} {1} + + + Addressing10 ({0}) + + + Addressing200408 ({0}) + + + AddressingNone ({0}) + + + Addressing Version '{0}' is not supported. + + + The '{0}' addressing mode is not supported. + + + Soap11 ({0}) + + + Soap12 ({0}) + + + EnvelopeNone ({0}) + + + The IMessageProperty could not be copied. CreateCopy returned null. + + + Unrecognized message version. + + + Unrecognized envelope version: {0}. + + + Envelope Version '{0}' is not supported. + + + Cannot detect WS-Addressing version. EndpointReference does not start with an Element. + + + Envelope Version '{0}' does not support adding Message Headers. + + + Addressing Version '{0}' does not support adding WS-Addressing headers. + + + The element '{0}' in namespace '{1}' is not valid. This either means that element '{0}' is a duplicate element, or that it is not a legal extension because extension elements cannot be in the addressing namespace. + + + The '{0}' header cannot be added because it does not support the specified message version '{1}'. + + + This message cannot support the operation because it has been copied. + + + This message cannot support the operation because it has been written. + + + This message cannot support the operation because it has been read. + + + An internal error has occurred. Invalid MessageState. + + + The body reader is in ReadState '{0}' and cannot be consumed. + + + The size necessary to buffer the XML content exceeded the buffer quota. + + + An internal error has occurred. The XML buffer is not in the correct state to perform the operation. + + + A body element was not found inside the message envelope. + + + The version of the header(s) ({0}) differs from the version of the message ({1}). + + + Manual addressing is enabled on this factory, so all messages sent must be pre-addressed. + + + A one-way header was expected on this message and none was found. It is possible that your bindings are mismatched. + + + Receive on local address {0} timed out after {1}. The time allotted to this operation may have been a portion of a longer timeout. + + + Receive timed out after {0}. The time allotted to this operation may have been a portion of a longer timeout. + + + WaitForMessage timed out after {0}. The time allotted to this operation may have been a portion of a longer timeout. + + + Receive timed out after {0}. The time allotted to this operation may have been a portion of a longer timeout. + + + Receive request timed out after {0}. The time allotted to this operation may have been a portion of a longer timeout. + + + Receive request on local address {0} timed out after {1}. The time allotted to this operation may have been a portion of a longer timeout. + + + Sending to via {0} timed out after {1}. The time allotted to this operation may have been a portion of a longer timeout. + + + Close timed out after {0}. Increase the timeout value passed to the call to Close or increase the CloseTimeout value on the Binding. The time allotted to this operation may have been a portion of a longer timeout. + + + Open timed out after {0} while establishing a transport session to {1}. The time allotted to this operation may have been a portion of a longer timeout. + + + Request timed out after {0} while establishing a transport connection to {1}. The time allotted to this operation may have been a portion of a longer timeout. + + + Connecting to via {0} timed out after {1}. Connection attempts were made to {2} of {3} available addresses ({4}). Check the RemoteAddress of your channel and verify that the DNS records for this endpoint correspond to valid IP Addresses. The time allotted to this operation may have been a portion of a longer timeout. + + + The request channel timed out attempting to send after {0}. Increase the timeout value passed to the call to Request or increase the SendTimeout value on the Binding. The time allotted to this operation may have been a portion of a longer timeout. + + + The request channel timed out while waiting for a reply after {0}. Increase the timeout value passed to the call to Request or increase the SendTimeout value on the Binding. The time allotted to this operation may have been a portion of a longer timeout. + + + The policy being imported for contract '{0}:{1}' contains multiple HTTP authentication scheme assertions. Since at most one such assertion is allowed, policy import has failed. This may be resolved by updating the policy to contain no more than one HTTP authentication scheme assertion. + + + More than one '{0}' objects were found in the BindingParameters of the BindingContext. This is usually caused by having multiple '{0}' objects in a CustomBinding. Remove all but one of these elements. + + + The '{0}' can only be used with HTTP (or HTTPS) transport. + + + The value specified, '{0}', for the If-Modified-Since header does not parse into a valid date. Check the property value and ensure that it is of the proper format. + + + The SOAP action specified on the message, '{0}', does not match the action specified on the HttpRequestMessageProperty, '{1}'. + + + The SOAP action specified on the message, '{0}', does not match the action specified in the content-type of the HttpRequestMessageProperty, '{1}'. + + + The SOAP action specified on the message, '{0}', does not match the HTTP SOAP Action, '{1}'. + + + An error ({0}) occurred while parsing the content type of the HTTP request. The content type was: {1}. + + + The HTTP service located at {0} is unavailable. This could be because the service is too busy or because no endpoint was found listening at the specified address. Please ensure that the address is correct and try accessing the service again later. + + + The HTTP request to '{0}' was aborted. This may be due to the local channel being closed while the request was still in progress. If this behavior is not desired, then update your code so that it does not close the channel while request operations are still in progress. + + + The HTTP request to '{0}' has exceeded the allotted timeout of {1}. The time allotted to this operation may have been a portion of a longer timeout. + + + The HTTP request to '{0}' has exceeded the allotted timeout of {1} while reading the response. The time allotted to this operation may have been a portion of a longer timeout. + + + An error ({0}) occurred while transmitting data over the HTTP channel. + + + An error occurred while receiving the HTTP response to {0}. This could be due to the service endpoint binding not using the HTTP protocol. This could also be due to an HTTP request context being aborted by the server (possibly due to the service shutting down). See server logs for more details. + + + An error occurred while making the HTTP request to {0}. This could be due to the fact that the server certificate is not configured properly with HTTP.SYS in the HTTPS case. This could also be caused by a mismatch of the security binding between the client and the server. + + + HTTP request streaming cannot be used in conjunction with HTTP authentication. Either disable request streaming or specify anonymous HTTP authentication. + + + A reply has already been sent from this RequestContext. + + + Unable to start the HTTP listener. The URI provided, '{0}', is invalid for listening. Check the base address of your service and verify that it is a valid URI. + + + The requestContext has been aborted. + + + The receive context, {0}, is in the {1} state. Receive contexts cannot be used for sending delayed acks unless they are in the Received state. + + + The receive context, {0}, is in an unsupported state '{1}'. This indicates an internal error in the implementation of that receive context. + + + The receive context, {0}, cannot be used for sending delayed acks because it is in the Faulted state. + + + Invalid HostNameComparisonMode value: {0}. + + + Invalid data buffer. + + + A security session renew response was received with an invalid action '{0}'. + + + A security session close response was received with an invalid action '{0}', + + + TransactedBatchingBehavior cannot be used when ReceiveContext is being used. + + + Could not formulate request message for security session operation '{0}'. + + + There is no handler registered for session token issuance event. + + + There is no handler registered for session token renew event. + + + The identity of the security session renew message does not match the identity of the session token. + + + The RequestSecurityToken has an invalid or unspecified RequestType '{0}'. + + + The RequestSecurityToken must specify a CloseTarget. + + + Secure channel cannot be opened because security negotiation with the remote endpoint has failed. This may be due to absent or incorrectly specified EndpointIdentity in the EndpointAddress used to create the channel. Please verify the EndpointIdentity specified or implied by the EndpointAddress correctly identifies the remote endpoint. + + + The CloseTarget specified '{0}' does not identify the security token that signed the message. + + + The renew security session message does not have the session token as a supporting token. + + + The RequestSecurityToken must specify a RenewTarget. + + + There is no endorsing session token that matches the specified RenewTarget '{0}'. + + + Invalid format for encrypted body. + + + The EncryptedData or EncryptedKey is in an invalid state for this operation. + + + No signature message parts were specified for messages with the '{0}' action. + + + No encryption message parts were specified for messages with the '{0}' action. + + + The receiver sent back a security session fault message. Retry the request. + + + The Inner listener factory of {0} must be set before this operation. + + + Cannot create security binding element based on configuration data. The secure conversation bootstrap requires another secure conversation which is not supported. + + + Cannot open ChannelFactory as the inner channel factory was not set during the initialization process. + + + Duplex security is not supported by the security protocol factory '{0}'. + + + Request-reply security is not supported by the security protocol factory '{0}'. + + + The security protocol factory must be set before this operation is performed. + + + Security session protocol factory must be set before this operation is performed. + + + Security channel or listener factory creation failed. Secure conversation security token parameters do not specify the bootstrap security binding element. + + + The required '{0}' property on the '{1}' security protocol factory is not set or has an invalid value. + + + The protocol factory cannot create a protocol. + + + The identity check failed for the outgoing message. The expected identity is '{0}' for the '{1}' target endpoint. + + + The identity check failed for the incoming message. The expected identity is '{0}' for the '{1}' target endpoint. + + + The Identity check failed for the incoming message. The remote endpoint did not provide a domain name system (DNS) claim and therefore did not satisfied DNS identity '{0}'. This may be caused by lack of DNS or CN name in the remote endpoint X.509 certificate's distinguished name. + + + The Identity check failed for the outgoing message. The remote endpoint did not provide a domain name system (DNS) claim and therefore did not satisfied DNS identity '{0}'. This may be caused by lack of DNS or CN name in the remote endpoint X.509 certificate's distinguished name. + + + Identity check failed for incoming message. The expected DNS identity of the remote endpoint was '{0}' but the remote endpoint provided DNS claim '{1}'. If this is a legitimate remote endpoint, you can fix the problem by explicitly specifying DNS identity '{1}' as the Identity property of EndpointAddress when creating channel proxy. + + + Identity check failed for outgoing message. The expected DNS identity of the remote endpoint was '{0}' but the remote endpoint provided DNS claim '{1}'. If this is a legitimate remote endpoint, you can fix the problem by explicitly specifying DNS identity '{1}' as the Identity property of EndpointAddress when creating channel proxy. + + + The serialized token version {0} is unsupported. + + + The RequestSecurityTokenResponseCollection does not contain an authenticator. + + + The negotiation RequestSecurityTokenResponse has a different context from the authenticator RequestSecurityTokenResponse. + + + The recipient did not provide its certificate. This certificate is required by the TLS protocol. Both parties must have access to their certificates. + + + The authenticator was not included in the final leg of negotiation. + + + The RequestSecurityTokenResponse CombinedHash is incorrect. + + + The certificate for the client has not been provided. The certificate can be set on the ClientCredentials or ServiceCredentials. + + + The client certificate is not provided. Specify a client certificate in ServiceCredentials. + + + The client certificate is not provided. Specify a client certificate in ClientCredentials. + + + The service certificate is not provided. Specify a service certificate in ServiceCredentials. + + + The service certificate is not provided for target '{0}'. Specify a service certificate in ClientCredentials. + + + The username is not provided. Specify username in ClientCredentials. + + + Object is read-only. + + + Element {0} cannot be empty. + + + XML child node {0} of type {1} is unexpected for element {2}. + + + The context-id={0} (generation-id={1}) is already registered with SecurityContextSecurityTokenAuthenticator. + + + The context-id={0} (no key generation-id) is already registered with SecurityContextSecurityTokenAuthenticator. + + + There is no SecurityContextSecurityToken with context-id={0} (generation-id={1}) registered with SecurityContextSecurityTokenAuthenticator. + + + There is no SecurityContextSecurityToken with context-id={0} (no key generation-id) registered with SecurityContextSecurityTokenAuthenticator. + + + The SecurityContextSecurityToken has an invalid Cookie. The following error occurred when processing the Cookie: '{0}'. + + + The SecurityContextSecurityToken with context-id={0} (key generation-id={1}) is not registered. + + + The SecurityContextSecurityToken with context-id={0} (key generation-id={1}) has expired. + + + The SecurityContextSecurityToken with context-id={0} (no key generation-id) has expired. + + + The SecurityContextSecurityToken does not have a context-id. + + + For sending a message on server side composite duplex channels, the message must have either the 'Via' property or the 'To' header set. + + + The 'Via' property on the message is set to Anonymous Uri '{0}'. Please set the 'Via' property to a non-anonymous address as message cannot be addressed to anonymous Uri on server side composite duplex channels. + + + The 'To' header on the message is set to Anonymous Uri '{0}'. Please set the 'To' header to a non-anonymous address as message cannot be addressed to anonymous Uri on server side composite duplex channels. + + + This SecurityProtocol instance was not set up to process outgoing messages. + + + This SecurityProtocol instance was not set up to process incoming messages. + + + The token provider cannot get tokens for target '{0}'. + + + Key derivation algorithm '{0}' is not supported. + + + Cannot find the correlation state for applying security to reply at the responder. + + + The reply was not signed with the required signing token. + + + Encryption not expected for this message. + + + A signature is not expected for this message. + + + The QName is invalid. + + + The ICrypto implementation '{0}' is not supported. + + + On DuplexSecurityProtocolFactory, the same protocol factory cannot be set for the forward and reverse directions. + + + The algorithm '{0}' is not accepted for operation '{1}' by algorithm suite {2}. + + + '{0}' does not support '{1}' creation. + + + Cannot create an ICrypto interface from the '{0}' token for signature verification. + + + Message security verification failed. + + + Transport secured messages should have the 'To' header specified. + + + The message received over Transport security was missing the 'To' header. + + + The message received over Transport security has unsigned 'To' header. + + + More than one 'To' header specified in a message secured by Transport Security. + + + Received security header contains unexpected token '{0}'. + + + Cannot find the X.509 certificate using the following search criteria: StoreName '{0}', StoreLocation '{1}', FindType '{2}', FindValue '{3}'. + + + Cannot find The X.509 certificate using the following search criteria: StoreName '{0}', StoreLocation '{1}', FindType '{2}', FindValue '{3}' for target '{4}'. + + + Found multiple X.509 certificates using the following search criteria: StoreName '{0}', StoreLocation '{1}', FindType '{2}', FindValue '{3}'. Provide a more specific find value. + + + Found multiple X.509 certificates using the following search criteria: StoreName '{0}', StoreLocation '{1}', FindType '{2}', FindValue '{3}' for target '{4}'. Provide a more specific find value. + + + The KeyInfo clause is missing or empty in EncryptedKey. + + + The EncryptedKey clause was not wrapped with the required encryption token '{0}'. + + + The message was not encrypted with the required encryption token. + + + The timestamp must occur first in this security header layout. + + + The timestamp must occur last in this security header layout. + + + Only one primary signature is allowed in a security header. + + + The signing token {0} has no keys. The security token is used in a context that requires it to perform cryptographic operations, but the token contains no cryptographic keys. Either the token type does not support cryptographic operations, or the particular token instance does not contain cryptographic keys. Check your configuration to ensure that cryptographically disabled token types (for example, UserNameSecurityToken) are not specified in a context that requires cryptographic operations (for example, an endorsing supporting token). + + + The signing token {0} has no key that supports the algorithm suite {1}. + + + Delayed security application has already been completed. + + + Cannot resolve KeyInfo in derived key token for resolving source token: KeyInfoClause '{0}'. + + + KeyInfo clause '{0}' resolved to token '{1}', which does not contain a Symmetric key that can be used for derivation. + + + Cannot resolve KeyInfo for verifying signature: KeyInfo '{0}', available tokens '{1}'. + + + Cannot resolve KeyInfo for unwrapping key: KeyInfo '{0}', available tokens '{1}'. + + + Cannot resolve KeyInfo for decryption: KeyInfo '{0}', available tokens '{1}'. + + + An empty value was found for the required base-64 attribute name '{0}', namespace '{1}'. + + + The security header element '{0}' with the '{1}' id must be signed. + + + The '{0}' security token with the '{1}' attachment mode must be signed. + + + The '{0}' security token with the '{1}' attachment mode must be encrypted. + + + Operation '{0}' is not valid in message body state '{1}'. + + + EncryptedKey with ReferenceList is not allowed according to the current settings. + + + Cannot find a token authenticator for the '{0}' token type. Tokens of that type cannot be accepted according to current security settings. + + + No signature was created because not part of the message matched the supplied message part specification. + + + Supporting SecurityToken cannot be written without encryption. + + + The '{0}' id occurred twice in the message that is supplied for verification. + + + Canonicalization algorithm '{0}' is not supported. + + + The KeyInfo value was not found in the encrypted item to find the decrypting token. + + + No KeyInfo in signature to find verification token. + + + Security header is empty. + + + The encryption method is missing in encrypted data. + + + The Encrypted Header and the Security Header '{0}' attribute did not match. Encrypted Header: {1}. Security Header: {2}. + + + At most one reference list is supported with default policy check. + + + At most one signature is supported with default policy check. + + + Unexpected encrypted element in security header. + + + Id is missing in encrypted item in security header. + + + The supplied token manager cannot create a token reference. + + + The timestamp element added to security header to sign has no id. + + + An encrypted header must have an id. + + + The data reference '{0}' could not be resolved in the received message. + + + A timestamp element has already been set for this security header. + + + More than one Timestamp element was present in security header. + + + The incoming message was signed with a token which was different from what used to encrypt the body. This was not expected. + + + Cannot create the '{0}' symmetric algorithm from the token. + + + Unrecognized encoding occurred while reading the binary security token. + + + Cannot resolve reference URI '{0}' in signature to compute digest. + + + No timestamp is available in the security header to do replay detection. + + + No signature is available in the security header to provide the nonce for replay detection. + + + There is no namespace binding for prefix '{0}' in scope. + + + Derived Key Token cannot derive key from the secret. + + + Both offset and generation cannot be specified for Derived Key Token. + + + Either offset or generation must be specified for Derived Key Token. + + + DerivedKeyToken requires a reference to a token. + + + DerivedKey length ({0}) exceeds the allowed settings ({1}). + + + The Implicit derived key clause '{0}' specifies a derivation key length ({1}) which exceeds the allowed maximum length ({2}). + + + The received derived key token has a invalid offset value specified. Value: {0}. The value should be greater than or equal to zero. + + + The received derived key token has a invalid generation value specified. Value: {0}. The value should be greater than or equal to zero. + + + The XML element {0} does not have a child of type {1}. + + + RequestedSecurityToken not specified in RequestSecurityTokenResponse. + + + Binary encoding {0} is not supported. + + + Invalid key encryption algorithm {0}. + + + The asynchronous result object used to end this operation was not the object that was returned when the operation was initiated. + + + Unable to create token reference. + + + null + + + The specified nonce is too short. The minimum required nonce length is 4 bytes. + + + There is no binary negotiation to send to the other party. + + + Security negotiation failure because an incorrect Context attribute specified in RequestSecurityToken/RequestSecurityTokenResponse from the other party. + + + No binary negotiation was received from the other party. + + + The proof token was not wrapped correctly in the RequestSecurityTokenResponse. + + + Final RSTR from other party does not contain a service token. + + + The Security Support Provider Interface (SSPI) negotiation failed. + + + Cannot authenticate the other party. + + + Incoming binary negotiation has invalid ValueType {0}. + + + The channel is not open. + + + Security negotiation failed because the remote party did not send back a reply in a timely manner. This may be because the underlying transport connection was aborted. + + + SecurityVersion must be WsSecurity10 or WsSecurity11. + + + Creation time must be before expiration time. + + + Negotiation state already exists for context '{0}'. + + + Cannot find the negotiation state for the context '{0}'. + + + Send cannot be called when the session does not expect output. + + + The session was closed before message transfer was complete. + + + The item cannot be added. The maximum cache size is ({0} items). + + + The server's X509SecurityTokenProvider cannot be null. + + + Expected binary secret of type {0} but got secret of type {1}. + + + The '{0}' username token has an unsupported password type. + + + Unrecognized identity property type: '{0}'. + + + There was no channel that could accept the message with action '{0}'. + + + There was no endpoint listening at {0} that could accept the message. This is often caused by an incorrect address or SOAP action. See InnerException, if present, for more details. + + + This factory buffers messages, so the message sizes must be in the range of an integer value. + + + For TransferMode.Buffered, MaxReceivedMessageSize and MaxBufferSize must be the same value. + + + MaxBufferSize must not exceed MaxReceivedMessageSize. + + + This Factory buffers messages, so the message sizes must be in the range of a int value. + + + URI {0} could not be set because its size ({1}) exceeds the max supported size ({2}). + + + Expecting first char - c - to be in set [Char.IsLetter(c) && c == '_', found '{0}'. + + + Expecting all chars - c - of id to be in set [Char.IsLetter(c), Char.IsNumber(c), '.', '_', '-'], found '{0}'. + + + HTTP could not register URL {0}. Another application has already registered this URL with HTTP.SYS. + + + HTTP could not register URL {0}. Your process does not have access rights to this namespace (see http://go.microsoft.com/fwlink/?LinkId=70353 for details). + + + HTTP could not register URL {0} because TCP port {1} is being used by another application. + + + HTTP could not register URL {0} because the MaxEndpoints quota has been exceeded. To correct this, either close other HTTP-based services, or increase your MaxEndpoints registry key setting (see http://go.microsoft.com/fwlink/?LinkId=70352 for details). + + + The remote server returned an unexpected response: ({0}) {1}. + + + The number of bytes available is inconsistent with the HTTP Content-Length header. There may have been a network error or the client may be sending invalid requests. + + + A response was received from a one-way send over the underlying IRequestChannel. Make sure the remote endpoint has a compatible binding at its endpoint (one that contains OneWayBindingElement). + + + The receiver returned an error indicating that the content type was missing on the request to {0}. See the inner exception for more information. + + + Duplex channel to {0} was aborted during the open process. + + + Operation was aborted while establishing a connection to {0}. + + + The incoming message contains a SOAP header representing the WS-Addressing '{0}', yet the HTTP transport is configured with AddressingVersion.None. As a result, the message is being dropped. If this is not desired, then update your HTTP binding to support a different AddressingVersion. + + + There is a problem with the XML that was received from the network. See inner exception for more details. + + + An IPv4 address was specified ({0}), but IPv4 is not enabled on this machine. + + + An IPv6 address was specified ({0}), but IPv6 is not enabled on this machine. + + + Cannot find a unique port number that is available for both IPv4 and IPv6. + + + There is already a listener on IP endpoint {0}. This could happen if there is another application already listening on this endpoint or if you have multiple service endpoints in your service host with the same IP endpoint but with incompatible binding configurations. + + + Insufficient winsock resources available to complete socket connection initiation. + + + Insufficient memory avaliable to complete the operation. + + + Could not connect to {0}. TCP error code {1}: {2}. + + + Could not connect to {0}. The connection attempt lasted for a time span of {3}. TCP error code {1}: {2}. + + + A TCP error ({0}: {1}) occurred while listening on IP Endpoint={2}. + + + A TCP error ({0}: {1}) occurred while transmitting data. + + + A TCP error ({0}: {1}) occurred while transmitting data. The local IP address and port is {2}. The remote IP address and port is {3}. + + + The socket connection was aborted by your local machine. This could be caused by a channel Abort(), or a transmission error from another thread using this socket. + + + The HTTP request context was aborted while writing the response. As a result, the response may not have been completely written to the network. This can be remedied by gracefully closing the request context rather than aborting it. + + + The socket connection was aborted. This could be caused by an error processing your message or a receive timeout being exceeded by the remote host, or an underlying network resource issue. Local socket timeout was '{0}'. + + + The socket connection was aborted. This could be caused by an error processing your message or a receive timeout being exceeded by the remote host, or an underlying network resource issue. Local socket timeout was '{0}'. The local IP address and port is {1}. The remote IP address and port is {2}. + + + The socket transfer timed out after {0}. You have exceeded the timeout set on your binding. The time allotted to this operation may have been a portion of a longer timeout. + + + The socket transfer timed out after {0}. You have exceeded the timeout set on your binding. The time allotted to this operation may have been a portion of a longer timeout. The local IP address and port is {1}. The remote IP address and port is {2}. + + + The socket connection has been disposed. + + + The socket listener has been disposed. + + + The socket listener is not listening. + + + No duplex session listener was listening at {0}. This could be due to an incorrect via set on the client or a binding mismatch. + + + The entry found in AuthenticationManager's CustomTargetNameDictionary for {0} does not match the requested identity of {1}. + + + An HTTP Content-Type header is required for SOAP messaging and none was found. + + + Content Type {0} was sent to a service expecting {1}. The client and service bindings may be mismatched. + + + The content type {0} of the response message does not match the content type of the binding ({1}). If using a custom encoder, be sure that the IsContentTypeSupported method is implemented properly. The first {2} bytes of the response were: '{3}'. + + + The content type {0} of the message is not supported by the encoder. + + + The binding specified requires that the to and via URIs must match because the Addressing Version is set to None. The to URI specified was '{0}'. The via URI specified was '{1}'. + + + The server challenged this request and streamed requests cannot be resubmitted. To enable HTTP server challenges, set your TransferMode to Buffered or StreamedResponse. + + + Content Type {0} was not supported by service {1}. The client and service bindings may be mismatched. + + + Server faulted with code '{0}'. + + + Content type '{0}' is too long to be processed by the remote host. See the server logs for more details. + + + Via '{0}' is too long to be processed by the remote host. See the server logs for more details. + + + The .Net Framing mode being used is not supported by '{0}'. See the server logs for more details. + + + The .Net Framing version being used is not supported by '{0}'. See the server logs for more details. + + + The requested upgrade is not supported by '{0}'. This could be due to mismatched bindings (for example security enabled on the client and not on the server). + + + Server '{0}' sent back a fault indicating it is too busy to process the request. Please retry later. Please see the inner exception for fault details. + + + Server '{0}' sent back a fault indicating it is in the process of shutting down. Please see the inner exception for fault details. + + + Server '{0}' is too busy to process this request. Try again later. + + + Protocol Type {0} was sent to a service that does not support that type of upgrade. + + + .Net Framing upgrade request for {0} was sent to a service that is not setup to receive upgrades. + + + You have tried to create a channel to a service that does not support .Net Framing. + + + You have tried to create a channel to a service that does not support .Net Framing. It is possible that you are encountering an HTTP endpoint. + + + An error occurred while transmitting data. + + + The server rejected the upgrade request. + + + The server at {0} rejected the session-establishment request. + + + Cannot resolve the host name of URI \"{0}\" using DNS. + + + The '{0}' authentication scheme has been specified on the HTTP factory. However, the factory only supports specification of exactly one authentication scheme. Valid authentication schemes are Digest, Negotiate, NTLM, Basic, or Anonymous. + + + The value specified for the AuthenticationScheme property on the HttpTransportBindingElement ('{0}') is not allowed when building a ChannelFactory. If you used a standard binding, ensure the ClientCredentialType is not set to HttpClientCredentialType.InheritedFromHost, a value which is invalid on a client. If you set the value to '{0}' directly on the HttpTransportBindingElement, please set it to Digest, Negotiate, NTLM, Basic, or Anonymous. + + + The '{0}' authentication scheme has been specified for the proxy on the HTTP factory. However, the factory only supports specification of exactly one authentication scheme. Valid authentication schemes are Digest, Negotiate, NTLM, Basic, or Anonymous. + + + The remote HTTP server did not satisfy the mutual authentication requirement. + + + The HTTP request is unauthorized with client authentication scheme '{0}'. The authentication header received from the server was '{1}'. + + + The HTTP request with client authentication scheme '{0}' failed with '{1}' status. + + + The HTTP request was forbidden with client authentication scheme '{0}'. + + + The provided URI scheme '{0}' is invalid; expected '{1}'. + + + The HTTPS listener factory was configured to require a client certificate and the '{0}' authentication scheme. However, only one form of client authentication can be required at once. + + + Could not find an appropriate transport manager for listen URI '{0}'. + + + The specified channel listener at '{0}' is not registered with this transport manager. + + + The HTTPS channel factory does not support explicit specification of an identity in the EndpointAddress unless the authentication scheme is NTLM or Negotiate. + + + The endpoint identity specified when creating the HTTPS channel to '{0}' contains multiple server certificates. However, the HTTPS transport only supports the specification of a single server certificate. In order to create an HTTPS channel, please specify no more than one server certificate in the endpoint identity. + + + The server certificate with name '{0}' failed identity verification because its thumbprint ('{1}') does not match the one specified in the endpoint identity ('{2}'). As a result, the current HTTPS request has failed. Please update the endpoint identity used on the client or the certificate used by the server. + + + A registration already exists for URI '{0}'. + + + Could not establish secure channel for SSL/TLS with authority '{0}'. + + + Could not establish trust relationship for the SSL/TLS secure channel with authority '{0}'. + + + Could not find a compatible transport manager for URI '{0}'. + + + The SPN for the responding server at URI '{0}' could not be determined. + + + The remote server did not satisfy the mutual authentication requirement. + + + Transfer mode {0} is not supported by {1}. + + + The token provider of type '{0}' did not return a token of type '{1}'. Check the credential configuration. + + + The required UserNameSecurityToken was not provided. + + + The following remote identity failed verification: '{0}'. + + + You cannot specify an explicit Proxy Address as well as UseDefaultWebProxy=true in your HTTP Transport Binding Element. + + + The HTTP proxy authentication credential specified an impersonation level restriction ({0}) that is stricter than the restriction for target server authentication ({1}). + + + The HTTP proxy authentication credential specified an mutual authentication requirement ({0}) that is stricter than the requirement for target server authentication ({1}). + + + The NTLM authentication scheme was specified, but the target credential does not allow NTLM. + + + The impersonation level '{0}' was specified, yet HTTP Digest authentication can only support 'Impersonation' level when used with an explicit credential. + + + The scheme parameter must not be empty. + + + The protection level '{0}' was specified, yet SSL transport security only supports EncryptAndSign. + + + {0}. This often indicates that a service that HTTP.SYS depends upon (such as httpfilter) is not started. + + + {0}. This often indicates that the HTTP client has prematurely closed the underlying TCP connection. + + + Opening the channel timed out after {0}. The time allotted to this operation may have been a portion of a longer timeout. + + + Opening the {0} channel timed out after {1}. The time allotted to this operation may have been a portion of a longer timeout. + + + TimeSpan must be greater than TimeSpan.Zero. + + + TimeSpan cannot be less than TimeSpan.Zero. + + + The value of this argument must be non-negative. + + + The value of this argument must be positive. + + + The value of this argument must be greater than 0. + + + The value of this argument must fall within the range {0} to {1}. + + + The specified offset exceeds the upper bound of the buffer ({0}). + + + The specified offset exceeds the buffer size ({0} bytes). + + + The specified size exceeds the remaining buffer space ({0} bytes). + + + The space needed for encoding ({0} bytes) exceeds the message frame offset. + + + {0} returned true from OnTryCreateFaultMessage, but did not return a fault message. + + + {0} returned false from OnTryCreateFaultMessage, but returned a non-null fault message. + + + {0} returned true from OnTryCreateException, but did not return an Exception. + + + {0} returned false from OnTryCreateException, but returned a non-null Exception (See InnerException for details). + + + Policy chain contains self issued URI or a managed issuer in the wrong position. + + + The Binding with name {0} failed validation because it contains a BindingElement with type {1} which is not supported in partial trust. Consider using BasicHttpBinding or WSHttpBinding, or hosting your application in a full-trust environment. + + + The WSHttpBinding with name {0} failed validation because it contains a BindingElement with type {1} which is not supported in partial trust. Consider disabling the message security and reliable session options, using BasicHttpBinding, or hosting your application in a full-trust environment. + + + The Binding with name {0} failed validation because the Binding type {1} is not supported in partial trust. Consider using BasicHttpBinding or WSHttpBinding, or hosting your application in a full-trust environment. + + + The Service with name '{0}' could not be constructed because the application does not have permission to construct the type: both the Type and its default parameter-less constructor must be public. + + + The Method with name '{1}' in Type '{0}' could not be invoked because the application does not have permission to invoke the method: both the Method and its containing Type must be public. + + + Access to performance counters is denied. Application may be running in partial trust. Either disable performance counters or configure the application to run in full trust. + + + Performance counter instance names may not be unique. See "http://go.microsoft.com/fwlink/?LinkId=524462" for details. + + + Access to windows management instrumentation (WMI) is denied. Application may be running in partial trust. Either disable WMI or configure the application to run in full trust. + + + Unable to log messages. Application may be running in partial trust. Either disable message logging or configure the application to run in full trust. + + + The 'scopeName' argument to the InstanceKey constructor must be a non-empty string which indicates the scope of uniqueness for the key. Durable services use the service namespace and name as the scope of uniqueness. + + + The 'provider' argument to the InstanceKey constructor must be a non-empty string which identifies the source of the key data. The 'provider' argument can be null, in which case the default correlation provider name is used. + + + The 'Name' property cannot be set on an invalid InstanceKey. + + + The type {0} is not a supported result type. + + + The result cannot be represented as a nodeset. Only results of type XPathResultType.NodeSet can be represented as nodesets. + + + Message with id {0} was not in a locked state. + + + Validity of message with id {0} has expired. + + + The StreamUpgradeInitiator specified ({0}) is not supported by this IStreamUpgradeChannelBindingProvider implementation. The most likely cause of this is passing a StreamUpgradeInitiator that was not created by the StreamUpgradeProvider associated with the current IStreamUpgradeChannelBindingProvider implementation. + + + The StreamUpgradeAcceptor specified ({0}) is not supported by this IStreamUpgradeChannelBindingProvider implementation. The most likely cause of this is passing a StreamUpgradeAcceptor that was not created by the StreamUpgradeProvider associated with this IStreamUpgradeChannelBindingProvider implementation. + + + The StreamUpgradeProvider {0} does not support the specified ChannelBindingKind ({1}). + + + Extended protection is not supported on this platform. Please install the appropriate patch or change the ExtendedProtectionPolicy on the Binding or BindingElement to a value with a PolicyEnforcement value of "Never" or "WhenSupported". + + + The Authentication Scheme "Basic" does not support Extended Protection. Please use a different authentication scheme or disable the ExtendedProtectionPolicy on the Binding or BindingElement by creating a new ExtendedProtectionPolicy with a PolicyEnforcement value of "Never". + + + CustomChannelBindings are not supported. Please remove the CustomChannelBinding from the ExtendedProtectionPolicy". + + + ClientCredentialType '{0}' can only be used on the server side, not the client side. Please use one of the following values instead 'None, Basic, Client, Digest, Ntlm, Windows'. + + + When authentication schemes 'Basic' and also '{0}' are enabled, the value of IncludeWindowsGroups for Windows ('{1}') and UserName authentication ('{2}') must match. Please consider using the same value in both places. + + + The authentication schemes cannot be inherited from the host for binding '{0}'. No AuthenticationScheme was specified on the ServiceHost or in the virtual application in IIS. This may be resolved by enabling at least one authentication scheme for this virtual application in IIS, through the ServiceHost.Authentication.AuthenticationSchemes property or in the configuration at the <serviceAuthenticationManager> element. + + + The authentication schemes configured on the host ('{0}') do not allow those configured on the binding '{1}' ('{2}'). Please ensure that the SecurityMode is set to Transport or TransportCredentialOnly. Additionally, this may be resolved by changing the authentication schemes for this application through the IIS management tool, through the ServiceHost.Authentication.AuthenticationSchemes property, in the application configuration file at the <serviceAuthenticationManager> element, by updating the ClientCredentialType property on the binding, or by adjusting the AuthenticationScheme property on the HttpTransportBindingElement. + + + Object type must be an enum with the flag attribute. '{0}' is not an enum - or the flag attribute is not set. Please use an enum type with the flag attribute instead. + + + Object type must be an enum with the flag attribute and may only contain powers of two for the flags enum values or a combination of such values. Please use an enum type according to these rules. + + + There is no pending asynchronous write on this stream. Ensure that there is pending write on the stream or verify that the implementation does not try to complete the same operation multiple times. + + + Cannot write to a buffer which is currently being flushed. + + + An asynchronous write was called on the stream without a free buffer. + + + The transport configured on this binding does not appear to support the CompressionFormat specified ({0}) on the message encoder. To resolve this issue, set the CompressionFormat on the {1} to '{2}' or use a different transport. + + + The value '{1}' is not supported in this context for the binding security property '{0}'. + + + The value '{1}' is not supported in this context for the binding property '{0}'. + + + The value of MaxPendingAccepts should not be larger than {0}. + + + The initialization process of the request message timed out after {0}. To increase this quota, use the '{1}' property on the '{2}'. + + + The value '{1}' for the '{0}' property is not supported in Windows Store apps. + + + The remote endpoint requested an address for acknowledgements that is not the same as the address for application messages. The channel could not be opened because this is not supported. Ensure the endpoint address used to create the channel is identical to the one the remote endpoint was set up with. + + + The address for acknowledgements must be the same as the address for application messages. Verify that your endpoint is configured to use the same URI for these two addresses. + + + The {0}:{1} assertion is not supported. + + + An unexpected error occurred while attempting to close the output half of the duplex reliable session. + + + The remote endpoint sent conflicting requests to create a reliable session. The conflicting requests have inconsistent filter criteria such as address or action. The reliable session has been faulted. + + + The remote endpoint sent conflicting requests to create a reliable session. The remote endpoint requested both a one way and a two way session. The reliable session has been faulted. + + + A message with action {0} could not be parsed. + + + The request to create a reliable session has been refused by the RM Destination. {0} The channel could not be opened. + + + The endpoint processing requests to create a reliable session only supports sessions in which the AcksTo Uri and the Endpoint Uri are the same. + + + The endpoint processing requests to create a reliable session only supports sessions in which the AcksTo Uri and the ReplyTo Uri are the same. + + + The endpoint at {0} processes duplex sessions. The create sequence request must contain an offer for a return sequence. This is likely caused by a binding mismatch. + + + The endpoint at {0} processes input sessions. The create sequence request must not contain an offer for a return sequence. This is likely caused by a binding mismatch. + + + The request to create a reliable session contains an invalid wsrm:IncompleteSequenceBehavior value. This is a WS-ReliableMessaging protocol violation. + + + The request to create a reliable session contains the wsse:SecurityTokenReference but does not carry a wsrm:UsesSequenceSTR header. This is a WS-ReliableMessaging protocol violation. The session could not be created. + + + The endpoint at {0} processes reply sessions. The create sequence request must contain an offer for a return sequence. This is likely caused by a binding mismatch. + + + The RM Destination requires the WS-SecureConversation protocol in the binding. This is likely caused by a binding mismatch. + + + The endpoint processing requests to create a reliable session does not support sessions that use SSL. This is likely caused by a binding mismatch. The session could not be created. + + + The request to create a reliable session carries a wsrm:UsesSequenceSTR header, but does not contain the wsse:SecurityTokenReference. This is a WS-ReliableMessaging protocol violation. The session could not be created. + + + The message is not a valid SOAP message. The body contains more than 1 root element. + + + The remote endpoint replied to a request for a two way session with an offer for a one way session. This is likely caused by a binding mismatch. The channel could not be opened. + + + The client requested creation of a two way session. A one way session was created. The session cannot continue without as a one way session. This is likely caused by a binding mismatch. + + + The response to the request to create a reliable session contains an invalid wsrm:IncompleteSequenceBehavior value. This is a WS-ReliableMessaging protocol violation. + + + The remote endpoint replied to a request for a one way session with an offer for a two way session. This is a WS-ReliableMessaging protocol violation. The channel could not be opened. + + + A return sequence was not offered by the create sequence request. The create sequence response cannot accept a return sequence. + + + The remote endpoint replied to a request for a two way session with an offer for a one way session. This is a WS-ReliableMessaging protocol violation. The channel could not be opened. + + + A return sequence was offered by the create sequence request but the create sequence response did not accept this sequence. + + + The WS-RM policy under the namespace {0} requires the wsrmp:ExactlyOnce, wsrmp:AtLeastOnce, or wsrmp:AtMostOnce assertion. Nothing was found. + + + The WS-RM policy under the namespace {0} requires the wsrmp:ExactlyOnce, wsrmp:AtLeastOnce, or wsrmp:AtMostOnce assertion. The {1} element under the {2} namespace was found. + + + The remote endpoint sent a TerminateSequence protocol message before fully acknowledging all messages in the reply sequence. This is a violation of the reliable request reply protocol. The reliable session was faulted. + + + The remote endpoint has closed the underlying secure session before the reliable session fully completed. The reliable session was faulted. + + + The underlying secure session has faulted before the reliable session fully completed. The reliable session was faulted. + + + The remote endpoint has errantly sent a TerminateSequence protocol message before the sequence finished. + + + The {0}:{1} element requires a {2}:{3} child element but has the {4} child element under the {5} namespace. + + + The {0}:{1} element requires a {2}:{3} child element but has no child elements. + + + The remote endpoint specified two different last message numbers. The reliable session is in an inconsistent state since it cannot determine the actual last message. The reliable session was faulted. + + + The SequenceAcknowledgement violates the cumulative acknowledgement invariant. + + + A violation of acknowledgement protocol has been detected. An InvalidAcknowledgement fault was sent to the remote endpoint and the reliable session was faulted. + + + An acknowledgement was received indicating the remaining buffer space on the remote endpoint is {0}. This number cannot be less than zero. The reliable session was faulted. + + + A message was received with a sequence number of {0}. Sequence numbers cannot be less than 1. The reliable session was faulted. + + + An acknowledgement range starting at {0} and ending at {1} was received. This is an invalid acknowledgement range. The reliable session was faulted. + + + The remote endpoint responded to the {0} request with a response with action {1}. The response must be a {0}Response with action {2}. The channel could not be opened. + + + The remote endpoint responded to the {0} request with a response with action {1}. The response must be a {0}Response with action {2}. The channel was faulted. + + + The {0} request's response was a message with action {1}. The response must be a {0}Response with action {2}. The reliable session cannot continue. + + + A message was received with a sequence number higher than the sequence number of the last message in this sequence. This is a violation of the sequence number protocol. The reliable session was faulted. + + + The value for wsrm:MessageNumber exceeds the value of the MessageNumber accompanying a LastMessage element in this Sequence. + + + Binding validation failed because the TransportBindingElement's ManualAddressing property was set to true on a binding that is configured to create reliable sessions. This combination is not supported and the channel factory or service host was not opened. + + + The maximum retry count has been exceeded with no response from the remote endpoint. The reliable session was faulted. This is often an indication that the remote endpoint is no longer available. + + + A problem occurred while reading a message. See inner exception for details. + + + The maximum message number for this sequence has been exceeded. The reliable session was faulted. + + + The maximum value for wsrm:MessageNumber has been exceeded. + + + The {0} assertion's Milliseconds attribute does not fall within the range this binding uses. The ReliableSessionBindingElement could not be created. + + + The remote endpoint did not include a final acknowledgement in the reply to the close sequence request message. This is a violation of the WS-ReliableMessaging protocol. The reliable session was faulted. + + + The wsa:MessageId header must be present on a wsrm:{0} message. + + + The returned wsrm:{0}Response message was missing the required wsa:RelatesTo header. This is a violation of the WS-Addressing request reply protocol. The reliable session was faulted. + + + The wsa:ReplyTo header must be present on a wsrm:{0} message. + + + More than one version of the {0} assertion was found. The ReliableSessionBindingElement could not be created. + + + The endpoint only processes messages using the WS-ReliableMessaging protocol. The message sent to the endpoint does not have an action or any headers used by the protocol and cannot be processed. + + + A message with action {0} is an empty message. This message cannot be processed because the body of this WS-ReliableMessaging protocol message must carry information pertaining to a reliable session. + + + The action {0} is not supported by this endpoint. Only WS-ReliableMessaging February 2005 messages are processed by this endpoint. + + + The remote endpoint closed the session before acknowledging all responses. All replies could not be delivered. The reliable session was faulted. + + + The remote endpoint returned a {0}Response when the {0} request had not been sent. This is a WS-ReliableMessaging protocol violation. The reliable session was faulted. + + + The {0}Response was received when the {0} request had not been sent. This is a WS-ReliableMessaging protocol violation. The reliable session cannot continue. + + + The remote endpoint failed to include a required SequenceAcknowledgement header on a reliable reply message. The reliable session was faulted. + + + Due to a request context abort call, the reliable reply session channel potentially has a gap in its reply sequence. The ExactlyOnce assurance can no longer be satisfied. The reliable session was faulted. + + + The required {0} attribute is missing from the {1} element in the {2} assertion. The ReliableSessionBindingElement could not be created. + + + The {0} assertion's required Milliseconds attribute is not schema compliant. Milliseconds must be convertible to an unsigned long. The ReliableSessionBindingElement could not be created. + + + The endpoint at {0} has stopped accepting wsrm sessions. + + + The Sequence is closed and cannot accept new messages. + + + The RM Source could not transfer the last message within the timeout the user specified. + + + The server received a TerminateSequence message before all reply sequence messages were acknowledged. This is a violation of the reply sequence acknowledgement protocol. + + + The wsrm:TerminateSequence protocol message was transmitted before the sequence was successfully completed. + + + The inactivity timeout of ({0}) has been exceeded. + + + Two different wsrm:LastMsgNumber values were specified. Because of this the reliable session cannot complete. + + + The user specified maximum retry count for a particular message has been exceeded. Because of this the reliable session cannot continue. + + + The CloseSequence request's reply message must carry a final acknowledgement. This is a violation of the WS-ReliableMessaging protocol. The reliable session cannot continue. + + + Due to a user abort the reliable session cannot continue. + + + The necessary size to buffer a sequence message has exceeded the configured buffer quota. Because of this the reliable session cannot continue. + + + The session has stopped waiting for a particular reply. Because of this the reliable session cannot continue. + + + A reply message was received with no acknowledgement. + + + All of the reply sequence's messages must be acknowledged prior to closing the request sequence. This is a violation of the reply sequence's delivery guarantee. The session cannot continue. + + + The user of the remote endpoint's reliable session expects no more messages and a new message arrived. Due to this the reliable session cannot continue. + + + The wsrm:LastMsgNumber value is too small. A message with a larger sequence number has already been received. + + + The RM destination received an acknowledgement message. The RM destination does not process acknowledgement messages. + + + The RM source received an AckRequested message. The RM source does not process AckRequested messages. + + + The RM source received an CloseSequence message. The RM source does not process CloseSequence messages. + + + The RM destination received an CloseSequenceResponse message. The RM destination does not process CloseSequenceResponse messages. + + + The RM source received a CreateSequence request. The RM source does not process CreateSequence requests. + + + The RM destination received multiple CreateSequence requests with different OfferId values over the same session. + + + The RM destination received a CreateSequenceResponse message. The RM destination does not process CreateSequenceResponse messages. + + + The RM source received multiple CreateSequenceResponse messages with different sequence identifiers over the same session. + + + The RM source received a TerminateSequence message. The RM source does not process TerminateSequence messages. + + + The RM destination received a TerminateSequenceResponse message. The RM destination does not process TerminateSequenceResponse messages. + + + The RM source does not support an RM destination initiated close since messages can be lost. The reliable session cannot continue. + + + The RM source does not support an RM destination initiated termination since messages can be lost. The reliable session cannot continue. + + + An unknown error occurred while trying to add a sequence message to the window. + + + The remote endpoint specified a last message number that is smaller than a sequence number that has already been seen. The reliable session is in an inconsistent state since it cannot determine the actual last message. The reliable session was faulted. + + + The message could not be transferred within the allotted timeout of {0}. There was no space available in the reliable channel's transfer window. The time allotted to this operation may have been a portion of a longer timeout. + + + The close operation did not complete within the allotted timeout of {0}. The time allotted to this operation may have been a portion of a longer timeout. + + + The open operation did not complete within the allotted timeout of {0}. The time allotted to this operation may have been a portion of a longer timeout. + + + The operation did not complete within the allotted timeout of {0}. The time allotted to this operation may have been a portion of a longer timeout. + + + The request operation did not complete within the allotted timeout of {0}. The time allotted to this operation may have been a portion of a longer timeout. + + + The send operation did not complete within the allotted timeout of {0}. The time allotted to this operation may have been a portion of a longer timeout. + + + The remote endpoint sent an unexpected ack. Simplex servers do not process acks. + + + The remote endpoint sent an unexpected request for an ack. Simplex clients do not send acks and do not process requests for acks. + + + The remote endpoint sent an unexpected close sequence message. Simplex clients do not process this message. + + + The remote endpoint sent an unexpected close sequence response message. Simplex servers do not process this message. + + + The remote endpoint sent an unexpected request to create a sequence. Clients do not process requests for a sequence. + + + The remote endpoint sent an unexpected create sequence response. Servers do not process this message. + + + The remote endpoint sent inconsistent requests to create the same sequence. The OfferId values are not identical. + + + The remote endpoint sent inconsistent responses to the same create sequence request. The sequence identifiers are not identical. + + + The remote endpoint sent an unexpected terminate sequence message. Simplex clients do not process this message. + + + The remote endpoint sent an unexpected terminate sequence response message. Simplex servers do not process this message. + + + The remote endpoint replied to the request for a sequence with a response that could not be parsed. See inner exception for details. The channel could not be opened. + + + The value of wsrm:Identifier is not a known Sequence identifier. + + + The remote endpoint no longer recognizes this sequence. This is most likely due to an abort on the remote endpoint. {0} The reliable session was faulted. + + + The remote endpoint has sent a message containing an unrecognized sequence identifier. The reliable session was faulted. + + + The remote endpoint has sent an unrecognized fault with namespace, {0}, name {1}, and reason {2}. The reliable session was faulted. + + + The remote endpoint has sent an unrecognized fault with namespace, {0}, name {1}, and reason {2}. The channel could not be opened. + + + The remote endpoint closed the sequence before message transfer was complete. This is not supported since all messages could not be transferred. The reliable session was faulted. + + + The remote endpoint terminated the sequence before message transfer was complete. This is not supported since all messages could not be transferred. The reliable session was faulted. + + + The remote endpoint has sent an fault message with an unexpected sequence identifier over a session. The fault may be intended for a different session. The fault reason is: {0} The reliable session was faulted. + + + Binding validation failed because the WSHttpBinding does not support reliable sessions over transport security (HTTPS). The channel factory or service host could not be opened. Use message security for secure reliable messaging over HTTP. + + + The sequence has been terminated by the remote endpoint. {0} The reliable session was faulted. + + + An error occurred while processing a message. {0} + + + The returned {0}Response was carrying the a wsa:RelatesTo header that does not correlate with the wsa:MessageId header on the {0} request. This is a violation of the WS-Addressing request reply protocol. The reliable session cannot continue. + + + The remote endpoint has responded to a {0} request message with an invalid reply. The reply has a wsa:RelatesTo header with an unexpected identifier. The reliable session cannot continue. + + + The remote endpoint sent a wsrm:{0} request message with a wsa:ReplyTo address containing a URI which is not equivalent to the remote address. This is not supported. The reliable session was faulted. + + + The wsrm:{0} request message's wsa:ReplyTo address containing a URI which is not equivalent to the remote address. This is not supported. The reliable session was faulted. + + + The incoming message is not a WS-ReliableMessaging 1.1 message and could not be processed. + + + The RM server requires the use of WS-ReliableMessaging 1.1 protocol. This is likely caused by a binding mismatch. + + + The operations {0} and {1} have the same action ({2}). Every operation must have a unique action value. + + + Cannot create a typed message due to action mismatch, expecting {0} encountered {1} + + + Part {1} in message {0} cannot be exported with RPC or encoded since its type is anonymous. + + + The IAsyncResult returned from Begin and the IAsyncResult supplied to the Callback are on different objects. These are required to be the same object. + + + Binding name cannot be null or empty. + + + Value '{0}' provided for {1} property is an invalid URI. + + + Parameter value '{0}' is an invalid URI. + + + Header name cannot be null or empty. + + + Could not find a base address that matches scheme {0} for the endpoint with binding {1}. Registered base address schemes are [{2}]. + + + The scheme '{0}' used by binding {1} does not match the required scheme '{2}'. + + + Only a '{0}' using '{1}' or '{2}' is supported in this scenario. + + + MessageVersion '{0}' is not supported in this scenario. Only MessageVersion '{1}' is supported. + + + The binding associated with ServiceMetadataBehavior or ServiceDebugBehavior is not supported. The inner binding elements used by this binding must support IReplyChannel. Verify that HttpGetBinding/HttpsGetBinding (on ServiceMetadataBehavior) and HttpHelpPageBinding/HttpsHelpPageBinding (on ServiceDebugBehavior) are supported. + + + Method '{0}' in class '{1}' has bad parameter metadata: a pass-by-reference parameter is marked with the 'in' but not the 'out' parameter mode. + + + Method '{0}' in class '{1}' has bad parameter metadata: a pass-by-value parameter is marked with the 'out' parameter mode. + + + When calling the CreateFromPolicy method, the policy argument must be an XmlElement instance with LocalName '{1}' and NamespaceUri '{0}'. This XmlElement has LocalName '{3}' and NamespaceUri '{2}'. + + + The URI supplied to ServiceMetadataBehavior via the ExternalMetadataLocation property or the externalMetadataLocation attribute in the serviceMetadata section in config must be a relative URI or an absolute URI with an http or https scheme. '{0}' was specified, which is a absolute URI with {1} scheme. + + + The URL supplied to ServiceMetadataBehavior via the ExternalMetadataLocation property or the externalMetadataLocation attribute in the serviceMetadata section in config was a relative URL and there is no base address with which to resolve it. '{0}' was specified. + + + There was a problem reading the MetadataSet argument: a MetadataSection instance with identifier '{0}' and dialect '{1}' has a Metadata property whose type does not match the dialect. The expected Metadata type for this dialect is '{2}' but was found to be '{3}'. + + + Metadata contains a reference that cannot be resolved: '{0}'. + + + The MaximumResolvedReferences property of MetadataExchangeClient must be greater than or equal to one. '{0}' was specified. + + + The MetadataExchangeClient was not supplied with a MetadataReference or MetadataLocation from which to get metadata. You must supply one to the constructor, to the GetMetadata method, or to the BeginGetMetadata method. + + + The MetadataExchangeClient could not create an IChannelFactory for: address='{0}', dialect='{1}', and identifier='{2}'. + + + The MetadataExchangeClient could not create an HttpWebRequest for: address='{0}', dialect='{1}', and identifier='{2}'. + + + The MetadataExchangeClient instance could not be initialized because no Binding is available for scheme '{0}'. You can supply a Binding in the constructor, or specify a configurationName. + + + The TransactionProtocol setting was not understood. A supported protocol must be specified. + + + The MetadataResolver cannot recieve an empty contracts argument to the Resolve or BeginResolve methods. You must supply at least one ContractDescription. + + + The ContractDescriptions in contracts must all have unique Name and Namespace pairs. More than one ContractDescription had the pair Name='{0}' and Namespace='{1}'. + + + The contracts argument to the Resolve or BeginResolve methods cannot contain a null ContractDescription. + + + The binding specified to do metadata exchange does not contain a TransportBindingElement. + + + The binding (Name={0}, Namespace={1}) does not contain a TransportBindingElement. + + + Body object cannot be null in message {0} + + + Type {0} cannot inherit from any class other than object to be used as body object in RPC style. + + + Type {0} implements interface {1} which is not supported for body object in RPC style. + + + CallbackBehaviorAttribute can only be run as a behavior on an endpoint with a duplex contract. Contract '{0}' is not duplex, as it contains no callback operations. + + + This operation would deadlock because the reply cannot be received until the current Message completes processing. If you want to allow out-of-order message processing, specify ConcurrencyMode of Reentrant or Multiple on {0}. + + + In order to use the contract '{0}' with DuplexChannelFactory, the contract must specify a valid callback contract. If your contract does not have a callback contract, consider using ChannelFactory instead of DuplexChannelFactory. + + + The dispatch instance for duplex callbacks cannot be activated - you must provide an instance. + + + ServiceHostBase's AddBaseAddress method cannot be called after the InitializeDescription method has completed. + + + Cannot make a call on this channel because a call to Open() is in progress. + + + The MetadataExchangeClient can only get metadata from absolute addresses. It cannot get metadata from '{0}'. + + + The MetadataExchangeClient can only get metadata from http or https addresses when using MetadataExchangeClientMode HttpGet. It cannot get metadata from '{0}'. + + + The MetadataExchangeClient can only get metadata from http and https MetadataLocations. It cannot get metadata from '{0}'. + + + The configured policy specifies more than one TransactionProtocol across the operations. A single TransactionProtocol for each endpoint must be specified. + + + Generating message contract since the operation {0} is neither RPC nor document wrapped. + + + Generating message contract since the wrapper namespace ({1}) of message {0} does not match the default value ({2}) + + + Generating message contract since the wrapper name ({1}) of message {0} does not match the default value ({2}) + + + Generating message contract since element name {0} from namespace {1} is not marked nillable + + + Generating message contract since message {0} requires protection. + + + Headers are not supported in RPC encoded format. Headers are ignored in message {0}. + + + Generating message contract since message {0} has headers + + + Generating message contract since the operation {0} has untyped Message as argument or return type + + + Generating message contract since message part namespace ({0}) does not match the default value ({1}) + + + There are two contracts listening on the same binding ({2}) and address with conflicting settings. Specifically, the contract '{0}' specifies SessionMode.NotAllowed while the contract '{1}' specifies SessionMode.Required. You should either change one of the SessionMode values or specify a different address (or ListenUri) for each endpoint. + + + This collection does not support setting extensions by index. Please consider using the InsertItem or RemoveItem methods. + + + This ChannelDispatcher is not currently attached to the provided ServiceHost. + + + Cannot add a ChannelDispatcher to more than one ServiceHost. + + + Cannot open ChannelDispatcher because it is not attached to a ServiceHost. + + + Cannot open ChannelDispatcher because it is does not have a MessageVersion set. + + + The ChannelDispatcher at '{0}' is unable to open its IChannelListener as there are no endpoints for the ChannelDispatcher. + + + The ChannelDispatcher at '{0}' with contract(s) '{1}' is unable to open its IChannelListener. + + + The type argument passed to the generic ChannelFactory class must be an interface type. + + + ApplyConfiguration requires that the Endpoint property be initialized. Either provide a valid ServiceEndpoint in the CreateDescription method or override the ApplyConfiguration method to provide an alternative implementation. + + + CreateFactory requires that the Endpoint property be initialized. Either provide a valid ServiceEndpoint in the CreateDescription method or override the CreateFactory method to provide an alternative implementation. + + + An operation marked as IsTerminating has already been invoked on this channel, causing the channel's connection to terminate. No more operations may be invoked on this channel. Please re-create the channel to continue communication. + + + This channel can no longer be used to send messages as the output session was auto-closed due to a server-initiated shutdown. Either disable auto-close by setting the DispatchRuntime.AutomaticInputSessionShutdown to false, or consider modifying the shutdown protocol with the remote server. + + + Array of type {0} is not supported. + + + Can only store into ArgBuilder or LocalBuilder. Got: {0}. + + + Expecting End {0}. + + + {0} is not assignable from {1}. + + + No conversion possible to {0}. + + + CODEGEN: {0} + + + Internal Error: Unrecognized constant type {0}. + + + This collection does not support setting items by index. + + + This operation is not supported because the collection is read-only. + + + The collection of type {0} does not support values of type {1}. + + + Top level XML element with name {0} in namespace {1} cannot reference {2} type because it already references a different type ({3}). Use a different operation name or MessageBodyMemberAttribute to specify a different name for the Message or Message parts. + + + Duplicate top level XML Schema type with name {0} in namespace {1}. + + + The value of OperationContext.Current is not the OperationContext value installed by this OperationContextScope. + + + ContractDescription's Name must be a non-empty string. + + + ContractDescription '{0}' has zero operations; a contract must have at least one operation. + + + ContractDescription '{0}' has zero IsInitiating=true operations; a contract must have at least one IsInitiating=true operation. + + + The service class of type {0} both defines a ServiceContract and inherits a ServiceContract from type {1}. Contract inheritance can only be used among interface types. If a class is marked with ServiceContractAttribute, it must be the only type in the hierarchy with ServiceContractAttribute. Consider moving the ServiceContractAttribute on type {1} to a separate interface that type {1} implements. + + + The service class of type {0} both defines a ServiceContract and inherits a ServiceContract from type {1}. Contract inheritance can only be used among interface types. If a class is marked with ServiceContractAttribute, then another service class cannot derive from it. + + + SynchronizedReadOnlyCollection's CopyTo only works if the underlying list implements ICollection. + + + The callback contract of contract {0} either does not exist or does not define any operations. If this is not a duplex contract, consider using ChannelFactory instead of DuplexChannelFactory. + + + This CreateChannel overload cannot be called on this instance of DuplexChannelFactory, as the DuplexChannelFactory was not initialized with an InstanceContext. Please call the CreateChannel overload that takes an InstanceContext. + + + This CreateChannel overload cannot be called on this instance of DuplexChannelFactory, as the DuplexChannelFactory was initialized with a Type and no valid InstanceContext was provided. Please call the CreateChannel overload that takes an InstanceContext. + + + This CreateChannel overload cannot be called on this instance of DuplexChannelFactory, as the InstanceContext provided to the DuplexChannelFactory does not contain a valid UserObject. + + + The InstanceContext provided to the ChannelFactory contains a UserObject that does not implement the CallbackContractType '{0}'. + + + ChannelFactory does not support the contract {0} as it defines a callback contract with one or more operations. Please consider using DuplexChannelFactory instead of ChannelFactory. + + + The CustomBinding on the ServiceEndpoint with contract '{0}' lacks a TransportBindingElement. Every binding must have at least one binding element that derives from TransportBindingElement. + + + The Scheme cannot be computed for this binding because this CustomBinding lacks a TransportBindingElement. Every binding must have at least one binding element that derives from TransportBindingElement. + + + The formatter threw an exception while trying to deserialize the message: {0} + + + This operation is not possible since the dictionary is empty. + + + The type or member named '{0}' could not be loaded because it has two incompatible attributes: '{1}' and '{2}'. To fix the problem, remove one of the attributes from the type or member. + + + The endpoint's address is not specified. + + + The endpoint's contract is not specified. + + + The endpoint's binding is not specified. + + + The channel is configured to use interactive initializer '{0}', but the channel was Opened without calling DisplayInitializationUI. Call DisplayInitializationUI before calling Open or other methods on this channel. + + + AllowInitializationUI was set to false for this channel, but the channel is configured to use the '{0}' as an interactive initializer. + + + This is a Windows&#169; Communication Foundation service.<BR/><BR/><B>Metadata publishing for this service is currently disabled.</B><BR/><BR/>If you have access to the service, you can enable metadata publishing by completing the following steps to modify your web or application configuration file:<BR/><BR/>1. Create the following service behavior configuration, or add the &lt;serviceMetadata&gt; element to an existing service behavior configuration: + + + 2. Add the behavior configuration to the service: + + + Note: the service name must match the configuration name for the service implementation.<BR/><BR/>3. Add the following endpoint to your service configuration: + + + Note: your service must have an http base address to add this endpoint.<BR/><BR/>The following is an example service configuration file with metadata publishing enabled: + + + For more information on publishing metadata please see the following documentation: <a href="http://go.microsoft.com/fwlink/?LinkId=65455">http://go.microsoft.com/fwlink/?LinkId=65455</a>. + + + Note: the service name must match the configuration name for the service implementation. + + + Add the following endpoint. + + + Note: your service must have an http base address to add this endpoint. + + + Add the following element to your service behavior configuration. + + + <P class='intro'><B>C#</B></P> + + + <P class='intro'><B>Visual Basic</B></P> + + + Service + + + {0} Service + + + You have created a service.<P class='intro'>To test this service, you will need to create a client and use it to call the service. You can do this using the svcutil.exe tool from the command line with the following syntax:</P> + + + You have created a service.<P class='intro'>To test this service, you will need to create a client and use it to call the service; however, metadata publishing via ?WSDL is currently disabled. This can be enabled via the service's configuration file. </P> + + + This will generate a configuration file and a code file that contains the client class. Add the two files to your client application and use the generated client class to call the Service. For example:<BR/> + + + Use the 'client' variable to call operations on the service. + + + Always close the client. + + + The service encountered an error. + + + Operation '{0}' could not be loaded as it uses an unsupported combination of Use and Style settings: Document with Encoded. To fix the problem, change the Use setting to Literal or change the Style setting to Rpc. + + + Fault could not be loaded as the Use setting is Encoded and it references a schema definition using Element attribute. To fix the problem, change the Use setting to Literal. + + + Message part {0} in namespace {1} appears more than once in Message. + + + This service has multiple endpoints listening at '{0}' which share the same initiating action '{1}'. As a result, messages with this action would be dropped since the dispatcher would not be able to determine the correct endpoint for handling the message. Please consider hosting these Endpoints at separate ListenUris. + + + The IEndpointBehavior '{0}' cannot be used on the server side; this behavior can only be applied to clients. + + + Cannot add EndpointDispatcher to more than one ChannelDispatcher. + + + This EndpointDispatcher is not currently attached to the provided ChannelDispatcher. + + + Error creating a reader for the MTOM message + + + Error in deserializing body of request message for operation '{0}'. + + + Error in deserializing body of request message for operation '{0}'. {1} + + + Error in deserializing body of reply message for operation '{0}'. + + + Error in deserializing body of reply message for operation '{0}'. {1} + + + There was an error in serializing body of message {0}: '{1}'. Please see InnerException for more details. + + + There was an error in deserializing one of the headers in message {0}. Please see InnerException for more details. + + + There was an error in serializing one of the headers in message {0}: '{1}'. Please see InnerException for more details. + + + Server returned an invalid SOAP Fault. Please see InnerException for more details. + + + An error occurred while loading attribute '{0}' on type '{1}'. Please see InnerException for more details. + + + An error occurred while loading attribute '{0}' on method '{1}' in type '{2}'. Please see InnerException for more details. + + + An error occurred while loading attribute '{0}' on parameter {1} of method '{2}' in type '{3}'. Please see InnerException for more details. + + + An error occurred while loading attribute '{0}'. Please see InnerException for more details. + + + --- End of inner ExceptionDetail stack trace --- + + + An ExceptionDetail, likely created by IncludeExceptionDetailInFaults=true, whose value is: + + + Internal Error: Message must be a valid IMethodCallMessage. + + + The specified ContractDescription could not be exported to WSDL because the Type property of the MessagePartDescription with name '{1}' in the OperationDescription with name '{0}' is not set. The Type property must be set in order to create WSDL. + + + Fault named {0} in operation {1} cannot be imported. {2} + + + In operation {0}, more than one fault is declared with detail type {1} + + + In operation {0}, more than one fault is declared with element name {1} in namespace {2} + + + {0}: {1} (Fault Detail is equal to {2}). + + + The creator of this fault did not specify a Reason. + + + In operation {0}, the schema type corresponding to the fault detail type {1} is anonymous. Please set Fault name explicitly to export anonymous types. + + + Header name mismatch in member {1} of type {0}. The header name found in the description is {2}. The element name deduced by the formatter is {3}. This mismatch can happen if the ElementName specified in XmlElementAttribute or XmlArrayAttribute does not match the name specified in the MessageHeaderAttribute or MessageHeaderArrayAttribute or the member name. + + + Header name mismatch in operation {0} from contract {1}:{2}. The header name found in the description is {3}. The element name deduced by the formatter is {4}. This mismatch can happen if the ElementName specified in XmlElementAttribute or XmlArrayAttribute does not match the name specified in the MessageHeaderAttribute or MessageHeaderArrayAttribute or the member name. + + + Header namespace mismatch in member {1} of type {0}. The header namespace found in the description is {2}. The element namespace deduced by the formatter is {3}. This mismatch can happen if the Namespace specified in XmlElementAttribute or XmlArrayAttribute does not match the namespace specified in the MessageHeaderAttribute or MessageHeaderArrayAttribute or the contract namespace. + + + Header namespace mismatch in operation {0} from contract {1}:{2}. The header namespace found in the description is {3}. The element namespace deduced by the formatter is {4}. This mismatch can happen if the Namespace specified in XmlElementAttribute or XmlArrayAttribute does not match the namespace specified in the MessageHeaderAttribute or MessageHeaderArrayAttribute or the contract namespace. + + + The header '{0}' from the namespace '{1}' was not understood by the recipient of this message, causing the message to not be processed. This error typically indicates that the sender of this message has enabled a communication protocol that the receiver cannot process. Please ensure that the configuration of the client's binding is consistent with the service's binding. + + + Message {0} must not have headers to be used in RPC encoded style. + + + This value cannot be changed after the ServiceHost has opened. + + + This value cannot be changed after the ChannelFactory has opened. + + + This value cannot be changed after the first ClientBase of type '{0}' has been created. + + + {0} cannot be changed after the ServiceHost has opened. + + + Operation {0} binding {1} has extra part {2} that is not present in other bindings + + + Style {1} on header {0} does not match expected style {2}. + + + All parts of message in operation {0} must either contain type or element. + + + Style {1} inferred from messages in operation {0} does not match expected style {2} specified via bindings. + + + Bindings for operation {0} cannot specify different use and style values. Binding {1} specifies use {2} and style {3} while binding {4} specifies use {5} and style {6}. + + + Extensions for operation {0} in binding {1} cannot specify different use values. + + + Message bindings for operation {0} in binding {1} cannot specify different use values. + + + Fault bindings for operation {0} in binding {1} cannot specify different use values. + + + Service implementation object invoked with wrong number of input parameters, operation expects {0} parameters but was called with {1} parameters. + + + Service implementation object invoked with null input parameters, but operation expects {0} parameters. + + + The InstanceContext has no provider for creating Service implementation objects. + + + This OperationContextScope is being disposed out of order. + + + The server was unable to process the request due to an internal error. For more information about the error, either turn on IncludeExceptionDetailInFaults (either from ServiceBehaviorAttribute or from the <serviceDebug> configuration behavior) on the server in order to send the exception information back to the client, or turn on tracing as per the Microsoft .NET Framework SDK documentation and inspect the server trace logs. + + + The client was unable to process the callback request due to an internal error. For more information about the error, either turn on IncludeExceptionDetailInFaults (either from CallbackBehaviorAttribute or from the <clientDebug> configuration behavior) on the client in order to send the exception information back to the server, or turn on tracing as per the Microsoft .NET Framework SDK documentation and inspect the client trace logs. + + + IAsyncResult's State must be the state argument passed to your Begin call. + + + IAsyncResult not provided or of wrong type. + + + The CallbackContract {0} is invalid because it is not an interface type. + + + Invalid IContextChannel passed to OperationContext. Must be either a server dispatching channel or a client proxy channel. + + + This OperationContextScope is being disposed on a different thread than it was created. + + + OperationFormatter encountered an invalid Message body. Expected to find node type 'Element' with name '{0}' and namespace '{1}'. Found node type '{2}' with name '{3}' and namespace '{4}' + + + The OperationFormatter could not deserialize any information from the Message because the Message is empty (IsEmpty = true). + + + There was an error while trying to serialize parameter {0}:{1}. The InnerException message was '{2}'. Please see InnerException for more details. + + + There was an error while trying to deserialize parameter {0}:{1}. Please see InnerException for more details. + + + There was an error while trying to deserialize parameter {0}:{1}. The InnerException message was '{2}'. Please see InnerException for more details. + + + The operation {0} either has a parameter or a return type that is attributed with MessageContractAttribute. In order to represent the request message using a Message Contract, the operation must have a single parameter attributed with MessageContractAttribute. In order to represent the response message using a Message Contract, the operation's return value must be a type that is attributed with MessageContractAttribute and the operation may not have any out or ref parameters. + + + MessageHeaderArrayAttribute found on member {0} is not a single dimensional array. + + + Outgoing request message for operation '{0}' specified Action='{1}', but contract for that operation specifies Action='{2}'. The Action specified in the Message must match the Action in the contract, or the operation contract must specify Action='*'. + + + Outgoing reply message for operation '{0}' specified Action='{1}', but contract for that operation specifies ReplyAction='{2}'. The Action specified in the Message must match the ReplyAction in the contract, or the operation contract must specify ReplyAction='*'. + + + In order to use Streams with the MessageContract programming model, the type {0} must have a single member with MessageBodyMember attribute and the member type must be Stream. + + + For request in operation {0} to be a stream the operation must have a single parameter whose type is Stream. + + + For response in operation {0} to be a stream the operation must have a single out parameter or return value whose type is Stream. + + + Buffer size must be at least {0} bytes. + + + The PrimitiveOperationFormatter was given a parameter or return type which it does not support. + + + The static CreateChannel method cannot be used with the contract {0} because that contract defines a callback contract. Please try using one of the static CreateChannel overloads on DuplexChannelFactory<TChannel>. + + + XmlSerializer attribute {0} is not valid in {1}. Only SoapElement attribute is supported. + + + XmlSerializer attribute {0} is not valid in {1}. Only XmlElement, XmlArray, XmlArrayItem and XmlAnyElement attributes are supported in MessageContract when IsWrapped is false. + + + XmlSerializer attribute {0} is not valid in {1}. Only XmlElement, XmlArray, XmlArrayItem, XmlAnyAttribute and XmlAnyElement attributes are supported when IsWrapped is true. + + + {0} must contain either a single ServiceKnownTypeAttribute that refers to a method or a set of ServiceKnownTypeAttributes, each specifying a valid type + + + The return type of method {1} in type {2} must be IEnumerable<Type> to be used by ServiceKnownTypeAttribute in {0} + + + ServiceKnownTypeAttribute in {0} refers to a method {1} that does not exist in type {2} + + + KnownType cannot be null in operation {0} + + + The type {1} defines a MessageContract but also derives from a type {0} that does not define a MessageContract. All of the objects in the inheritance hierarchy of {1} must defines a MessageContract. + + + The message cannot be deserialized into MessageContract type {0} since it does not have a default (parameterless) constructor. + + + MessageOperationFormatter cannot serialize faults. + + + The value '{0}' is not valid for the Location property. The Location property must be a valid absolute or relative URI. + + + Method {0} is not supported on this proxy, this can happen if the method is not marked with OperationContractAttribute or if the interface type is not marked with ServiceContractAttribute. + + + Callback method {0} is not supported, this can happen if the method is not marked with OperationContractAttribute or if its interface type is not the target of the ServiceContractAttribute's CallbackContract. + + + ServiceHost implementation type {0} does not implement ServiceContract {1}. + + + A DispatchOperation (or ClientOperation) can only be added to its parent DispatchRuntime (or ClientRuntime). + + + No Action header was found with namespace '{0}' for the given message. + + + Calling Post() on '{0}' resulted in multiple callbacks. This indicates a problem in '{0}'. + + + The callback passed to operation '{0}' was called more than once. This indicates an internal error in the implementation of that operation. + + + Method {0} in type {1} has more than one header part of type array of XmlElement. + + + A ServiceContract has more the one operation with an Action of "*". A ServiceContract can have at most one operation an Action = "*". + + + The Service contains multiple ServiceEndpoints with different ContractDescriptions which each have Name='{0}' and Namespace='{1}'. Either provide ContractDescriptions with unique Name and Namespaces, or ensure the ServiceEndpoints have the same ContractDescription instance. + + + Part {1}:{0} is repeating and is not supported in Soap Encoding. + + + The Name property must be a non-empty string. + + + The ConfigurationName property must be a non-empty string. + + + Cannot handle invocation of {0} on interface {1} because the OperationSelector on ClientRuntime is null. + + + The service type provided could not be loaded as a service because it does not have a default (parameter-less) constructor. To fix the problem, add a default constructor to the type, or pass an instance of the type to the host. + + + The contract specified by type '{0}' is ambiguous. The type derives from at least two different types that each define its own service contract. For this type to be used as a contract type, exactly one of its inherited contracts must be more derived than any of the others. + + + Extension {0} prevented call to operation '{1}' from replying by setting the reply to null. + + + Formatter {0} returned a null reply message for call to operation '{1}'. + + + The operation '{0}' could not be completed because the sessionful channel timed out waiting to receive a message. To increase the timeout, either set the receiveTimeout property on the binding in your configuration file, or set the ReceiveTimeout property on the Binding directly. + + + {0} must be a relative URI or an absolute URI with scheme '{1}'. '{2}' is an absolute URI with scheme '{3}'. + + + The HttpGetEnabled property of ServiceMetadataBehavior is set to true and the HttpGetUrl property is a relative address, but there is no http base address. Either supply an http base address or set HttpGetUrl to an absolute address. + + + The HttpsGetEnabled property of ServiceMetadataBehavior is set to true and the HttpsGetUrl property is a relative address, but there is no https base address. Either supply an https base address or set HttpsGetUrl to an absolute address. + + + The ChannelDispatcher with ListenUri '{0}' has endpoints with the following contracts: {1}. Metadata endpoints cannot share ListenUris. The conflicting endpoints were either specified in AddServiceEndpoint() calls, in a config file, or a combination of AddServiceEndpoint() and config. + + + Service implementation type is an interface or abstract class and no implementation object was provided. + + + This property sets EnableFaults on the client. To set EnableFaults on the server, use ChannelDispatcher's EnableFaults. + + + This property sets ManualAddressing on the client. To set ManualAddressing on the server, use ChannelDispatcher's ManualAddressing. + + + TransactedBatchingBehavior validation failed. Service or client cannot be started. Transacted batching is not supported for session contracts. Remove transacted batching behavior from the endpoint or define a non-sessionful contract. + + + TransactedBatchingBehavior validation failed. Service cannot be started. Transacted batching requires ServiceBehavior.ReleaseServiceInstanceOnTransactionComplete to be false. + + + The service implementation object was not initialized or is not available. + + + The WS-Addressing "none" value is not valid for the August 2004 version of WS-Addressing. + + + An object that is not an exception was thrown. + + + The operation '{0}' cannot be the first operation to be called because IsInitiating is false. + + + There was no CLR type specified for parameter {0}, preventing the operation from being generated. + + + The one-way operation '{1}' on ServiceContract '{0}' is configured for transaction flow. Transactions cannot be flowed over one-way operations. + + + The incoming message with action could not be processed because it is targeted at a request-reply operation, but cannot be replied to as the MessageId property is not set. + + + OperationBehaviorAttribute can only go on the service class, it cannot be put on the ServiceContract interface. Method '{0}' on type '{1}' violates this. + + + The ReleaseInstanceMode property on OperationBehaviorAttribute can only be set on non-callback operations. Method '{0}' violates this. + + + Method '{0}' has OperationContractAttribute, but enclosing type '{1}' does not have ServiceContractAttribute. OperationContractAttribute can only be used on methods in ServiceContractAttribute types or on their CallbackContract types. + + + Method '{1}' has {0}, but enclosing type '{2}' does not have ServiceContractAttribute. {0} can only be used on methods in ServiceContractAttribute types. + + + OperationDescription's Name must be a non-empty string. + + + All parameter names used in operations that make up a service contract must not be null. + + + OperationDescription '{0}' is invalid because its Messages property contains an invalid number of MessageDescription instances. Each OperationDescription must have one or two messages. + + + There was a mismatch between the number of supplied arguments and the number of expected arguments. Specifically, the argument '{0}' has '{1}' elements while the argument '{2}' has '{3}' elements. + + + The 'parameters' argument must be an array that contains a single Message object. + + + The 'parameters' argument must be either null or an empty array. + + + The 'parameters' argument must be an array of one element. + + + Message part name {0} is not unique in an RPC Message. + + + The contract '{0}' has at least one operation annotated with '{1}', but the binding used for the contract endpoint at address '{2}' does not support required binding property '{3}'. Please ensure that the binding used for the contract supports the ReceiveContext capability. + + + Required message property '{0}' is missing from the IncomingProperties collections of the received message. Ensure that when the receive context is enabled on the binding, the created channel ensures that '{0}' is present on all received messages. + + + The request message has ReplyTo='{0}' but IContextChannel.LocalAddress is '{1}'. When ManualAddressing is false, these values must be the same, null, or EndpointAddress.AnonymousAddress. Enable ManualAddressing or avoid setting ReplyTo on the message. + + + The request message has FaultTo='{0}' but IContextChannel.LocalAddress is '{1}'. When ManualAddressing is false, these values must be the same, null, or EndpointAddress.AnonymousAddress. Enable ManualAddressing or avoid setting FaultTo on the message. + + + The request message has From='{0}' but IContextChannel.LocalAddress is '{1}'. When ManualAddressing is false, these values must be the same, null, or EndpointAddress.AnonymousAddress. Enable ManualAddressing or avoid setting From on the message. + + + The request message has ReplyTo='{0}' but IContextChannel.RemoteAddress is '{1}'. When ManualAddressing is false, these values must be the same, null, or EndpointAddress.AnonymousAddress because sending a reply to a different address than the original sender can create a security risk. If you want to process such messages, enable ManualAddressing. + + + The request message has FaultTo='{0}' but IContextChannel.RemoteAddress is '{1}'. When ManualAddressing is false, these values must be the same, null, or EndpointAddress.AnonymousAddress because sending a reply to a different address than the original sender can create a security risk. If you want to process such messages, enable ManualAddressing. + + + The request message has From='{0}' but IContextChannel.RemoteAddress is '{1}'. When ManualAddressing is false, these values must be the same, null, or EndpointAddress.AnonymousAddress because sending a reply to a different address than the original sender can create a security risk. If you want to process such messages, enable ManualAddressing. + + + A message was received with a WS-Addressing ReplyTo or FaultTo header targeted at the "None" address. These values are not valid for request-reply operations. Please consider using a one-way operation or enabling ManualAddressing if you need to support ReplyTo or FaultTo values of "None." + + + This request operation did not receive a reply within the configured timeout ({0}). The time allotted to this operation may have been a portion of a longer timeout. This may be because the service is still processing the operation or because the service was unable to send a reply message. Please consider increasing the operation timeout (by casting the channel/proxy to IContextChannel and setting the OperationTimeout property) and ensure that the service is able to connect to the client. + + + This request operation sent to {0} did not receive a reply within the configured timeout ({1}). The time allotted to this operation may have been a portion of a longer timeout. This may be because the service is still processing the operation or because the service was unable to send a reply message. Please consider increasing the operation timeout (by casting the channel/proxy to IContextChannel and setting the OperationTimeout property) and ensure that the service is able to connect to the client. + + + A reply message was received for operation '{0}' with action '{1}'. However, your client code requires action '{2}'. + + + Required runtime property '{0}' is not initialized on DispatchRuntime. Do not remove ServiceBehaviorAttribute from ServiceDescription.Behaviors or ensure that you include a third-party service behavior that supplies this value. + + + The MetadataExchangeClient has resolved more than MaximumResolvedReferences. + + + The 'result' argument must be of type Message. + + + Could not revert impersonation on current thread. Continuing would compromise system security. Terminating process. + + + RPC Message {1} in operation {0} has an invalid body name {2}. It must be {3} + + + RPC Message {1} in operation {0} must have a single MessageBodyMember. + + + There was a problem loading the XSD documents provided: a reference to a schema element with name '{0}' and namespace '{1}' could not be resolved because the element definition could not be found in the schema for targetNamespace '{1}'. Please check the XSD documents provided and try again. + + + There was a problem loading the XSD documents provided: a reference to a schema type with name '{0}' and namespace '{1}' could not be resolved because the type definition could not be found in the schema for targetNamespace '{1}'. Please check the XSD documents provided and try again. + + + Service description message '{1}' from target namespace '{2}' does not contain part named '{0}'. + + + Schema with target namespace '{0}' could not be found. + + + SecurityContextProperty is missing from the request Message, this may indicate security is configured incorrectly. + + + The server did not provide a meaningful reply; this might be caused by a contract mismatch, a premature session shutdown or an internal server error. + + + Endpoints cannot be added after the ServiceHost has been opened/faulted/aborted/closed. + + + Endpoints cannot be added before the Description property has been initialized. + + + ApplyConfiguration requires that the Description property be initialized. Either provide a valid ServiceDescription in the CreateDescription method or override the ApplyConfiguration method to provide an alternative implementation. + + + LoadConfigurationSection requires that the Description property be initialized. Provide a valid ServiceDescription in the CreateDescription method. + + + InitializeRuntime requires that the Description property be initialized. Either provide a valid ServiceDescription in the CreateDescription method or override the InitializeRuntime method to provide an alternative implementation. + + + InitializeDescription must be called with a serviceType or singletonInstance parameter. + + + Header properties cannot be set in MessageHeaderAttribute of {0} as its type is MessageHeader<T>. + + + An exception has been thrown when reading the stream. + + + The message containing this stream has been closed. Note that request streams cannot be accessed after the service operation returns. + + + The message containing this stream has been closed. + + + This channel cannot send any more messages because IsTerminating operation '{0}' has already been called. + + + Throttle limit must be greater than zero. To disable, set to Int32.MaxValue. + + + The timeout value provided was not of a recognized format. Please see InnerException for more details. + + + Timeout must be greater than or equal to TimeSpan.Zero. To disable timeout, specify TimeSpan.MaxValue. + + + Timeouts larger than Int32.MaxValue TotalMilliseconds (approximately 24 days) cannot be honored. To disable timeout, specify TimeSpan.MaxValue. + + + Cannot create a unique part name for {0}. + + + An unrecognized element was encountered in the XML during deserialization which was ignored. + + + TransactedBatchingBehavior validation failed. The service endpoint cannot be started. TransactedBatchingBehavior requires a binding that contains a binding element ITransactedBindingElement that returns true for ITransactedBindingElement.TransactedReceiveEnabled. If you are using NetMsmqBinding or MsmqIntegrationBinding make sure that ExactlyOnce is set to true. + + + TThe operation '{1}' on contract '{0}' is configured with TransactionAutoComplete set to false and with ConcurrencyMode not set to Single. TransactionAutoComplete set to false requires ConcurrencyMode.Single. + + + The '{0}' service is configured with ReleaseServiceInstanceOnTransactionComplete set to true, but the ConcurrencyMode is not set to Single. The ReleaseServiceInstanceOnTransactionComplete requires the use of ConcurrencyMode.Single. + + + The '{0}' service is configured with EnsureOrderedDispatch set to true, but the ConcurrencyMode is not set to Single. EnsureOrderedDispatch requires the use of ConcurrencyMode.Single. + + + The DispatchRuntime.EnsureOrderedDispatch property is set to true, but the DispatchRuntime.ConcurrencyMode is not set to Single. EnsureOrderedDispatch requires the use of ConcurrencyMode.Single. + + + The service does not support concurrent transactions. + + + The transaction under which this method call was executing was asynchronously aborted. + + + The SetTransactionComplete method was called in the operation '{0}' on contract '{1}' when TransactionAutoComplete was set to true. The SetTransactionComplete method can only be called when TransactionAutoComplete is set to false. This is an invalid scenario and the current transaction was aborted. + + + The SetTransactionComplete method was wrongly called more than once in the operation '{0}' on contract '{1}'. The SetTransactionComplete method can only be called once. This is an invalid scenario and the current transaction was aborted. + + + The binding for the endpoint at address '{0}' is configured with both the MsmqTransportBindingElement and the TransactionFlowBindingElement. These two elements cannot be used together. + + + The operation '{1}' on contract '{0}' is configured with TransactionAutoComplete set to false and the InstanceContextMode is not set to PerSession. TransactionAutoComplete set to false requires the use of InstanceContextMode.PerSession. + + + The operation '{0}' on callback contract '{1}' is configured with TransactionAutoComplete set to false. TransactionAutoComplete set to false cannot be used with operations on callback contracts. + + + The operation '{1}' on contract '{0}' is configured with TransactionAutoComplete set to false but SessionMode is not set to Required. TransactionAutoComplete set to false requires SessionMode.Required. + + + The service '{0}' is configured with TransactionAutoCompleteOnSessionClose set to true and with an InstanceContextMode not set to PerSession. TransactionAutoCompleteOnSessionClose set to true requires an instancing mode that uses sessions. + + + The service '{0}' is configured with a TransactionTimeout but no operations are configured with TransactionScopeRequired set to true. TransactionTimeout requires at least one operation with TransactionScopeRequired set to true. + + + The service '{0}' is configured with a TransactionIsolationLevel but no operations are configured with TransactionScopeRequired set to true. TransactionIsolationLevel requires at least one operation with TransactionScopeRequired set to true. + + + The service '{0}' is configured with ReleaseServiceInstanceOnTransactionComplete but no operations are configured with TransactionScopeRequired set to true. The ReleaseServiceInstanceOnTransactionComplete property requires at least one operation with TransactionScopeRequired set to true. Remove the ReleaseServiceInstanceOnTransactionComplete property from the service if this is the case. + + + The service '{0}' is configured with TransactionAutoCompleteOnSessionClose, but no operations are configured with TransactionScopeRequired set to true. The TransactionAutoCompleteOnSessionClose property requires at least one operation with TransactionScopeRequired set to true. Remove the TransactionAutoCompleteOnSessionClose property from the service if this is the case. + + + The service operation requires a transaction to be flowed. + + + The flowed transaction could not be unmarshaled. The following exception occurred: {0} + + + The incoming transaction cannot be deserialized. The transaction header in the message was either malformed or in an unrecognized format. The client and the service must be configured to use the same protocol and protocol version. The following exception occurred: {0} + + + The transaction header '{0}' within the namespace '{1}' was not understood by the service. The client and the service must be configured to use the same protocol and protocol version ('{2}'). + + + An attempt was made to add more than one transaction to a message. At most one transaction can be added. + + + Internal Error: The instance of the MessageContract cannot be null in {0}. + + + The operation '{0}' could not be loaded because it specifies "rpc-style" in "literal" mode, but uses message contract types or the CoreWCF.Channels.Message. This combination is disallowed -- specify a different value for style or use parameters other than message contract types or System.ServiceModel.Channels.Message. + + + The operation '{0}' could not be loaded because it has a parameter or return type of type CoreWCF.Channels.Message or a type that has MessageContractAttribute and other parameters of different types. When using System.ServiceModel.Channels.Message or types with MessageContractAttribute, the method must not use any other types of parameters. + + + When using the rpc-encoded style, message contract types or the CoreWCF.Channels.Message type cannot be used if the operation has no parameters or has a void return value. Add a blank message contract type as a parameter or return type to operation '{0}'. + + + This fault did not provide a matching translation: {0} + + + This fault did not provide a reason (MessageFault.Reason was null). + + + This fault did not provide a reason (MessageFault.Reason.Translations.Count was 0). + + + User operation '{0}.{1}' threw an exception that is unhandled in user code. This exception will be rethrown. If this is a recurring problem, it may indicate an error in the implementation of the '{0}.{1}' method. + + + Parameter '{0}' requires additional schema information that cannot be captured using the parameter mode. The specific attribute is '{1}'. + + + In order to use one of the ServiceHost constructors that takes a service instance, the InstanceContextMode of the service must be set to InstanceContextMode.Single. This can be configured via the ServiceBehaviorAttribute. Otherwise, please consider using the ServiceHost constructors that take a Type argument. + + + Cannot add outgoing headers to message as MessageVersion in OperationContext.Current '{0}' does not match with the header version of message being processed '{1}'. + + + When multiple endpoints on a service share the same ListenUri, those endpoints must all have the same Identity in their EndpointAddress. The endpoints at ListenUri '{0}' do not meet this criteria. + + + Wrapper element name cannot be empty. + + + Wrapper type for message {0} cannot be projected as a data contract type since it has multiple namespaces. Consider using the XmlSerializer + + + WSDL part {0} in message {1} from namespace {2} must have either an element or a type name + + + DataContractSerializer does not support collection specified on element '{0}' + + + Invalid OperationFormatUse specified in the OperationFormatStyle of operation {0}, DataContractSerializer supports only Literal. + + + XmlArrayAttribute cannot be used in repeating part {1}:{0}. + + + Could not find default endpoint element that references contract '{0}' in the ServiceModel client configuration section. This might be because no configuration file was found for your application, or because no endpoint element matching this contract could be found in the client element. + + + Could not find endpoint element with name '{0}' and contract '{1}' in the ServiceModel client configuration section. This might be because no configuration file was found for your application, or because no endpoint element matching this name could be found in the client element. + + + The Address property on ChannelFactory.Endpoint was null. The ChannelFactory's Endpoint must have a valid Address specified. + + + In order to generate configuration information using the GenerateServiceEndpoint method, the ServiceContractGenerator instance must have been initialized with a valid Configuration object. + + + The ServiceHost close operation timed out after {0}. This could be because a client failed to close a sessionful channel within the required time. The time allotted to this operation may have been a portion of a longer timeout. + + + Close process timed out waiting for service dispatch to complete. + + + The WSDL binding named {0} is not valid because no match for operation {1} was found in the corresponding portType definition. + + + The WSDL binding named {0} is not valid because an operation binding doesn't have a name specified. + + + The underlying channel factory could not be created because no binding information was found in the configuration file for endpoint with name '{0}'. Please check the endpoint configuration section with name '{0}' to ensure that binding information is present and correct. + + + The underlying channel factory could not be created because no Binding was passed to the ChannelFactory. Please supply a valid Binding instance via the ChannelFactory constructor. + + + The endpoint configuration section for contract '{0}' with name '{1}' could not be loaded because more than one endpoint configuration with the same name and contract were found. Please check your config and try again. + + + An endpoint configuration section for contract '{0}' could not be loaded because more than one endpoint configuration for that contract was found. Please indicate the preferred endpoint configuration section by name. + + + In operation '{0}', cannot pass null to methods that take Message as input parameter. + + + In operation '{0}', cannot return null from methods that return Message. + + + ServiceHost only supports class service types. + + + The contract name '{0}' could not be found in the list of contracts implemented by the service '{1}'. + + + In order to add an endpoint to the service '{0}', a non-empty contract name must be specified. + + + The contract name 'IMetadataExchange' could not be found in the list of contracts implemented by the service {0}. Add a ServiceMetadataBehavior to the configuration file or to the ServiceHost directly to enable support for this contract. + + + The contract type {0} is not attributed with ServiceContractAttribute. In order to define a valid contract, the specified type (either contract interface or service class) must be attributed with ServiceContractAttribute. + + + An endpoint for type '{0}' could not be added because the ServiceHost instance was not initialized properly. In order to add endpoints by Type, the CreateDescription method must be called. If you are using a class derived from ServiceHost, ensure that the class is properly calling base.CreateDescription. + + + Instance of MessagePartDescription Name='{0}' Namespace='{1}' cannot be used in this context: required 'Type' property was not set. + + + The wsdl operation input {0} in portType {1} does not reference a message. This is either because the message attribute is missing or empty. + + + The wsdl operation output {0} in portType {1} does not reference a message. This is either because the message attribute is missing or empty. + + + The wsdl operation {0} in portType {1} contains a fault that does not reference a message. This is either because the message attribute is missing or empty. + + + Cannot create a typed message from type '{0}'. The functionality only valid for types decorated with MessageContractAttribute. + + + A Channel/Service Endpoint is null. + + + A Channel/Service endpoint's Binding is null. + + + A Channel/Service endpoint's Contract is null. + + + A Channel/Service endpoint's Contract's name is null or empty. + + + A Channel/Service endpoint's Contract's namespace is null. + + + Service '{0}' has zero application (non-infrastructure) endpoints. This might be because no configuration file was found for your application, or because no service element matching the service name could be found in the configuration file, or because no endpoints were defined in the service element. + + + DeliveryRequirementsAttribute requires QueuedDelivery, but binding for the endpoint with contract '{0}' doesn't support it or isn't configured properly to support it. + + + DeliveryRequirementsAttribute disallows QueuedDelivery, but binding for the endpoint with contract '{0}' supports it. + + + The DeliveryRequirementsAttribute on contract '{0}' specifies that the binding must support ordered delivery (RequireOrderedDelivery). This condition could not be verified because the configured binding does not implement IBindingDeliveryCapabilities. The DeliveryRequirementsAttribute may only be used with bindings that implement the IBindingDeliveryCapabilities interface. + + + The DeliveryRequirementsAttribute on contract '{0}' specifies a QueuedDeliveryRequirements constraint. This condition could not be verified because the configured binding does not implement IBindingDeliveryCapabilities. The DeliveryRequirementsAttribute may only be used with bindings that implement the IBindingDeliveryCapabilities interface. + + + The DeliveryRequirementsAttribute on contract '{0}' specifies a QueuedDeliveryRequirements value of NotAllowed. However, the configured binding for this contract specifies that it does support queued delivery. A queued binding may not be used with this contract. + + + At least one operation on the '{0}' contract is configured with the TransactionFlowAttribute attribute set to Mandatory but the channel's binding '{1}' is not configured with a TransactionFlowBindingElement. The TransactionFlowAttribute attribute set to Mandatory cannot be used without a TransactionFlowBindingElement. + + + At least one operation on the '{0}' contract is configured with the TransactionFlowAttribute attribute set to Mandatory but the channel's binding '{1}' is not configured with a TransactionFlowBindingElement. The TransactionFlowAttribute attribute set to Mandatory cannot be used without a TransactionFlowBindingElement. + + + The message with Action '{0}' cannot be processed at the receiver, due to a ContractFilter mismatch at the EndpointDispatcher. This may be because of either a contract mismatch (mismatched Actions between sender and receiver) or a binding/security mismatch between the sender and the receiver. Check that sender and receiver have the same contract and the same binding (including security requirements, e.g. Message, Transport, None). + + + The message with To '{0}' cannot be processed at the receiver, due to an AddressFilter mismatch at the EndpointDispatcher. Check that the sender and receiver's EndpointAddresses agree. + + + The message with Action '{0}' cannot be processed at the receiver because this Action is reserved for the connection opening messages only and cannot be sent from client to server. To invoke this operation on the server, call the '{1}' method on the client proxy instead. + + + The operation '{0}' could not be invoked because the property '{1}' on the OperationContract is set to '{2}'. To invoke this operation on the server, call the '{3}' method on the client proxy instead. + + + When using the IAsyncResult design pattern, the End method cannot be decorated with OperationContractAttribute. Only the corresponding Begin method can be decorated with OperationContractAttribute; that attribute will apply to the Begin-End pair of methods. Method '{0}' in type '{1}' violates this. + + + The WS-AT messaging library failed to initialize. + + + A client-side channel to the WS-AT protocol service could not be created. + + + The DispatchOperation '{0}' requires Formatter, since DeserializeRequest and SerializeReply are not both false. + + + The ClientOperation '{0}' requires Formatter, since SerializeRequest and DeserializeReply are not both false. + + + DispatchOperation requires Invoker. + + + Channel requirements cannot be met by the ChannelFactory for Binding '{0}' since the contract requires support for one of these channel types '{1}' but the binding doesn't support any of them. + + + Channel type '{1}' was requested, but Binding '{0}' doesn't support it or isn't configured properly to support it. + + + ChannelDispatcher requirements cannot be met by the IChannelListener for Binding '{0}' since the contract requires support for one of these channel types '{1}' but the binding only supports these channel types '{2}'. + + + The listener at Uri '{0}' could not be initialized because it was created for an unrecognized channel type. + + + Contract requires Session, but Binding '{0}' doesn't support it or isn't configured properly to support it. + + + Contract does not allow Session, but Binding '{0}' does not support Datagram or is not configured properly to support it. + + + Contract requires OneWay, but Binding '{0}' doesn't support it or isn't configured properly to support it. + + + Contract requires TwoWay (either request-reply or duplex), but Binding '{0}' doesn't support it or isn't configured properly to support it. + + + Contract requires Request/Reply, but Binding '{0}' doesn't support it or isn't configured properly to support it. + + + Contract requires Duplex, but Binding '{0}' doesn't support it or isn't configured properly to support it. + + + Binding '{0}' doesn't support creating any channel types. This often indicates that the BindingElements in a CustomBinding have been stacked incorrectly or in the wrong order. A Transport is required at the bottom of the stack. The recommended order for BindingElements is: TransactionFlow, ReliableSession, Security, CompositeDuplex, OneWay, StreamSecurity, MessageEncoding, Transport. + + + The contract '{0}' is not self-consistent -- it has one or more IsTerminating or non-IsInitiating operations, but it does not have the SessionMode property set to SessionMode.Required. The IsInitiating and IsTerminating attributes can only be used in the context of a session. + + + The operation contract '{0}' is not self-consistent. When the '{1}' is set to '{2}', both '{3}' and '{4}' properties must be true, and the operation must not have any input parameters. + + + The ServiceHost must be configured with either a serviceType or a serviceInstance. Both of these values are currently null. + + + The ServiceMetadataExtension instance could not be added to the ServiceHost instance because it has already been added to another ServiceHost instance. + + + The ServiceMetadataExtension instance could not be removed from the ServiceHost instance because it has not been added to any ServiceHost instance. + + + The ServiceMetadataExtension instance could not be removed from the ServiceHost instance because it has already been added to a different ServiceHost instance. + + + A value of type '{0}' cannot be added to the generic collection, because the collection has been parameterized with a different type. + + + A null value cannot be added to the generic collection, because the collection has been parameterized with a value type. + + + Cannot add two items with the same key to SynchronizedKeyedCollection. + + + Item does not exist in SynchronizedKeyedCollection. + + + A reply message was received without a valid RelatesTo header. This may have been caused by a missing RelatesTo header or a RelatesTo header with an invalid WS-Addressing Relationship type. + + + Internal Error: The InnerChannel property is null. + + + The current channel does not support closing the output session as this channel does not implement ISessionChannel<IDuplexSession>. + + + The ServiceEndpoint with name '{0}' could not be exported to WSDL because the Binding property is null. To fix this, set the Binding property to a valid Binding instance. + + + A binding instance has already been associated to listen URI '{0}'. If two endpoints want to share the same ListenUri, they must also share the same binding object instance. The two conflicting endpoints were either specified in AddServiceEndpoint() calls, in a config file, or a combination of AddServiceEndpoint() and config. + + + The following Policy Assertions were not Imported:\r\n + + + XPath:{0}\r\n Assertions: + + + "XPath Unavailable" + + + A policy expression was ignored because another policy expression with that ID has already been read in this document.\r\nXPath:{0} + + + A policy document was ignored because a policy expression with that ID has already been imported.\r\nPolicy ID:{0} + + + A metadata section containing policy did not have an identifier so it cannot be referenced. + + + XPath:{0} + + + A policy reference was ignored because the policy with ID '{0}' could not be found. + + + A policy reference was ignored because the URI of the reference was empty. + + + A policy reference was ignored because the required {0} attribute was missing. + + + The policy expression was not fully imported because it exceeded the maximum allowable complexity. The import stopped at element '{0}' '{1}'. + + + The policy expression was not fully imported because its normalized form was too large. + + + Unrecognized policy element {0} in namespace {1}. + + + "{0}" is not a supported WS-Policy document root element. + + + The "{0}" namespace is not a recognized WS-Policy namespace. + + + Cannot find usable policy alternatives. + + + Unreachable policy detected.\r\nA WS-Policy element embedded in WSDL is missing a fragment identifier. This policy cannot be referenced by any WS-PolicyAttachment mechanisms.\r\nXPath:{0} + + + The processing of the WSDL parameter failed. Error: {0} + + + The optional WSDL extension element '{0}' from namespace '{1}' was not handled.\r\nXPath: {2} + + + The required WSDL extension element '{0}' from namespace '{1}' was not handled. + + + An unrecognized WSDL extension of Type '{0}' was not handled. + + + A previous call to this WsdlExporter left it in a faulted state. It is no longer usable. + + + A previous call to this WsdlImporter left it in a faulted state. It is no longer usable. + + + The ContractDescription argument to ImportEndpoints must be contained in the KnownContracts collection. + + + A previous attempt to import this {0} already failed. + + + The type {0} registered as a policy extension does not implement IPolicyImportExtension + + + The type {0} registered as a policy extension does not have a public default constructor. Policy extensions must have a public default constructor + + + An exception was thrown in a call to a policy import extension.\r\nExtension: {0}\r\nError: {1} + + + An exception was thrown in a call to a policy export extension.\r\nExtension: {0}\r\nError: {1} + + + Calling IWsdlExportExtension.ExportContract twice with the same ContractDescription is not supported. + + + Duplicate contract XmlQualifiedNames are not supported.\r\nAnother ContractDescription with the Name: {0} and Namespace: {1} has already been exported. + + + Similar ServiceEndpoints were exported. The WSDL export process was forced to suffix wsdl:binding names to avoid naming conflicts.\r\n Similar ServiceEndpoints means different binding instances having the Name: {0} and Namespace: {1} and either the same ContractDescription or at least the same contract Name: {2}. + + + An operation was skipped during export because it has a wildcard action. This is not supported in WSDL.\r\nContract Name:{0}\r\nContract Namespace:{1}\r\nOperation Name:{2} + + + An operation was skipped during export because the property '{0}' is set to '{1}'. This operation should be used for server only and should not be exposed from WSDL. \r\nContract Name:{2}\r\nContract Namespace:{3}\r\nOperation Name:{4} + + + The type {0} registered as a WSDL extension does not implement IWsdlImportExtension. + + + The type {0} registered as a WSDL extension does not have a public default constructor. WSDL extensions must have a public default constructor. + + + An exception was thrown in a call to a WSDL export extension: {0}\r\n contract: {1} + + + An exception was thrown in a call to a WSDL export extension: {0}\r\n Endpoint: {1} + + + A WSDL import extension threw an exception during the BeforeImport call: {0}\r\nError: {1} + + + An exception was thrown while running a WSDL import extension: {0}\r\nError: {1} + + + Cannot import {0}\r\nDetail: {2}\r\nXPath to Error Source: {1} + + + There was an error importing a {0} that the {1} is dependent on.\r\nXPath to {0}: {2} + + + The {0} binding element requires envelope version '{1}' It doesn't support '{2}'. + + + No value. + + + The '{0}' binding element does not support cloning. + + + WsdlImporter encountered unrecognized policy assertions in ServiceDescription '{0}': + + + The {0} declared on method '{1}' in type '{2}' is invalid. {0}s are only valid on methods that are declared in a type that has ServiceContractAttribute. Either add ServiceContractAttribute to type '{2}' or remove {0} from method '{1}'. + + + Too many attributes of type {0} on {1}. + + + Couldn't find required attribute of type {0} on {1}. + + + Attempted to get contract type for {0}, but that type is not a ServiceContract, nor does it inherit a ServiceContract. + + + OperationContract method '{0}' in type '{1}' does not properly implement the async pattern, as no corresponding method '{2}' could be found. Either provide a method called '{2}' or set the AsyncPattern property on method '{0}' to false. + + + OperationContract method '{0}' in type '{1}' does not properly implement the async pattern, as more than one corresponding method '{2}' was found. When using the async pattern, exactly one end method must be provided. Either remove or rename one or more of the '{2}' methods such that there is just one, or set the AsyncPattern property on method '{0}' to false. + + + Invalid async End method signature for method {0} in ServiceContract type {1}. Your end method must take an IAsyncResult as the last argument. + + + Invalid async Begin method signature for method {0} in ServiceContract type {1}. Your begin method must take an AsyncCallback and an object as the last two arguments and return an IAsyncResult. + + + Because base ServiceContract '{0}' has a CallbackContract '{1}', derived ServiceContract '{2}' must also specify either '{1}' or a derived type as its CallbackContract. + + + In a contract inheritance hierarchy, the ServiceContract's CallbackContract must be a subtype of the CallbackContracts of all of the CallbackContracts of the ServiceContracts inherited by the original ServiceContract, Types {0} and {1} violate this rule. + + + Cannot have two operations in the same contract with the same name, methods {0} and {1} in type {2} violate this rule. You can change the name of one of the operations by changing the method name or by using the Name property of OperationContractAttribute. + + + The {0}.{1} operation references a message element [{2}] that has already been exported from the {3}.{4} operation. You can change the name of one of the operations by changing the method name or using the Name property of OperationContractAttribute. Alternatively, you can control the element name in greater detail using the MessageContract programming model. + + + Cannot inherit two different operations with the same name, operation '{0}' from contracts '{1}' and '{2}' violate this rule. You can change the name of one of the operations by changing the method name or by using the Name property of OperationContractAttribute. + + + The synchronous OperationContract method '{0}' in type '{1}' was matched with the asynchronous OperationContract methods '{2}' and '{3}' because they have the same operation name '{4}'. When a synchronous OperationContract method is matched to a pair of asynchronous OperationContract methods, the two OperationContracts must define the same number and types of parameters. In this case, some of the arguments are different. To fix it, ensure that the OperationContracts define the same number and types of arguments, in the same order. Alternatively, changing the name of one of the methods will prevent matching. + + + The synchronous OperationContract method '{0}' in type '{1}' was matched with the task-based asynchronous OperationContract method '{2}' because they have the same operation name '{3}'. When a synchronous OperationContract method is matched to a task-based asynchronous OperationContract method, the two OperationContracts must define the same number and types of parameters. In this case, some of the arguments are different. To fix it, ensure that the OperationContracts define the same number and types of arguments, in the same order. Alternatively, changing the name of one of the methods will prevent matching. + + + The task-based asynchronous OperationContract method '{0}' in type '{1}' was matched with the asynchronous OperationContract methods '{2}' and '{3}' because they have the same operation name '{4}'. When a task-based asynchronous OperationContract method is matched to a pair of asynchronous OperationContract methods, the two OperationContracts must define the same number and types of parameters. In this case, some of the arguments are different. To fix it, ensure that the OperationContracts define the same number and types of arguments, in the same order. Alternatively, changing the name of one of the methods will prevent matching. + + + The synchronous OperationContract method '{0}' in type '{1}' was matched with the asynchronous OperationContract methods '{2}' and '{3}' because they have the same operation name '{4}'. When a synchronous OperationContract method is matched to a pair of asynchronous OperationContract methods, the two OperationContracts must define the same return type. In this case, the return types are different. To fix it, ensure that method '{0}' and method '{3}' have the same return type. Alternatively, changing the name of one of the methods will prevent matching. + + + The synchronous OperationContract method '{0}' in type '{1}' was matched with the task-based asynchronous OperationContract method '{2}' because they have the same operation name '{3}'. When a synchronous OperationContract method is matched to a task-based asynchronous OperationContract method, the two OperationContracts must define the same return type. In this case, the return types are different. To fix it, ensure that method '{0}' and method '{2}' have the same return type. Alternatively, changing the name of one of the methods will prevent matching. + + + The task-based asynchronous OperationContract method '{0}' in type '{1}' was matched with the asynchronous OperationContract methods '{2}' and '{3}' because they have the same operation name '{4}'. When a synchronous OperationContract method is matched to a pair of asynchronous OperationContract methods, the two OperationContracts must define the same return type. In this case, the return types are different. To fix it, ensure that method '{0}' and method '{3}' have the same return type. Alternatively, changing the name of one of the methods will prevent matching. + + + The synchronous OperationContract method '{0}' in type '{1}' was matched with the asynchronous OperationContract methods '{2}' and '{3}' because they have the same operation name '{4}'. When a synchronous OperationContract method is matched to a pair of asynchronous OperationContract methods, any additional attributes must be declared on the synchronous OperationContract method. In this case, the asynchronous OperationContract method '{2}' has one or more attributes of type '{5}'. To fix it, remove the '{5}' attribute or attributes from method '{2}'. Alternatively, changing the name of one of the methods will prevent matching. + + + The synchronous OperationContract method '{0}' in type '{1}' was matched with the task-based asynchronous OperationContract method '{2}' because they have the same operation name '{3}'. When a synchronous OperationContract method is matched to a task-based asynchronous OperationContract method, any additional attributes must be declared on the synchronous OperationContract method. In this case, the task-based asynchronous OperationContract method '{2}' has one or more attributes of type '{4}'. To fix it, remove the '{4}' attribute or attributes from method '{2}'. Alternatively, changing the name of one of the methods will prevent matching. + + + The task-based asynchronous OperationContract method '{0}' in type '{1}' was matched with the asynchronous OperationContract methods '{2}' and '{3}' because they have the same operation name '{4}'. When a task-based asynchronous OperationContract method is matched to a pair of asynchronous OperationContract methods, any additional attributes must be declared on the task-based asynchronous OperationContract method. In this case, the asynchronous OperationContract method '{2}' has one or more attributes of type '{5}'. To fix it, remove the '{5}' attribute or attributes from method '{2}'. Alternatively, changing the name of one of the methods will prevent matching. + + + The synchronous OperationContract method '{0}' in type '{1}' was matched with the asynchronous OperationContract methods '{2}' and '{3}' because they have the same operation name '{4}'. When a synchronous OperationContract method is matched to a pair of asynchronous OperationContract methods, the two OperationContracts must have the same value for the '{5}' property. In this case, the values are different. To fix it, change the '{5} property of one of the OperationContracts to match the other. Alternatively, changing the name of one of the methods will prevent matching. + + + The synchronous OperationContract method '{0}' in type '{1}' was matched with the task-based asynchronous OperationContract method '{2}' because they have the same operation name '{3}'. When a synchronous OperationContract method is matched to a task-based asynchronous OperationContract method, the two OperationContracts must have the same value for the '{4}' property. In this case, the values are different. To fix it, change the '{4} property of one of the OperationContracts to match the other. Alternatively, changing the name of one of the methods will prevent matching. + + + The task-based asynchronous OperationContract method '{0}' in type '{1}' was matched with the asynchronous OperationContract methods '{2}' and '{3}' because they have the same operation name '{4}'. When a task-based asynchronous OperationContract method is matched to a pair of asynchronous OperationContract methods, the two OperationContracts must have the same value for the '{5}' property. In this case, the values are different. To fix it, change the '{5} property of one of the OperationContracts to match the other. Alternatively, changing the name of one of the methods will prevent matching. + + + Operations marked with IsOneWay=true must not declare output parameters, by-reference parameters or return values. + + + One way operation {0} cannot not specify a reply action. + + + The method '{1}' in type '{0}' is marked IsOneWay=true and declares one or more FaultContractAttributes. One-way methods cannot declare FaultContractAttributes. To fix it, change IsOneWay to false or remove the FaultContractAttributes. + + + Only malformed Messages are supported. + + + Cannot locate operation {0} in Contract {1}. + + + Unsupported WSDL, only one message part is supported for fault messages. This fault message references zero or more than one message part. If you have edit access to the WSDL file, you can fix the problem by removing the extra message parts such that fault message references just one part. + + + Unsupported WSDL, the fault message part must reference an element. This fault message does not reference an element. If you have edit access to the WSDL document, you can fix the problem by referencing a schema element using the 'element' attribute. + + + Async End called on wrong channel. + + + Async End called with an IAsyncResult from a different Begin method. + + + The received transaction has an isolation level of '{0}' but the service is configured with a TransactionIsolationLevel of '{1}'. The isolation level for received transactions and the service must be the same. + + + The value of the addressHeaders argument is invalid because the collection contains null values. Null is not a valid value for the AddressHeaderCollection. + + + The array passed does not have enough space to hold all the properties contained by this collection. + + + The value could not be added to the collection, as the collection already contains an item of the same type: '{0}'. This collection only supports one instance of each type. + + + Cannot create channel for a contract that requires request/reply and a binding that requires manual addressing but only supports duplex communication. + + + Missing required '{0}' attribute. + + + Ignoring invalid SOAP header extension in wsdl:operation name='{0}' from targetNamespace='{1}'. Reason: {2} + + + Ignoring invalid SOAP fault extension in wsdl:operation name='{0}' from targetNamespace='{1}'. Reason: {2} + + + Ignoring invalid part in wsdl:message name='{0}' from targetNamespace='{1}'. Reason: {2} + + + PrivacyNotice element must have a Version attribute. + + + PrivacyNotice element Version attribute must have an integer value. + + + Binding validation failed. The client cannot send messages. A conflict in the binding properties caused this failure. The UseActiveDirectory is set to true and QueueTransferProtocol is set to Native. To resolve the conflict, correct one of the properties. + + + Binding validation failed because the binding's ReceiveErrorHandlig property is set to Move or Reject while the version of MSMQ installed on this system is not 4.0 or higher. The channel listener cannot be opened. Resolve the conflict by setting the ReceiveErrorHandling property to Drop or Fault, or by upgrading to MSMQ v4.0. + + + The Ambient transaction used to Complete the ReceiveContext Operation is not in an active state. + + + Binding validation failed because the binding's MsmqAuthenticationMode property is set to Certificate while the MsmqProtectionLevel property is not set to Sign or EncryptAndSign. The channel factory or service host cannot be opened. Resolve the conflict by correcting one of the properties. + + + Binding validation failed. The service or the client cannot be started. A conflict in the binding properties caused this failure. The MsmqAuthenticationMode is set to None and MsmqProtectionLevel is not set to None. To resolve to conflict, correct one of the properties. + + + Binding validation failed because the binding's MsmqAuthenticationMode property is set to WindowsDomain while the MsmqProtectionLevel property is not set to Sign or EncryptAndSign. The channel factory or service host cannot be opened. Resolve the conflict by correcting one of the properties. + + + Creation of a message security context failed because the attached sender certificate was invalid or cannot be validated. The message cannot be received. Ensure that a valid certificate is attached to the message and that the certificate is present in the receiver's certificate store. + + + The content type of an incoming message is unknown or not supported. The message cannot be received. Ensure that the sender was configured to use the same message encoder as the receiver. + + + An incoming MSMQ message contained invalid or unexpected .NET Message Framing information in its body. The message cannot be received. Ensure that the sender is using a compatible service contract with a matching SessionMode. + + + An XML error was encountered while reading a WCF message. The message cannot be received. Ensure the message was sent by a WCF client which used an identical message encoder. + + + TransactedBatchingBehavior validation failed because none of the service operations had the TransactionScopeRequired property set to true on their OperationBehavior attribute. The service host cannot be started. Ensure this requirement is met if you wish to use this behavior. + + + A mismatch was detected between the serialization format specified in the MsmqIntegrationMessageProperty and the body of the MSMQ message. The message cannot be sent. The serialization format ByteArray requires the body of the MSMQ message to be of type byte[]. + + + An error occurred while deserializing an MSMQ message's ActiveX body. The message cannot be received. The specified variant type for the body does not match the actual MSMQ message body. + + + An error occurred while deserializing an MSMQ message's XML body. The message cannot be received. Ensure that the service contract is decorated with appropriate [ServiceKnownType] attributes or the TargetSerializationTypes property is set on the MsmqIntegrationBindingElement. + + + The properties of the message are mismatched. The message cannot be sent. The BodyType message property cannot be specified if the ActiveX serialization format is used. + + + The sender's X.509 certificate was not found. The message cannot be sent. Ensure the certificate is available in the sender's certificate store. + + + Binding validation failed. The client cannot send the message. The DeadLetterQueue is set to Custom, but the CustomDeadLetterQueue is not specified. Specify the URI of the dead letter queue for each application in the CustomDeadLetterQueue property. + + + An error was encountered while deserializing the message. The message cannot be received. + + + Binding validation failed because the endpoint listen URI does not represent an MSMQ direct format name. The service host cannot be opened. Make sure you use a direct format name for the endpoint's listen URI. + + + The host in the CustomDeadLetterQueue URI is not "localhost" or the local machine name. A custom DLQ must reside on the sender's machine. + + + Binding validation failed. The client cannot send a message. The specified dead letter queue does not exist or cannot be written. Ensure the queue exists with the proper authorization to write to it. + + + Binding validation failed because the binding's MsmqProtectionLevel property is set to EncryptAndSign while the UseActiveDirectory is not set to true. The channel factory or the service host cannot be opened. Resolve the conflict by correcting one of the properties. + + + Binding validation failed. The service or the client cannot be started. The ExactlyOnce property is set to false and ReceiveContext is enabled. This is not supported. To resolve the conflict, either set ExactlyOnce to true or disable ReceiveContext. + + + The version check failed with the error: '{0}'. The version of MSMQ cannot be detected All operations that are on the queued channel will fail. Ensure that MSMQ is installed and is available. + + + The message ID '{0}' is not in the right format. + + + The specified addressing scheme is invalid for this binding. The NetMsmqBinding scheme must be net.msmq. The MsmqIntegrationBinding scheme must be msmq.formatname. + + + The MsmqIntegrationBinding validation failed. The service cannot be started. The {0} binding does not support the method signature for the service operation {1} in the {2} contract. Correct the service operation to use the MsmqIntegrationBinding. + + + The ActiveX serialization failed because the serialization format cannot be recognized. The message cannot be received. + + + The variant type is not recognized. The ActiveX serialization failed. The message cannot be sent. The specified variant type is not supported. + + + {0} ({1}, 0x{2}) + + + The message cannot be sent because it's missing an MsmqIntegrationMessageProperty. All messages sent over MSMQ integration channels must carry the MsmqIntegrationMessageProperty. + + + Binding validation failed. The service or the client cannot be started. The ExactlyOnce property is set to true and the Durable property is set to false. This is not supported. To resolve the conflict, correct one of these properties. + + + Argument must be a positive number or zero. + + + A mismatch between the binding and MSMQ queue configuration was detected. The service cannot be started. The ExactlyOnce property is set to false and the queue to read messages from is a transactional queue, Correct the error by setting the ExactlyOnce property to true or create a non-transactional binding. + + + Binding validation failed because the URI represents a subqueue and the ReceiveErrorHandling parameter is set to Move. The service host or channel listener cannot be opened. Resolve this conflict by setting the ReceiveErrorHandling to Fault, Drop or Reject. + + + Creation of a message security context failed because the sender's SID was not found in the message. The message cannot be received. The WindowsDomain MsmqAuthenticationMode requires the sender's SID. + + + An error occurred while opening the queue:{0}. The message cannot be sent or received from the queue. Ensure that MSMQ is installed and running. Also ensure that the queue is available to open with the required access mode and authorization. + + + An error occurred when converting the '{0}' queue path name to the format name: {1}. All operations on the queued channel failed. Ensure that the queue address is valid. MSMQ must be installed with Active Directory integration enabled and access to it is available. + + + Binding validation failed. The client cannot send messages. The CustomDeadLetterQueue property is set, but the DeadLetterQueue property is not set to Custom. Set the DeadLetterQueue property to Custom. + + + Binding validation failed. The client cannot send messages. A conflict in the binding properties is causing the failure. To use the custom dead letter queue, ExactlyOnce must be set to true to resolve to conflict. + + + A mismatch between the binding and MSMQ configuration was detected. The client cannot send messages. To use the custom dead letter queue, you must have MSMQ version 4.0 or higher. If you do not have MSMQ version 4.0 or higher set the DeadLetterQueue property to System or None. + + + The transport channel detected a poison message. This occurred because the message exceeded the maximum number of delivery attempts or because the channel detected a fundamental problem with the message. The inner exception may contain additional information. + + + There was an error opening the queue. Ensure that MSMQ is installed and running, the queue exists and has proper authorization to be read from. The inner exception may contain additional information. + + + The ReceiveContext delete operation failed because the message with Id '{0}' could not be received from the lock subqueue. + + + The ReceiveContext unlock operation failed because the message with Id '{0}' could not be moved from the lock subqueue to the main queue. + + + The queue could not be opened because the ReceiveContext feature is not supported on subqueues. Specify a different queue to receive from, or disable ReceiveContext. + + + An error occurred while receiving a message from the queue: {0}. Ensure that MSMQ is installed and running. Make sure the queue is available to receive from. + + + A transaction error occurred for this session. The session channel is faulted. Messages in the session cannot be sent or received. A queued session cannot be associated with more than one transaction. Ensure that all messages in the session are sent or received using a single transaction. + + + An error occurred while sending to the queue: {0}.Ensure that MSMQ is installed and running. If you are sending to a local queue, ensure the queue exists with the required access mode and authorization. + + + A serialization error occurred. The message cannot be sent or received. The MSMQ integration channel is able to serialize no more than {0} types. + + + The transaction associated with this session channel has been rolled back because Abort was called on the session channel before the transaction committed. + + + Session channels must not have pending messages when the transactions associated with these channels are committed. Pending messages are either messages that have not been received from the session channel or messages that have been received but Complete has not been called for them. The channel has faulted and the transaction was rolled back. + + + Session channels must be closed before the transaction is committed. The channel has faulted and the transaction was rolled back. + + + The total size of messages sent in this session exceeded the maximum value of Int32. The messages in this session cannot be sent. + + + An attempt made to close the session channel while there are still messages pending in the session. Current transaction will be rolled back and the session channel will be faulted. Messages in a session must be consumed all at once. + + + An attempt was made to close the session channel while there are still messages pending in the session. The sessiongram will be rolled back to the queue and the session channel will be faulted. + + + A serialization error occurred because of a mismatch between the value of the SerializationFormat property and the type of the body. The message cannot be sent. Ensure the type of the body is Stream or use a different SerializationFormat. + + + The message time to live (TTL) is too large. The message cannot be sent. The message TTL cannot exceed the Int32 maximum value. + + + A client X.509 certificate was not specified through the channel factory's Credentials property, but one is required when the binding's MsmqAuthenticationMode property is set to Certificate. The message cannot be sent. + + + The current transaction is not active. Messages in this session cannot be sent or received and the session channel will be faulted. All messages in a session must be sent or received using a single transaction. + + + Binding validation failed because the binding's ExactlyOnce property is set to true while the destination queue is non-transactional. The service host cannot be opened. Resolve this conflict by setting the ExactlyOnce property to false or creating a transactional queue for this binding. + + + A transaction was not found in Transaction.Current but one is required for this operation. The channel cannot be opened. Ensure this operation is being called within a transaction scope. + + + A transaction is required but is not available. Messages cannot be sent or received. Ensure that the transaction scope is specified to send or receive messages. + + + A mismatch occurred between the binding and the MSMQ configuration. Messages cannot be sent. The custom dead letter queue specified in the binding must be a transactional queue. Ensure that the custom dead letter queue address is correct and the queue is a transactional queue. + + + The net.msmq scheme does not support port numbers. To correct this, remove the port number from the URI. + + + Unrecognized error {0} (0x{1}) + + + The serialization failed because the serialization format '{0}' is not supported. The message cannot be sent or received. + + + Binding validation failed because the binding's MsmqAuthenticationMode property is set to WindowsDomain but MSMQ is installed with Active Directory integration disabled. The channel factory or service host cannot be opened. + + + The URL in invalid. The URL for the queue cannot contain the '$' character. Use the syntax in net.msmq://machine/private/queueName to address a private queue. + + + The URI is invalid because it is missing a host. + + + Failed to reacquire lock for message. + + + Cannot find '{0}' value in dictionary string. + + + WMI GetObject Query: {0} + + + WMI PutInstance Class: {0} + + + Cannot dequeue a '{0}' object while in the Created state. + + + The binding (Name={0}, Namespace={1}) cannot be used to create a ChannelFactory or a ChannelListener because it appears to be missing a TransportBindingElement. Every binding must have at least one binding element that derives from TransportBindingElement. + + + The TransportBindingElement of type '{0}' in this CustomBinding returned a null or empty string for the Scheme. TransportBindingElement's Scheme must be a non-empty string. + + + Binding '{0}' lacks a TransportBindingElement. Every binding must have a binding element that derives from TransportBindingElement. This binding element must appear last in the BindingElementCollection. + + + In Binding '{0}', TransportBindingElement '{1}' does not appear last in the BindingElementCollection. Please change the order of elements such that the TransportBindingElement is last. + + + None of the binding elements in binding '{0}' define a message version. At least one binding element must define a message version and return it from the GetProperty<MessageVersion> method. + + + Some of the binding elements in this binding were not used when building the ChannelFactory / ChannelListener. This may be have been caused by the binding elements being misordered. The recommended order for binding elements is: TransactionFlow, ReliableSession, Security, CompositeDuplex, OneWay, StreamSecurity, MessageEncoding, Transport. Note that the TransportBindingElement must be last. The following binding elements were not built: {0}. + + + More than one MessageEncodingBindingElement was found in the BindingParameters of the BindingContext. This usually is caused by having multiple MessageEncodingBindingElements in a CustomBinding. Remove all but one of these elements. + + + More than one IStreamUpgradeProviderElement was found in the BindingParameters of the BindingContext. This usually is caused by having multiple IStreamUpgradeProviderElements in a CustomBinding. Remove all but one of these elements. + + + More than one PeerResolverBindingElement was found in the BindingParameters of the BindingContext. This usually is caused by having multiple PeerResolverBindingElements in a CustomBinding. Remove all but one of these elements. + + + More than one PeerCustomResolverBindingElement was found in the BindingParameters of the BindingContext. This usually is caused by having multiple PeerCustomResolverBindingElement in a CustomBinding. Remove all but one of these elements. + + + The security capabilities of binding '{0}' do not match those of the generated runtime object. Most likely this means the binding contains a StreamSecurityBindingElement, but lacks a TransportBindingElement that supports Stream Security (such as TCP or Named Pipes). Either remove the unused StreamSecurityBindingElement or use a transport that supports this element. + + + Only an absolute Uri can be used as a base address. + + + This collection already contains an address with scheme {0}. There can be at most one address per scheme in this collection. If your service is being hosted in IIS you can fix the problem by setting 'system.serviceModel/serviceHostingEnvironment/multipleSiteBindingsEnabled' to true or specifying 'system.serviceModel/serviceHostingEnvironment/baseAddressPrefixFilters'. + + + A base address cannot contain a Uri user info section. + + + The binding does not contain a TransportBindingElement. + + + The binding does not contain a ChannelDemuxerBindingElement. + + + A base address cannot contain a Uri query string. + + + A base address cannot contain a Uri fragment. + + + The given URI must be absolute. + + + The binding for scheme '{0}' specified in the protocol mapping does not exist and must be created. + + + The binding on the service endpoint cannot be configured. + + + Configuration binding extension '{0}' could not be found. Verify that this binding extension is properly registered in system.serviceModel/extensions/bindingExtensions and that it is spelled correctly. + + + A binding reference cycle was detected in your configuration. The following reference cycle must be removed: {0}. + + + The binding specified cannot be null or an empty string. Please specify a valid binding. Valid binding values can be found in the system.serviceModel/extensions/bindingExtensions collection. + + + Cannot parse type '{0}' into a CoreWCF.Dispatcher.XPathMessageFilter. + + + Configuration endpoint extension '{0}' could not be found. Verify that this endpoint extension is properly registered in system.serviceModel/extensions/endpointExtensions and that it is spelled correctly. + + + An endpoint reference cycle was detected in your configuration. The following reference cycle must be removed: {0}. + + + The endpoint specified cannot be null or an empty string. Please specify a valid endpoint. Valid endpoint values can be found in the system.serviceModel/extensions/endpointExtensions collection. + + + Filter element body must not be empty. + + + An extension named {0} already appears in the {1}. Extension names must be unique. + + + An extension of name '{0}' already appears in extension collection. Extension names must be unique. + + + An extension of type '{0}' already appears in extension collection. Extension types must be unique. + + + A child element with the element name '{0}' already exists. Child elements can only be added once. + + + A child element named '{0}' with same key already exists at the same configuration scope. Collection elements must be unique within the same configuration scope (e.g. the same application.config file). Duplicate key value: '{1}'. + + + The '{0}' configuration element key cannot be null. + + + At least one of the configuration element keys '{0}' must not be null. + + + Extension element '{0}' cannot be added to this element. Verify that the extension is registered in the extension collection at system.serviceModel/extensions/{1}. + + + Extension collection '{0}' not found. + + + The extension of type '{0}' is not registered in the extension collection '{1}'. + + + Invalid value for serviceAuthenticationManagerType. The serviceAuthenticationManagerType '{0}' does not derive from '{1}'. + + + Invalid value in policyType. The policyType '{0}' does not implement from '{1}'. + + + The {1} binding does not have a configured binding named '{0}'. + + + The binding at {1} does not have a configured binding named '{0}'. This is an invalid value for {2}. + + + Cannot add the behavior extension '{0}' to the common endpoint behavior because it does not implement '{1}'. + + + Cannot add the behavior extension '{0}' to the common service behavior because it does not implement '{1}'. + + + Invalid value for the certificate validator type. The type '{0}' does not derive from the appropriate base class '{1}'. + + + Invalid value for the client credentials type. The type '{0}' does not derive from the appropriate base class '{1}'. + + + The value '{0}' is not a valid instance of type '{1}'. + + + The instance is not a valid configurable value of type '{0}'. + + + {0} is not a valid encoding string for System.Text.Encoding.GetEncoding(string). + + + There is no endpoint behavior named '{0}'. + + + Cannot add the '{0}' behavior extension to '{1}' endpoint behavior because the underlying behavior type does not implement the IEndpointBehavior interface. + + + The endpoint at {1} does not have a configured endpoint named '{0}'. This is an invalid value for {2}. + + + The attribute '{0}' cannot be specified on element '{1}' when attribute '{2}' is not specified. + + + The CreateServiceEndpoint method in type '{0}' returned null instead of an instance of type '{1}'. + + + Invalid element in configuration. The extension '{0}' does not derive from correct extension base type '{1}'. + + + Invalid element in configuration. The extension name '{0}' is not registered in the collection at system.serviceModel/extensions/{1}. + + + The '{0}' type must derive from {1} to be used in the {2} collection. + + + The element {0} requires a key of type '{1}'. Type of the key passed in: '{2}'. + + + '{0}' is not a valid reliable messaging version. Valid values are 'WSReliableMessagingFebruary2005' and 'WSReliableMessaging11'. + + + Invalid value for the saml serializer type. The type '{0}' does not derive from the appropriate base class: '{1}'. + + + Invalid binding path. There is no binding registered with the configuration path '{0}'. + + + Invalid value for the service credentials type. The type '{0}' does not derive from the appropriate base class '{1}'. + + + Invalid value for the security state encoder type. The type '{0}' does not derive from the appropriate base class '{1}'. + + + Invalid value for the username password validator type. The type '{0}' does not derive from the appropriate base class '{1}'. + + + Invalid value for serviceAuthorizationManagerType. The serviceAuthorizationManagerType '{0}' does not derive from '{1}'. + + + There is no service behavior named '{0}'. + + + Cannot add the behavior extension '{0}' to the service behavior named '{1}' because the underlying behavior type does not implement the IServiceBehavior interface. + + + Start must be between 0 and {0}. Value passed in is {1}. + + + '{0}' is not a valid transaction protocol. Valid values are 'OleTransactions', 'WSAtomicTransactionOctober2004', and 'WSAtomicTransaction11'. + + + The type '{0}' registered for extension '{1}' could not be loaded. + + + Invalid binding type for binding extension configuration object. This binding extension manages configuration of binding type '{0}' and cannot act upon type '{1}'. + + + Invalid binding element type for binding element extension configuration object. This binding element extension manages configuration of binding element type '{0}' and cannot act upon type '{1}'. + + + Invalid endpoint type for endpoint extension configuration object. This endpoint extension manages configuration of endpoint type '{0}' and cannot act upon type '{1}'. + + + No elements matching the key '{0}' were found in the configuration element collection. + + + The key does not match the indexer key. When setting the value of a specific index, the key of the desired value must match the index at which it is being set. Key on element (expected value): {0}. Key provided to indexer: {1}. + + + Cannot add the message encoding element '{0}'. Another message encoding element already exists in the binding '{1}'. There can only be one message encoding element for each binding. + + + Cannot find the extension collection associated with extension of type '{0}'. + + + Federated issuer address cannot be null when specifying an issuer binding. + + + The configuration is read only. + + + The '{0}' configuration section cannot be created. The machine.config file is missing information. Verify that this configuration section is properly registered and that you have correctly spelled the section name. For Windows Communication Foundation sections, run ServiceModelReg.exe -i to fix this error. + + + Cannot add stream upgrade element '{0}'. Another stream upgrade element already exists in the binding '{1}'. There can only be one stream update element per binding. + + + Cannot add the transport element '{0}'. Another transport element already exists in the binding '{1}'. There can only be one transport element for each binding. + + + The XmlElement must contain XML content. + + + The XPathFilter for an XPathFilterElement cannot be null. + + + Namespace prefix '{0}' referenced in XPath expression was not found. + + + (Default) + + + MTAWorkerThread exception + + + An unexpected error has occurred. + + + The CLSID specified in the service file is not configured in the specified application. (The CLSID is {0}, the AppID is {1}.) + + + The CLSID specified in the service file does not have a service element in a configuration file. (The CLSID is {0}.) + + + An endpoint configured for the COM+ CLSID {0} is not a configured interface on the class. (The contract type is {1}.) + + + The COM+ string in the .svc file was formatted incorrectly. (The string is "{0}".) + + + The contract type name in the configuration file was not in the form of an interface identifier. (The string is "{0}".) + + + The configured application was not found. (The Application ID was {0}.) + + + A transaction vote request was completed, but there was no outstanding vote request. + + + Failed to convert type library to assembly + + + Incorrect Interface version in registry + + + Failed to load type library + + + An attempt to load the native type library '{0}' was made. Native type libraries cannot be loaded. + + + Could not find interface in the Assembly + + + The '{0}' user-defined type could not be found. Ensure that the correct type and type library are registered and specified. + + + Could not find keyword {0}. + + + Invalid serializer specified. The only valid values are 'xml' and 'datacontract'. + + + The keyword '{0}' has no equal sign following it. Ensure that each keyword is followed by an equal sign and a value. + + + No value found for a keyword. + + + Badly terminated value {0}. + + + Missing Quote in value {0}. + + + Repeated moniker keyword. + + + Interface {0} not found in configuration. + + + Interface {0} has a null namespace or name. + + + Method {0} given in config was not found on interface {1}. + + + Only one type of server identity can be specified. + + + Address not specified. + + + Mex binding section name attribute not specified. + + + Mex address not specified. + + + Contract not specified. + + + Binding not specified. + + + Binding namespace not specified. + + + Failed to do mex retrieval:{0}. + + + None of the contract in metadata matched the contract specified. + + + The contract does not have an endpoint supporting the binding specified. + + + Moniker Missing Colon + + + Multiple server identity keywords were specified. Ensure that at most one identity keyword is specified. + + + The object does not support the interface '{0}'. + + + Could not duplicate the token (error=0x{0:X}). + + + Could not perform an AccessCheck (error=0x{0:X}). + + + Could not impersonate the anonymous user (error=0x{0:X}). + + + The provided SafeArray parameter was passed by value. SafeArray parameters must be passed by reference. + + + Multi-dimensional SafeArray parameters cannot be used. + + + The elements of the SafeArray must be of the type VARIANT. + + + The lower bound of the SafeArray was not zero. SafeArrays with a lower bound other than zero cannot be used. + + + Could not open the thread token (error=0x{0:X}). + + + Could not open the process token (error=0x{0:X}). + + + The isolation level for component {0} is invalid. (The value was {1}.) + + + The conversion between the client parameter type '{0}' to the required server parameter type '{1}' cannot be performed. + + + The required outer proxy could not be created. Ensure that the service moniker is correctly installed and registered. + + + Cannot load library {0}. Ensure that WCF is properly installed. + + + Interface Not Registered + + + Bad Interface Registration + + + The argument passed to SetObject is not a COM object. + + + No type library available for interface + + + Cannot find CLSID {0} in COM+ application {1}. + + + Cannot create an instance of the specified service: access is denied. + + + An internal error occurred attempting to create an instance of the specified service. + + + No services are configured for the application. + + + Access is denied. The message was not authenticated with a valid windows identity. + + + The session requirements of the contracts are inconsistent. All COM contracts in a service must have the same session requirement. + + + Access is denied. + + + Parameter at index {0} is null. + + + Unable to retrieve IUnknown for object. + + + QueryInterface succeeded but the persistable type wrapper was null. + + + Unexpected threading model. WCF/COM+ integration only supports STA and MTA threading models. + + + None of the methods were found for interface {0}. + + + The {0} operation on the service {1} could not be found in the catalog. + + + The interface with IID {0} cannot be exposed as a web service + + + The parameter named {0} of type {1} on method {2} of interface {3} cannot be serialized. + + + The return value of type {0} on method {1} of interface {2} cannot be serialized. + + + The COM+ Integration service '{0}' specified in configuration is not in a supported format and could not be started. Ensure that the configuration is correctly specified. + + + The method '{0}' could not be found. Ensure that the correct method name is specified. + + + The Dispatch ID '{0}' could not be found or is invalid. + + + At least one operation is asynchronous. Asynchronous operations are not allowed. + + + There are duplicate operations, which is invalid. Remove the duplicates. + + + The number of parameters in the request did not match the number supported by the method. Ensure that the correct number of parameters are specified. + + + Binding type {0} instance {1} not found in config. + + + The required address keyword was not specified. + + + The required binding keyword was not specified or is not valid. + + + A VARIANT parameter was passed by value. VARIANT parameters must be passed by reference. + + + The type for the '{0}' parameter in '{1}' within the namespace '{2}' cannot not be resolved. + + + The operation cannot be performed after the communications channel has been created. + + + The interface with IID {0} has no methods configured in the COM+ catalog and cannot be exposed as a web service. + + + The interface with IID {0} is not configured in the COM+ catalog and cannot be exposed as a web service. + + + The channeloption intrinsic object cannot be created because the channel builder is not initialized. + + + There is no transaction in the context of the operation. + + + The service does not accept issued tokens. + + + There was an error verifying some XML Schemas generated during export:\r\n{0} + + + There was a validation error on a schema generated during export:\r\n Source: {0}\r\n Line: {1} Column: {2}\r\n Validation Error: {3} + + + The Address, Binding and Contract keywords are required. + + + Type load for contract interface ID {0} failed with Error:{1}. + + + Fail to load binding {0} from config. Error:{1}. + + + Application {0} is marked Pooled. Pooled applications are not supported under COM+ hosting. + + + Application {0} has recycling enabled. Recycling of applications is not supported under COM+ hosting. + + + The client token at least needs to have the SecurityImpersonationLevel of at least Impersonation for Out of process Webhost activations. + + + This InstanceContext requires a valid Message to obtain the instance. + + + From: {0}\nAppId: {1}\nClsId: {2}\nIncoming TransactionId: {3}\nRequesting Identity: {4} + + + From: {0}\nAppId: {1}\nClsId: {2}\nIid: {3}\nAction: {4}\nInstance Id: {5}\nManaged Thread Id: {6}\nUnmanaged Thread Id: {7}\nRequesting Identity: {8} + + + AppId: {0}\nClsId: {1}\n + + + AppId: {0} + + + Iid: {0}\nType Library ID: {1} + + + A Windows hotfix or later service pack is required on Windows XP and Windows Server 2003 to use WS-AtomicTransaction and COM+ Integration Web service transaction functionality. See the Microsoft .NET Framework release notes for instructions on installing the required hotfix. + + + Generating manifest file {0} failed with {1}. + + + Directory {0} not found. + + + Cannot access directory {0}. + + + The object with CLSID '{0}' does not support the required IPersistStream interface. + + + CLSID of type {0} does not match the CLSID on PersistStreamTypeWrapper which is {1}. + + + Target object does not support IPersistStream. + + + Target type is an interface but corresponding type is not PersistStreamTypeWrapper. + + + CLSID {0} is not allowed. + + + Transferring to ComPlus logical thread {0}. + + + The cNamedArgs parameter is not supported and must be 0. + + + Binding '{0}' was not found in config. The config file must be present and contain a binding matching the one specified in the moniker. + + + The claimType cannot be an empty string. + + + X509Chain does not have any valid certificates. + + + X509CertificateValidationMode.Custom requires a CustomCertificateValidator. Specify the CustomCertificateValidator property. + + + UserNamePasswordValidationMode.MembershipProvider requires a MembershipProvider. Specify the MembershipProvider property. + + + UserNamePasswordValidationMode.Custom requires a CustomUserNamePasswordValidator. Specify the CustomUserNamePasswordValidator property. + + + The Security Support Provider Interface does not support Impersonation level 'None'. Specify Identification, Impersonation or Delegation level. + + + The public key is not an RSA key. + + + The '{0}' dynamic link library (dll) failed to load. + + + Writing audit messages to the Security log is not supported by the current platform. You must write audit messages to the Application log. + + + No custom principal is specified in the authorization context. + + + Access is denied. + + + SecurityAuditBehavior is not supported on the channel factory. + + + The Infocard token created during channel intialization has expired. Please create a new channel to reacquire token. + + + No Infocard token was found in the ChannelParameters. Infocard requires that the security token be created during channel intialization. + + + Message with action {0} received from a neighbor is missing a via Header. + + + The LinkUtility message received from a neighbor has invalid values for usefull '{0}' and total '{1}'. + + + Internal Error: Peer Neighbor state change from {0} to {1} is invalid. + + + The MaxReceivedMessageSize of the associated listener ({0}) is greater than the MaxReceivedMessageSize of the PeerNode ({1}) with the meshid ({2}), ensure that all ChannelFactories and Endpoints for this mesh have the same configuration for MaxRecievedMessageSize. + + + Binding settings conflict with an existing instance that is using the same mesh name. Check the value of the property {0}. + + + value must be >= {0} and <= {1}. + + + Invalid message: the peer channel via ({0}) has a size of ({1}) it exceeds the maximum via size of ({2}). + + + The PeerNode cannot be opened because it has been Aborted. + + + PNRP is not available. Please refer to the documentation with your system for details on how to install and enable PNRP. + + + The PNRP service is not installed on this machine. Please refer to the documentation with your system for details on how to install and enable PNRP. + + + A PeerResolverBindingElement is required in the {0} binding. The default resolver (PNRP) is not available. + + + Resolver must be specified. The default resolver (PNRP) is not available. Please refer to the documentation with your system for details on how to install and enable PNRP. + + + The specified ResolverType: {0} cannot be loaded. Please ensure that the type name specified refers to a type that can be loaded. + + + Specified resolver settings are not enough to create a valid resolver. Please ensure that a ResolverType and an Address is specified for the custom resolver. + + + The ListenIPAddress {0} is invalid. + + + Internal Error. PeerFlooder instance is already disposed. It cannot be used to send messages. + + + Internal Error. Address of the Service cannot be registered with PNRP. + + + The registrationId {0} is invalid. + + + Application message contains a header that conflicts with a PeerChannel specific header. Name = {0} and Namespace = {1}. + + + PNRP could not find any clouds that match the current operation. + + + "Specified addresses can not be registered with PNRP because either PNRP is not enabled or the specified addresses do not have corresponding clouds. Please refer to the documentation with your system for details on how to install and enable PNRP." + + + The binding's PeerTransportSecuritySettings can not be supported under the current system security configuration. + + + Credentials specified are not sufficient to carry requested operation. Please specify a valid value for {0}. + + + Connection was not accepted because the SecurityContext contained tokens that do not match the current security settings. + + + Addresses specified in the registration exceed PNRP's per registration address limit. + + + Provided information is Insufficient to create a valid connection to the resolver service. + + + Specified PeerResolverMode value {0} is invalid. Please specify either PeerResolveMode.Auto, Default, or Pnrp. + + + concrete PeerResolver implementation must override Initialize to accept metadata about resolver service. + + + The operation: {0} is not valid while the object is in open state. + + + The operation: {0} is not valid while the object is in closed state. + + + Registration info can not be null. Please ensure that the Register operation is invoked with a valid RegistrationInfo object. + + + Resolve info can not be null. Please ensure that the Resolve operation is invoked with a valid ResolveInfo object. + + + Refresh info can not be null. Please ensure that the Refresh operation is invoked with a valid RefreshInfo object. + + + MessageBody does not contain a valid {0} message. Please ensure that the message is well formed. + + + A peer registration with the service address {0} already exists. + + + MeshId: {0}, Node Id: {1}, Online: {2}, Open: {3}, Port: {4} + + + The MessagePropagationFilter threw an exception. Please refer to InnerException. + + + An event notification threw an exception. Please refer to InnerException. + + + The Peer resolver threw an exception. Please refer to InnerException. + + + One of the addresses specified doesn't match any PNRP cloud for registration.{0} + + + Specified cloud {0} could not be used for the specified operation because it is disabled. + + + Specified cloud {0} is configured for Resolve operations only. + + + Requested PNRP operation {0} could not be performed because the port is blocked possibly by a firewall. + + + Specified mesh name {0} cannot be used because a name can only be registered once per process. + + + Invalid RefreshInterval value of {0}; it must be greater than zero + + + Invalid CleanupInterval value of {0}; it must be greater than zero + + + Multiple link-local only interfaces detected. Please specifiy the interface you require by using the ListenIpAddress attribute in the PeerTransportBindingElement + + + Registration with zero addresses detected. Please call Register with more than zero addresses. + + + Certificate generation has failed. Please see the inner exception for more information. + + + Throttle on the mesh {0} waiting. + + + Attempting to prune the slow neighbor for the mesh {0}. + + + Maintainer is starting for the mesh {0}. + + + Maintainer is attempting a connection to Peer {0} for the mesh {1}. + + + Maintainer encountered exception when attempting a connection to Peer {0} for the mesh {1}. Exception is {2}. + + + Mantainer's InitialConnect is running for the mesh {0}. + + + Mantainer is attempting to prune connections for the mesh {0}. + + + Mantainer is attempting to establish additional connections for the mesh {0}. + + + BasicHttpContextBinding {0}:{1} requires that AllowCookies property is set to true. + + + The message contains a callback context header with an endpoint reference for AddressingVersion '{0}'. Callback context can only be transmitted when the AddressingVersion is configured with 'WSAddressing10'. + + + The callback address already has a context header in it. + + + The callback address contains multiple context headers. There can be at most one context header in a callback address. + + + The incoming message with action '{0}' contains a callback context header with name '{1}' and namespace '{2}'. Callback context headers are not expected in incoming messages at the client. + + + The message contains a callback context message property. Callback context can be transmitted only when the ContextBindingElement is configured with ContextExchangeMechanism of ContextSoapHeader. + + + ContextBindingElement cannot provide channel factory for the requested channel shape {0}. + + + ContextBindingElement cannot provide channel listener for the requested channel shape {0}. + + + Value '{0}' specified for 'name' attribute of ContextMessageProperty is either null or has invalid character(s). Please ensure value of 'name' is within the allowed value space. + + + Context protocol was unable to parse the context header. Nodes disallowed by the context header schema were found inside the context header. + + + The outgoing message with action '{0}' contains a callback context message property. Callback context cannot be transmitted in outgoing messages at the server. + + + Channel context management cannot be enabled or disabled after the channel is opened. + + + Context cached at the channel cannot be set or retrieved when the context management is disabled at the channel layer. Ensure context channel property 'IContextManager.Enabled' is set to true. + + + Context cached at the channel layer cannot be changed after the channel is opened. + + + Cannot specify 'ContextMessageProperty' in message when using context channel with context management enabled. Ensure the message does not have 'ContextMessageProperty' or disable context management by setting channel property 'IContextManager.Enabled' to false. + + + Context channel received a message with context which does not match the current context cached at the channel. Ensure service does not change context after it was originally set or disable context management by setting channel property 'IContextManager.Enabled' to false. + + + Service behavior {0} requires that the binding associated with endpoint {1} listening on {2} supports the context protocol, because the contract associated with this endpoint may require a session. Currently configured binding for this endpoint does not support the context protocol. Please modify the binding to add support for the context protocol or modify the SessionMode on the contract to NotAllowed. + + + Binding {1}:{2} is configured with ContextExchangeMechanism.HttpCookie which is not compatible with the transport type {0}. Please modify the ContextExchangeMechanism or use HTTP or HTTPS transport. + + + ContextBindingElement of binding {0}:{1} is configured with ContextExchangeMode.HttpCookie but the configuration of this binding's HttpTransportBindingElement prevents upper channel layers from managing cookies. Please set the HttpTransportBindingElement.AllowCookies property to false or change the ContextExchangeMechanism of ContextBindingElement to SoapHeader. + + + ContextBindingElementImporter cannot import policy because PolicyImportContext.BindingElements collection is null. + + + EndpointAddress: {0}, Via:{1} + + + Context protocol was unable to parse the context header. + + + Context protocol was unable to parse the callback context header. + + + The OLE Transactions header was invalid or corrupt. + + + The WS-AtomicTransaction header was invalid or corrupt. + + + The issued token accompanying the WS-AtomicTransaction coordination context was invalid or corrupt. + + + The OLE Transactions propagation token received in the message could not be used to unmarshal a transaction. It may be invalid or corrupt. + + + The WS-AtomicTransaction extended information included in the OLE Transactions propagation token was invalid or corrupt. + + + An error occurred communicating with the distributed transaction manager. + + + The WS-AtomicTransaction protocol service could not unmarshal the flowed transaction. The following exception occured: {0} + + + The transaction identifier element in the registration header is invalid + + + The context identifier element in the registration header is invalid. + + + The token identifier element in the registration header is invalid. + + + The transaction identifier element in the coordination context is invalid. + + + The WS-AtomicTransaction transaction formatter could not read the registry value '{0}'. + + + The MSDTC transaction manager's WS-AtomicTransaction protocol service '{0}' is disabled and cannot unmarshal incoming transactions. + + + The MSDTC transaction manager has disabled incoming transactions. + + + The incoming transaction cannot be unmarshaled because the source MSDTC transaction manager has either disabled outbound transactions or disabled its WS-AtomicTransaction protocol service. + + + A registration service address could not be created from MSDTC whereabouts information. + + + The MSDTC whereabouts information could not be deserialized. + + + The standard whereabouts signature was missing from the MSDTC whereabouts information. + + + The MSDTC whereabouts information's protocol count was invalid. + + + The MSDTC whereabouts information's host name byte count was invalid. + + + The MSDTC whereabouts information's host name was invalid. + + + The MSDTC whereabouts information did not contain a host name. + + + The specified WSAT protocol version is invalid. + + + The parameter cannot be empty. + + + The requested resouce has not changed and should be taken from cache. + + + The requested resource has moved to one of the following locations:\n{0} + + + The requested resource must be accessed through one of the following intermediary service locations:\n{0} + + + The requested resource has been moved. + + + At least one RedirectionLocation must be provided for this RedirectionType. + + + RedirectionType 'Cache' does not allow any RedirectionLocation objects be passed into the constructor. + + + {0} ({1}) + + + {0} + + + The requested resource is available. + + + Executing user callback. + + + Close '{0}'. + + + Construct ChannelFactory. Contract type: '{0}'. + + + Construct ServiceHost '{0}'. + + + Execute '{0}.{1}'. + + + Execute Async: Begin: '{0}.{1}'; End: '{2}.{3}'. + + + Close ChannelFactory. Contract type: '{0}'. + + + Close ClientBase. Contract type: '{0}'. + + + Close ServiceHost '{0}'. + + + Listen at '{0}'. + + + Open '{0}'. + + + Open ServiceHost '{0}'. + + + Open ChannelFactory. Contract type: '{0}'. + + + Open ClientBase. Contract type: '{0}'. + + + Process action '{0}'. + + + Processing message {0}. + + + Receive bytes on connection '{0}'. + + + Set up Secure Session. + + + Renew Secure Session. + + + Close Security Session. + + + Shared listener connection: '{0}'. + + + Socket connection: '{0}'. + + + Reading data from connection on '{0}'. + + + Receiving data at via '{0}'. + + + Begin method execution. + + + Created: {0} + + + Disposed: {0} + + + Sent a message over a channel + + + Prepared message for sending over a channel + + + ComPlus:channel created. + + + ComPlus:Dispatch method details. + + + ComPlus:DllHost initializer:Adding host. + + + ComPlus:Started DllHost initializer. + + + ComPlus:Starting DllHost initializer. + + + ComPlus:Stopped DllHost initializer. + + + ComPlus:Stopping DllHost initializer. + + + ComPlus:Entering COM+ activity. + + + ComPlus:Executing COM call. + + + ComPlus:Received instance creation request. + + + ComPlus:Created instance. + + + ComPlus:Released instance. + + + ComPlus:Invoked method. + + + ComPlus:Invoking method. + + + Complus:Invoking method with transaction in COM+ context. + + + Complus:Invoking method with new incoming transaction. + + + ComPlus:Left COM+ activity. + + + Complus:Mex channel loader loaded. + + + Complus:Metadata exchange completed successfully. + + + ComPlus:Created service contract. + + + ComPlus:Created service endpoint. + + + ComPlus:Started service. + + + ComPlus:Started service:details. + + + ComPlus:Starting service. + + + ComPlus:Stopped service. + + + ComPlus:Stopping service. + + + ComPlus:Service moniker parsed. + + + ComPlus:Type library converter event. + + + ComPlus:Finished type library import. + + + ComPlus:Type library import: using assembly. + + + ComPlus:Type library import: using type library. + + + ComPlus:Starting type library import. + + + ComPlus:Transaction aborted by COM+ context. + + + ComPlus:Transaction aborted by Transaction Manager. + + + ComPlus:Transaction committed. + + + ComPlus:Typed channel builder loaded. + + + ComPlus:WSDL channel builder loaded. + + + Aborted '{0}'. + + + Failed to abort {0} + + + Failed to close {0} + + + Closed {0} + + + Created {0} + + + Closing {0} + + + Disposing {0} + + + CommunicationObject faulted due to exception. + + + Faulted {0} + + + Failed to open {0} + + + Opened {0} + + + Opening {0} + + + The configuration is read-only. + + + Extension type is not configured. + + + The connection has been abandoned. + + + Connection information. + + + An exception occurred while closing the connections in this connection pool. + + + A connection has exceeded the idle timeout of this connection pool ({0}) and been closed. + + + A connection has exceeded the connection lease timeout of this connection pool ({0}) and been closed. + + + MaxOutboundConnectionsPerEndpoint quota ({0}) has been reached, so connection was closed and not stored in this connection pool. + + + MaxOutboundConnectionsPerEndpoint quota ({0}) has been reached, so the connection was closed and not reused by the listener. + + + No matching <service> tag was found. Default endpoints added. + + + Failed to trace a message + + + Did not understand message header. + + + A response message was received, but there are no outstanding requests waiting for this message. The message is being dropped. + + + The given schema cannot be imported in this format. + + + The type of the element does not match the configuration type. + + + End method execution. + + + Endpoint listener closed. + + + Endpoint listener opened. + + + Error invoking user code + + + Configuration evaluation context not found. + + + Starting Security ExportChannelBinding + + + Finished Security ExportChannelBinding + + + The extension collection does not exist. + + + The extension collection is empty. + + + Extension element not associated with an extension collection. + + + The extension element already exists in the collection. + + + Extension type not found. + + + Failed to set an activity id header on an outgoing message + + + Failed to read an activity id header on a message + + + Evaluating message logging filter against the message exceeded the node quota set on the filter. + + + Get BehaviorElement. + + + Get ChannelEndpointElement. + + + Get machine.config common behaviors. + + + Get configuration section. + + + Get configured binding. + + + Get default configured binding. + + + Get configured endpoint. + + + Get default configured endpoint. + + + Get ServiceElement. + + + Authentication failed for HTTP(S) connection + + + The HTTP SOAPAction header and the wsa:Action SOAP header did not match. + + + Failed to lookup a channel to receive an incoming message. Either the endpoint or the SOAP action was not found. + + + Failed to send request message over HTTP + + + Failed to send response message over HTTP + + + Received bad HTTP response + + + HTTP response was received + + + The HTTP concurrent receive quota was reached. + + + Client certificate is invalid. + + + Client certificate is invalid with native error code {0} (see http://go.microsoft.com/fwlink/?LinkId=187517 for details). + + + Client certificate is required. No certificate was found in the request. This might be because the client certificate could not be successfully validated by the operating system or IIS. For information on how to bypass those validations and use a custom X509CertificateValidator in WCF please see http://go.microsoft.com/fwlink/?LinkId=208540. + + + Starting Security ImportChannelBinding + + + Finished Security ImportChannelBinding + + + An existing incompatible transport manager was found for the specified URI. + + + Initiating Named Pipe connection. + + + Initiating TCP connection. + + + The IssuanceTokenProvider has started a new security negotiation. + + + The IssuanceTokenProvider has completed the security negotiation. + + + The IssuanceTokenProvider applied a redirection header. + + + The IssuanceTokenProvider removed the expired service token. + + + IssuanceTokenProvider pruned service token cache. + + + The IssuanceTokenProvider used the cached service token. + + + Listener created + + + Listener disposed + + + Maximum number of pending connections has been reached. + + + Maximum number of inbound session channel has been reached. + + + A message was closed + + + A message was closed again + + + A message was copied + + + Reached the limit of messages to log. Message logging is stopping. + + + Message not logged because its size exceeds configured quota + + + A message was read + + + Sent a message over a channel. + + + Received a message over a channel. + + + A message was written + + + Switched threads while processing a message. + + + MsmqActivation service cannot peek on the queue. + + + MsmqActivation service cannot discover queues. + + + MSMQ datagram message received. + + + MSMQ datagram message sent. + + + MSMQ detected successfully. + + + Entered batching mode. + + + Expected exception caught. + + + Hosting environment found the base address for the service. + + + Left batching mode. + + + MsmqActivation service found application matching queue. + + + Cannot move or delete message because it is still locked under the transaction. + + + Message was dropped. + + + Message was rejected. + + + Cannot move or delete message. + + + Poison message moved to the poison subqueue. + + + Poison message moved to the retry subqueue. + + + Poison message rejected. + + + Pool of the native MSMQ messages is full. This may affect performance. + + + Transaction which received this message was aborted at least once. + + + MSMQ queue closed. + + + MSMQ queue opened. + + + Cannot detect if the queue is transactional. + + + MsmqActivation service started scan for queues. + + + MSMQ transport session received. + + + MSMQ transport session sent. + + + MSMQ Activation service started application. + + + Hosting environment started service. + + + Unexpected acknowledgment value. + + + Failed to receive a message over a named pipe channel. + + + Received a message over a named pipe channel. + + + NegotiationTokenAuthenticator was attached. + + + NegotiationTokenProvider was attached. + + + No existing transport manager was found for the specified URI. + + + Transport is listening at base URI. + + + The configuration system has detected a duplicate key in a different configuration scope and is overriding with the more recent value. + + + A message was received by a peer channel. + + + A message was sent on a peer channel. + + + A PeerNode received a message that did not match any local channels. + + + A PeerNode received a flooded message that was not propagated further. + + + A PeerNode received a flooded message. + + + Received message could not be forwarded to other neighbors since it exceeded the quota set for the peer node. + + + A Peer Neighbor close has failed. + + + A Peer Neighbor closing has failed. + + + A Peer Neighbor Manager is offline. + + + A Peer Neighbor Manager is online. + + + A message was received by a Peer Neighbor. + + + A Peer Neighbor was not accepted. + + + A Peer Neighbor was not found. + + + A Peer Neighbor open has failed. + + + A Peer Neighbor state change has failed. + + + A Peer Neighbor state has changed. + + + A PeerNode address has changed. + + + A neighbor connection could not be established due to insufficient or wrong credentials. + + + A neighbor security handshake as timed out. + + + A PeerNode was closed. + + + A PeerNode is closing. + + + Peer node open failed. + + + A PeerNode was opened. + + + A PeerNode is opening. + + + Message source could not be authenticated. + + + PeerService Opened and listening at '{0}'. + + + A performance counter failed to load. Some performance counters will not be available. + + + Failed to load the performance counter '{0}'. Some performance counters will not be available + + + There was an error while updating the performance counter '{0}'. This performance counter will be disabled. + + + Loading performance counters for the service failed. Performance counters will not be available for this service. + + + Unloading the performance counters failed. + + + Registered addresses in PNRP. + + + Resolved addresses in PNRP. + + + Unexpected Exception during PNRP resolve operation. + + + Unregistered addresses in PNRP. + + + A null Message (signalling end of channel) was received from a datagram channel, but the channel is still in the Opened state. This indicates a bug in the datagram channel, and the demuxer receive loop has been prematurely stalled. + + + PeerMaintainer Activity. + + + A reliable channel has been opened. + + + Behavior type already exists in the collection + + + Received reply over request channel + + + A failure occured while performing a security related operation. + + + An active security session was removed by the server. + + + A failure occurred while writing to the security audit log. + + + The security audit log is written successfully. + + + The security protocol verified the incoming message. + + + The security protocol secured the outgoing message. + + + The security protocol cannot secure the outgoing message. + + + The security protocol cannot verify the incoming message. + + + The client security session renewed the session key. + + + A Close message was sent by the client security session. + + + Close response message was sent by client security session. + + + Close message was received by client security session.TraceCodeSecurityClientSessionKeyRenewed=Client security session renewed session key. + + + The client security session discarded the previous session key. + + + The SecurityContextSecurityToken cache is full. + + + Identity cannot be determined for an EndpointReference. + + + Identity was determined for an EndpointReference. + + + The HostName portion of an endpoint address cannot be normalized. + + + Identity verification failed. + + + Identity verification succeeded. + + + Security impersonation failed at the server. + + + Security Impersonation succeeded at the server. + + + An inactive security session was faulted by the server. + + + Service security negotiation processing failure. + + + A new security session key was issued by the server. + + + A pending security session was added to the server. + + + The pending security session was closed by the server. + + + A pending security session was activated by the server. + + + The server security session received a close message from the client. + + + Server security session received Close response message from client. + + + Server security session sent session aborted fault to client. + + + The security session key was updated by the server. + + + The server security session sent a key renewal fault to the client. + + + The server security session sent a close response to the client. + + + Server security session sent Close to client. + + + Client security session received session aborted fault from server. + + + Failure sending security session aborted fault to client. + + + The client security session received a closed reponse from the server. + + + A failure occurred when sending a security session Close response to the client. + + + Failure sending security session Close to client. + + + The client security session received a key renewal fault from the server. + + + The client security session was redirected. + + + A failure occurred when sending a renewal fault on the security session key to the client. + + + The client security session operation failed. + + + The security session operation completed successfully at the client. + + + A security session operation was started at the client. + + + The security session operation failed at the server. + + + The ServicePrincipalName could not be mapped to a SecurityIdentifier. + + + Security Token Authenticator was closed. + + + Security Token Authenticator was opened. + + + Security Token Provider was closed. + + + Security Token Provider was opened. + + + ServiceChannel information. + + + ServiceHost base addresses. + + + ServiceHost close operation timedout. + + + ServiceHost faulted. + + + ServiceHost error on calling ReleasePerformanceCounters. + + + The system hit the limit set for throttle '{0}'. Limit for this throttle was set to {1}. Throttle value can be changed by modifying attribute '{2}' in serviceThrottle element or by modifying '{0}' property on behavior ServiceThrottlingBehavior. + + + The system hit an internal throttle limit. Limit for this throttle was set to {0}. This throttle cannot be configured. + + + The system hit the limit set for the '{0}' throttle. Throttle value can be changed by modifying {0} property on {1}. + + + Switched threads while processing a message for Contract '{0}' at Address '{1}'. ConcurrencyMode for service is set to Single/Reentrant and the service is currently processing another message. + + + Switched threads while processing a message for Contract '{0}' at Address '{1}'. Cannot process more than one transaction at a time and the transaction associated with the previous message is not yet complete. Ensure that the caller has committed the transaction. + + + Switched threads while processing a message for Contract '{0}' at Address '{1}'. Waiting for the completion of ReceiveContext acknowledgement. If your service seems to be not processing the message ensure that the channel implementation of receive context completes the operation. + + + Switched threads while processing a message for Contract '{0}' at Address '{1}'. UseSynchronizationContext property on ServiceBehaviorAttribute is set to true, and SynchronizationContext.Current was non-null when opening ServiceHost. If your service seems to be not processing messages, consider setting UseSynchronizationContext to false. + + + Replying to an operation threw a exception. + + + The Request/Reply operation {0} has no Reply Message. + + + The Request/Reply operation {0} has no IRequestContext to use for the reply. + + + Service security negotiation completed. + + + The incoming message is not part of an existing security session. + + + Create ServiceHost. + + + The TransportManager was successfully closed. + + + A named pipe was successfully duplicated. + + + A socket was successfully duplicated. + + + The PROCESS_DUP_HANDLE access right has been granted to the {0} service's account with SID '{1}'. + + + The TransportManager is now successfully listening. + + + Behavior type is not of expected type + + + An attempt to reuse a pooled connection failed. Another attempt will be made with {0} remaining in the overall timeout. + + + An attempt to connect to the named pipe endpoint at '{1}' failed. Another attempt will be made with {0} remaining in the overall timeout. + + + The operating system's timer resolution was detected as {0} ticks, which is about {1} milliseconds. + + + RequestContext aborted + + + PipeConnection aborted + + + The shared memory for the endpoint of the service '{0}' does not exist. The service may not be started. + + + SocketConnection aborted + + + SocketConnection aborted under Close + + + SocketConnection close + + + SocketConnection create + + + SpnegoTokenProvider completed SSPI negotiation. + + + SpnegoTokenAuthenticator completed SSPI negotiation. + + + Client's outgoing SSPI negotiation. + + + Service's outgoing SSPI negotiation. + + + The remote SSL client failed to provide a required certificate. + + + The stream security upgrade was accepted successfully. + + + Failed to receive a message over TCP channel + + + Received a message over TCP channel + + + Understood message header. + + + No service available to handle this action + + + Unhandled exception in user operation '{0}.{1}'. + + + Webhost could not activate service + + + Webhost couldn't compile service + + + Setting a value via WMI. + + + A non-critical error or warning occurred during WSDL Export + + + A non-critical error or warning occurred in the MetadataExchangeClient during WSDL Import This could result in some endpoints not being imported. + + + An incoming channel was disposed because there was an error while attempting to open it. + + + Listen at '{0}'. + + + An invalid create sequence message was received. + + + An invalid WS-RM message was received. + + + An incoming create sequence request was rejected because the maximum pending channel count was reached. + + + A message in a WS-RM sequence has been dropped because it could not be buffered. + + + The reliable session infrastructure detected a system clock change. This will temporarily result in a less optimal message retry strategy. + + + WS-RM SequenceAcknowledgement received. + + + WS-RM Last Sequence message received. + + + WS-RM Sequence message received. + + + WS-RM SequenceAcknowledgement sent. + + + WS-RM Last Sequence message sent. + + + WS-RM Sequence message sent. + + + A WS-RM sequence has faulted. + + + Channel connection was dropped + + + An async callback threw an exception! + + + The MetadataExchangeClient is sending a request for metadata. + + + The MetadataExchangeClient received a reply. + + + The ServiceDebugBehavior Help Page is enabled at a relative address and cannot be created because there is no base address. + + + The TCP connect operation failed. + + + The transaction '{0}' was received for operation '{1}' from a transacted transport, such as MSMQ. + + + The transaction '{0}' was flowed to operation '{1}'. + + + The transaction '{0}' was received for operation '{1}' from an InstanceContext transaction. + + + Existing transaction '{0}' being used for operation '{1}'. + + + The transaction '{0}' for operation '{1}' was completed due to the TransactionAutoComplete OperationBehaviorAttribute member being set to true. + + + The transaction '{0}' for operation '{1}' was completed due to an unhandled execution exception. + + + The transaction '{0}' for operation '{1}' was completed due to a call to SetTransactionComplete. + + + The transaction '{0}' was completed when the session was closed due to the TransactionAutoCompleteOnSessionClose ServiceBehaviorAttribute member. + + + The transaction '{0}' for operation '{1}' was completed due to asynchronous abort. + + + The transaction '{0}' for operation '{1}' remains attached to the InstanceContext. + + + The transaction '{0}' was aborted because it was uncompleted when the session was closed and the TransactionAutoCompleteOnSessionClose OperationBehaviorAttribute was set to false. + + + The service instance was released on the completion of the transaction '{0}' because the ReleaseServiceInstanceOnTransactionComplete ServiceBehaviorAttribute was set to true. + + + The transaction '{0}' was asynchronously aborted. + + + The OleTransactions protocol negotiation failed for coordination context '{0}'. + + + The transaction '{0}' for operation '{1}' was newly created. + + + Activating message received. + + + InstanceContext cached for InstanceId {0}. + + + InstanceContext for InstanceId {0} removed from cache. + + + DurableInstance's InstanceContext refcount incremented. + + + DurableInstance's InstanceContext refcount decremented. + + + ContextChannel created. + + + A new ContextChannel was accepted. + + + Context added to Message. + + + Context retrieved from Message. + + + WorkflowServiceHost created. + + + ServiceDurableInstance '{0}' deleted from persistence store. + + + ServiceDurableInstance '{0}' disposed. + + + ServiceDurableInstance loaded from persistence store. + + + ServiceDurableInstance saved to persistence store. + + + WorkflowDurableInstance '{0}' loaded. + + + WorkflowDurableInstance '{0}' activated. + + + WorkflowDurableInstance aborted. + + + Work item enqueued. + + + Reply sent for InstanceId {0}. + + + Fault Sent for InstanceId {0}. + + + Sql execution started. + + + Sql execution complete. + + + SqlPersistenceProvider.Open() parameters. + + + SynchronizationContextWorkflowSchedulerService - Timer {0} cancelled. + + + SynchronizationContextWorkflowSchedulerService - Timer {0} created for InstanceId {1}. + + + Reading of a syndication feed started. + + + Reading of a syndication feed completed. + + + Reading of a syndication item started. + + + Reading of a syndication item completed. + + + Writing of a syndication feed started. + + + Writing of a syndication feed completed. + + + Writing of a syndication item started. + + + Writing of a syndication item completed. + + + Syndication element with name '{0}' and namespace '{1}' was not written. + + + Syndication element with name '{0}' and namespace '{1}' is invalid. + + + HTTP query string parameter with name '{0}' was ignored. + + + Incoming HTTP request with URI '{0}' matched operation '{1}'. + + + Incoming HTTP request with URI '{0}' does not match any operation. + + + Parameter 'baseAddress' must an absolute uri. + + + BaseAddress must an absolute uri. + + + Cannot change BaseAddress after calling MakeReadOnly. + + + There were multiple UriTemplateMatch results, but MatchSingle was called. + + + BaseAddress has not been set. Set the BaseAddress property before calling MakeReadOnly, Match, or MatchSingle. + + + KeyValuePairs must have at least one element. + + + UriTemplate '{0}' contains {1} path variables and {2} query variables but {3} values were passed to the BindByPosition method. The number of values passed to BindByPosition should be greater than or equal to the number of path variables in the template and cannot be greater than the total number of variables in the template. + + + baseAddress must an absolute Uri. + + + The UriTemplate '{0}' is not valid; each portion of the query string must be of the form 'name' or of the form 'name=value', where each name is unique. Note that the names are case-insensitive. See the documentation for UriTemplate for more details. + + + The UriTemplate '{0}' is not valid; the query string cannot end with '&amp;'. See the documentation for UriTemplate for more details. + + + The UriTemplate '{0}' is not valid; each portion of the query string must be of the form 'name' or of the form 'name=value'. See the documentation for UriTemplate for more details. + + + The UriTemplate '{0}' is not valid; the UriTemplate variable named '{1}' appears multiple times in the template. Note that UriTemplate variable names are case-insensitive. See the documentation for UriTemplate for more details. + + + UriTemplateTable does not support '{0}' and '{1}' since they are not equivalent, but cannot be disambiguated because they have equivalent paths and the same common literal values for the query string. See the documentation for UriTemplateTable for more detail. + + + UriTemplateTable does not support multiple templates that have equivalent path as template '{0}' but have different query strings, where the query strings cannot all be disambiguated via literal values. See the documentation for UriTemplateTable for more detail. + + + UriTemplateTable (with allowDuplicateEquivalentUriTemplates = false) does not support both '{0}' and '{1}', since they are equivalent. Call MakeReadOnly with allowDuplicateEquivalentUriTemplates = true to use both of these UriTemplates in the same table. See the documentation for UriTemplateTable for more detail. + + + UriTemplate does not support '{0}' as a valid format for a segment or a query part. + + + The path variable '{0}' in the UriTemplate must be bound to a non-empty string value. + + + UriTemplate '{0}' contains no variables; yet the BindByPosition method was called with {1} values. + + + UTCSR - Lookup was called before match + + + The UriTemplate '{0}' is not valid; UriTemplate does not support two adjacent variables with no literal in compound segments, such as in the segment '{1}'. + + + The UriTemplate '{0}' is not valid; each portion of the query string must be of the form 'name=value', when value cannot be a compound segment. See the documentation for UriTemplate for more details. + + + The UriTemplate '{0}' is not valid; each portion of the query string must be of the form 'name' or of the form 'name=value', where name is a simple literal. See the documentation for UriTemplate for more details. + + + Changing an inline default value with information from the additional default values is not supported; the default value to the variable '{0}' was already provided as part of the UriTemplate '{1}'. See the documentation for UriTemplate for more details. + + + The default values of UriTemplate are immutable; they cannot be modified after the construction of the UriTemplate instance. See the documentation of UriTemplate for more details. + + + The UriTemplate '{0}' is not valid; the UriTemplate compound path segment '{1}' provides a default value to variable '{2}'. Note that UriTemplate doesn't support default values to variables in compound segments. See the documentation for UriTemplate for more details. + + + The UriTemplate '{0}' is not valid; the UriTemplate variable declaration '{1}' provides a default value to query variable '{2}'. Note that UriTemplate doesn't support default values to query variables. See the documentation for UriTemplate for more details. + + + The UriTemplate '{0}' is not valid; the UriTemplate variable declaration '{1}' provides an empty default value to path variable '{2}'. Note that UriTemplate path variables cannot be bound to a null or empty value. See the documentation for UriTemplate for more details. + + + The UriTemplate '{0}' is not valid; the UriTemplate variable declaration '{1}' isn't a valid variable construct. Note that UriTemplate variable definitions are either a simple, non-empty, variable name or a 'name=value' format, where the name must not be empty and the value provides a default value to the variable. See the documentation for UriTemplate for more details. + + + The UriTemplate '{0}' is not valid; the wildcard ('{1}') cannot appear in a variable name or literal, unless as a construct for a wildcard segment. Note that a wildcard segment, either a literal or a variable, is valid only as the last path segment in the template; the wildcard can appear only once. See the documentation for UriTemplate for more details. + + + The UriTemplate '{0}' is not valid; the UriTemplate last path segment '{1}' provides a default value to final star variable '{2}'. Note that UriTemplate doesn't support default values to final star variable. See the documentation for UriTemplate for more details. + + + The UriTemplate '{0}' is not valid; the path variable '{1}', defined as part of a compound path segment has been provided with a default value as part of the additional defaults. Note that UriTemplate doesn't support default values to variables in compound segments. See the documentation for UriTemplate for more details. + + + The UriTemplate '{0}' is not valid; the query variable '{1}' has been provided a default value as part of the additional defaults. Note that UriTemplate doesn't support default values to query variables. See the documentation for UriTemplate for more details. + + + The UriTemplate '{0}' is not valid; the additional default value '{1}' has a null value as default value. Note that null default values must be only provided to concrete path variables. See the documentation for UriTemplate for more details. + + + The UriTemplate '{0}' is not valid; the UriTemplate path variable '{1}' has a null default value while following path variable '{2}' has no defaults or provides a non-null default value. Note that UriTemplate path variable with null default value must be followed only with other path variables with null defaulted values. See the documentation for UriTemplate for more details. + + + The UriTemplate '{0}' is not valid; the UriTemplate path variable '{1}' has a null default value while the following path segment '{2}' is not a variable segment with a null default value. Note that UriTemplate path variable with null default values must be followed only with other path variables with null defaulted value. See the documentation for UriTemplate for more details. + + + The UriTemplate '{0}' is not valid; the UriTemplate path variable '{1}' has a null default value while the template is finished with a wildcard. Note that UriTemplate path variable with null default values must be followed only with other path variables with null defaulted value. See the documentation for UriTemplate for more details. + + + The UriTemplate '{0}' is not valid; the UriTemplate final star variable '{1}' has been provides a default value as part of the additional defaults information. Note that UriTemplate doesn't support default values to final star variable. See the documentation for UriTemplate for more details. + + + An invalid template '{0}' was passed as the key in a pair of template and its associated object. UriTemplateTable Key-Value pairs must always contain a valid UriTemplate object as key; note that UriTemplateTable doesn't support templates that are ignoring the trailing slash in respect to matching. See the documentation for UriTemplateTable for more details. + + + A null UriTemplate was passed as the key in a pair of template and its associated object. UriTemplateTable Key-Value pairs must always contain a valid UriTemplate object as key. See the documentation for UriTemplateTable for more details. + + + The BindByName method of UriTemplate was called with an empty name in the collection of arguments for the bind. Note that the NameValueCollection or the Dictionary passed to BindByName cannot contain an empty (or null) name as a key. See the documentation of UriTemplate for more details. + + + The UriTemplate contains a literal value for query key '{0}', but that key also is present in the NameValueCollection. Either remove that key from the NameValueCollection, or else change the UriTemplate to not have a query literal for that key. + + + The name of the extension element must be specified. + + + The Rss20Serializer does not support RSS version '{0}'. + + + The Atom10 specification requires '{0}' to have one of these values: \"text\", \"html\", \"xhtml\", however this value is '{1}' in the document being deserialized. + + + Error in line {0} position {1}. + + + An error was encountered when parsing the feed's XML. Refer to the inner exception for more details. + + + An error was encountered when parsing the document's XML. Refer to the inner exception for more details. + + + An error was encountered when parsing the item's XML. Refer to the inner exception for more details. + + + An error was encountered when parsing a DateTime value in the XML. + + + The outer element name must be specified. + + + The element with name '{0}' and namespace '{1}' is not an allowed feed format. + + + The element with name '{0}' and namespace '{1}' is not an allowed document format. + + + The element with name '{0}' and namespace '{1}' is not an allowed item format. + + + The syndication feed formatter must be configured with a syndication feed. + + + The document formatter must be configured with a document. + + + The syndication item formatter must be configured with a syndication item. + + + A feed containing items that are not buffered (i.e. the items are not stored in an IList) cannot clone its items. Buffer the items in the feed before calling Clone on it or pass false to the Clone method. + + + The feed being deserialized has non-contiguous sets of items in it. This is not supported by '{0}'. + + + The feed created a null category. + + + The item created a null category. + + + The feed created a null person. + + + The item created a null person. + + + =The feed created a null item. + + + Reading of a syndication feed started. + + + Reading of a syndication feed completed. + + + Reading of a syndication item started. + + + Reading of a syndication item completed. + + + Writing of a syndication feed started. + + + Writing of a syndication feed completed. + + + Writing of a syndication item started. + + + Writing of a syndication item completed. + + + Syndication XML node of type '{0}' with name '{1}' and namespace '{2}' ignored on read. + + + Reading of a service document started. + + + Reading of a service document completed. + + + Writing of a service document started. + + + Writing of a service document completed. + + + Reading of a categories document started. + + + Reading of a categories document completed. + + + Writing of a categories document started. + + + Writing of a categories document completed. + + + The feed's authors were not serialized as part of serializing the feed in RSS 2.0 format. + + + The feed's contributors were not serialized as part of serializing the feed in RSS 2.0 format. + + + The feed's id was not serialized as part of serializing the feed in RSS 2.0 format. + + + The feed's links were not serialized as part of serializing the feed in RSS 2.0 format. + + + The item's authors were not serialized as part of serializing the feed in RSS 2.0 format. + + + The item's contributors were not serialized as part of serializing the feed in RSS 2.0 format. + + + The item's links were not serialized as part of serializing the feed in RSS 2.0 format. + + + The item's copyrights were not serialized as part of serializing the feed in RSS 2.0 format. + + + The item's content was not serialized as part of serializing the feed in RSS 2.0 format. + + + The item's last updated time was not serialized as part of serializing the feed in RSS 2.0 format. + + + The outer name of the element extension cannot be empty. + + + The Type of object passed as parameter '{0}' is not derived from {1}. Ensure that the type of object passed is either of type {1} or derived from {1}. + + + Failed to impersonate client identity during serialization of the response message. + + + Line {0}, position {1}. + + + end of file + + + element '{0}' from namespace '{1}' + + + end element '{0}' from namespace '{1}' + + + text '{0}' + + + cdata '{0}' + + + comment '{0}' + + + node {0} + + + Start element expected. Found {0}. + + + A single WSDL document could not be generated for this service. Multiple service contract namespaces were found ({0}). Ensure that all your service contracts have the same namespace. + + + You can also access the service description as a single file: + + + The use of '{0}' on the task-based asynchronous method is not supported. + + + Client side task-based asynchronous method must not have any out or ref parameters. Any data that would have been returned through an out or ref parameter should instead be returned as part of the TResult in the resulting task. + + + Generating message contract since the operation has multiple return values. + + + ID0020: The collection is empty. + + + ID2004: IAsyncResult must be the AsyncResult instance returned from the Begin call. The runtime is expecting '{0}', and the actual type is '{1}'. + + + ID3002: WSTrustServiceContract could not create a SecurityTokenService instance from WSTrustServiceContract.SecurityTokenServiceConfiguration. + + + ID3004: Cannot obtain the schema for namespace: '{0}'. + + + ID3022: The WSTrustServiceContract only supports receiving RequestSecurityToken messages. If you need to support more message types, override the WSTrustServiceContract.DispatchRequest method. + + + ID3023: The WSTrustServiceContract only supports receiving RequestSecurityToken messages asynchronously. If you need to support more message types, override the WSTrustServiceContract.BeginDispatchRequest and EndDispatchRequest. + + + ID3097: ServiceHost does not contain any valid Endpoints. Add at least one valid endpoint in the SecurityTokenServiceConfiguration.TrustEndpoints collection. + + + ID3112: Unrecognized RequestType '{0}' specified in the incoming request. + + + ID3113: The WSTrustServiceContract does not support receiving '{0}' messages with the '{1}' SOAP action. If you need to support this, override the ValidateDispatchContext method. + + + ID3114: The WSTrustServiceContract cannot deserialize the WS-Trust request. + + + ID3137: The TrustVersion '{0}', is not supported, only 'TrustVersion.WSTrust13' and 'TrustVersion.WSTrustFeb2005' is supported. + + + ID3138: The RequestSecurityTokenResponse that was received did not contain a SecurityToken. + + + ID3139: The WSTrustChannel cannot compute a proof key. The KeyType '{0}' is not supported. Valid proof key types supported by the WSTrustChannel are WSTrust13 and WSTrustFeb2005. + + + ID3140: Specify one or more BaseAddresses to enable metadata or set DisableWsdl to true in the SecurityTokenServiceConfiguration. + + + ID3141: The RequestType '{0}', is not supported. If you need to support this RequestType, override the corresponding virtual method in your SecurityTokenService derived class. + + + ID3144: The PortType '{0}' Operation '{1}' has Message '{2}' is expected to have only one part but contains '{3}'. + + + ID3146: WsdlEndpointConversionContext.WsdlPort cannot be null. + + + ID3147: WsdlEndpointConversionContext.WsdlPort.Service cannot be null. + + + ID3148: WsdlEndpointConversionContext.WsdlPort.Service.ServiceDescription cannot be null. + + + ID3149: Cannot find an input message type for PortType '({0}, {1})' for operation '{2}' in the given ServiceDescription. + + + ID3150: Cannot find an output message type for PortType '({0}, {1})' for operation '{2}' in the given ServiceDescription. + + + ID3190: The WSTrustChannel cannot compute a proof key without a valid SecurityToken set as the RequestSecurityToken.UseKey when the RequestSecurityToken.KeyType is '{0}'. + + + ID3191: The WSTrustChannel received a RequestedSecurityTokenResponse message containing an Entropy without a ComputedKeyAlgorithm. + + + ID3192: The WSTrustChannel cannot compute a proof key. The received RequestedSecurityTokenResponse does not contain a RequestedProofToken and the ComputedKeyAlgorithm specified in the response is not supported: '{0}'. + + + ID3193: The WSTrustChannel cannot compute a proof key. The received RequestedSecurityTokenResponse indicates that the proof key is computed using combined entropy. However, the response does not include an entropy. + + + ID3194: The WSTrustChannel cannot compute a proof key. The received RequestedSecurityTokenResponse indicates that the proof key is computed using combined entropy. However, the request does not include an entropy. + + + ID3269: Cannot determine the TrustVersion. It must either be specified explicitly, or a SecurityBindingElement must be present in the binding. + + + ID3270: The WSTrustChannel does not support multi-leg issuance protocols. The RSTR received from the STS must be enclosed in a RequestSecurityTokenResponseCollection element. + + + ID3285: The WS-Trust operation '{0}' is not valid or unsupported. + + + ID3286: The 'inner' parameter must implement the 'CoreWCF.Channels.IChannel' interface. + + + ID3287: WSTrustChannelFactory does not support changing the value of this property after a channel is created. + + + ID4008: '{0}' does not provide an implementation for '{1}'. + + + ID4039: A custom ServiceAuthorizationManager has been configured. Any custom ServiceAuthorizationManager must be derived from IdentityModelServiceAuthorizationManager. + + + ID4041: Cannot configure the ServiceHost '{0}'. The ServiceHost is in a bad state and cannot be configured. + + + ID4053: The token has WS-SecureConversation version '{0}'. Version '{1}' was expected. + + + ID4072: The SecurityTokenHandler '{0}' registered for TokenType '{1}' must derive from '{2}'. + + + ID4101: The token cannot be validated because it is not a SamlSecurityToken or a Saml2SecurityToken. Token type: '{0}' + + + ID4192: The reader is not positioned on a KeyInfo element that can be read. + + + ID4240: The tokenRequirement must derived from 'RecipientServiceModelSecurityTokenRequirement' for SecureConversationSecurityTokens. The tokenRequirement is of type '{0}'. + + + ID4244: Internal error: sessionAuthenticator must support IIssuanceSecurityTokenAuthenticator. + + + ID4245: Internal error: sessionAuthenticator must support ICommunicationObject. + + + ID4268: MergeClaims must have at least one identity that is not null. + + + ID4271: No IAuthorizationPolicy was found for the Transport security token '{0}'. + + + ID4274: The Configuration property of this SecurityTokenHandler is set to null. Tokens cannot be read or validated in this state. Set this property or add this SecurityTokenHandler to a SecurityTokenHandlerCollection with a valid Configuration property. + + + ID4285: Cannot replace SecurityToken with Id '{0}' in cache with new one. Token must exist in cache to be replaced. + + + ID4287: The SecurityTokenRequirement '{0}' doesn't contain a ListenUri. + + + ID5004: Unrecognized namespace: '{0}'. + + + Authorize + + + OnAuthorizeRequest Failed. + + + OnAuthorizeRequest Succeeded. + + + Authentication failed. + + + The IssuedSecurityTokenProvider cannot support the FederatedClientCredentialsParameters. The FederatedClientCredentialsParameters has already provided the '{0}' parameter. + + + The TrustVersion '{0}', is not supported, only 'TrustVersion.WSTrust13' and 'TrustVersion.WSTrustFeb2005' is supported. + + + The input {0} must be a '{1}' object. + + + The input handler list cannot be empty. + + + The '{0}' list is invalid because the property '{1}' of '{2}' is not null. + + + The '{0}' list created by the Func '{1}' is invalid because it contains one or more null items. + + + The config element '{0}' is invalid because the attribute '{1}' and the sub element '{2}' were both specified. These are mutually exclusive items and cannot be used simultaneouly. + + + This '{0}' object cannot be used to generate configuration because it was created with the constructor that takes a '{1}' as the paramter. This functionality is not supported through configuration files. Please use a different constructor if you wish to generate a configuration file. + + + Invalid type: '{0}'. It must inherit from base type '{1}', cannot be abstract, and must expose a public default constructor. + + + '{0}' cannot return a null '{1}' instance. Please ensure that '{0}' returns a valid '{1}' instance. + + + HTTP pipeline operation cancelled. + + + The message property '{0}' is missing in the HttpRequestMessage. Please make sure this property not removed or changed from the properties of the HttpRequestMessage. If you are creating a new HttpRequestMessage, please copy this property from the old message to the new one. + + + The message property '{0}' inside the HttpRequestMessage is not with expected type '{1}'. Please make sure this property not removed or changed from the properties of the HttpRequestMessage. If you are creating a new HttpRequestMessage, please copy this property from the old message to the new one. + + + The value '{0}' is not a valid content type. + + + The property '{0}' is not supported when building a ChannelFactory. The property value must be null when calling BuildChannelFactory. + + + Cound not load type '{0}' from the assemblies in current AppDomain. + + + The HTTP response message should not be null. Please ensure your '{0}' instance returns a non-null '{1}' object. + + + The subprotocol '{0}' was not requested by the client - no '{1}' header was included in the request. + + + The subprotocol '{0}' was not requested by the client. The client requested the following subprotocol(s): '{1}'. + + + The subprotocol '{0}' is invalid because it contains the invalid character '{1}'. + + + The value specified ('{0}') contains more than one subprotocol which is not supported. + + + Empty string is not a valid subprotocol value. Please use "null" to specify no value. + + + This method is not supported for this HTTP content. + + + Invalid value for the {0} type. The type '{1}' does not derive from the appropriate base class '{2}' or is abstract. + + + This service only supports WebSocket connections. + + + This service does not support WebSocket connections. + + + WebSocket upgrade request failed. Received response status code '{0} ({1})', expected: '{2} ({3})'. + + + WebSocket upgrade request failed. The header '{0}' is missing in the response. + + + WebSocket upgrade request failed. The value of header '{0}' is '{1}'. The expected value is '{2}'. + + + Unexpected response - the server accepted the upgrade request but specified the subprotocol '{0}' when no subprotocol was requested. + + + WebSocket object cannot be accessed directly. + + + A WebSocket error occurred. + + + Unexpected WebSocket close message received when receiving a message. + + + Cannot write to the stream because the end of the stream marker was already written. + + + HttpChannelFactory cannot create the channel with shape '{0}' when the {1} of {2} was set as '{3}'. + + + Maximum number of pending WebSocket connections ({0}) has been reached. Consider increasing the '{1}' quota on the '{2}' property of the transport. + + + The opening handshake properties associated with the current WebSocket connection are not available. The most likely cause is that the property '{0}' on the '{1}' object returned from the custom '{2}' is not set. + + + The operation to establish the WebSocket connection timed out. To increase this time limit, use the OpenTimeout property on the service endpoint's binding. + + + The task was cancelled. + + + An error occured when getting the WebSocketVersion from the WebSocket factory of type '{0}'. See the inner exception for details. + + + The WebSocketVersion returned by the WebSocket factory of type '{0}' is either null, empty or invalid. + + + An error occurred when creating the WebSocket with the factory of type '{0}'. See the inner exception for details. + + + WebSocket creation failed. The '{0}' returned a WebSocket that is either null or not opened. + + + The WebSocket returned by the factory of type '{0}' has the SubProtocol '{1}' that doesn't match the requested SubProtocol value '{2}'. + + + The '{0}' contains multiple '{1}' objects, which is invalid. At most one '{1}' should be specified. + + + The Send operation timed out after '{0}'. Increase the SendTimeout value on the Binding. The time allotted to this operation may have been a portion of a longer timeout. + + + The Receive operation timed out after '{0}'. For duplex sessionful channels, the receive timeout is also the idle timeout for the channel, so consider setting a suitably large value for the ReceiveTimeout value on the Binding. The time allotted to this operation may have been a portion of a longer timeout. + + + The '{0}' operation timed out after '{1}'. The time allotted to this operation may have been a portion of a longer timeout. + + + This platform does not support server side WebSockets. + + + This platform does not support client side WebSockets natively. Support for client side WebSockets can be enabled on this platform by providing an implementation of {0}. + + + WebSockets are not supported in the classic pipeline mode. Consider using the integrated pipeline mode for the application pool. + + + The WebSocketModule is not loaded. Check if the WebSocket feature is installed and the WebSocketModule is enabled in the list of IIS modules (see http://go.microsoft.com/fwlink/?LinkId=231398 for details). + + + The name of the policy being imported for contract '{0}:{1}' is invalid:'{2}'. It should be either '{3}', '{4}' or '{5}'. + + + The server didn't accept the connection request. It is possible that the WebSocket protocol version on your client doesn't match the one on the server('{0}'). + + + The server didn't accept the connection request. It is possible that the WebSocket subprotocol sent by your client is not supported by the server. Protocol(s) supported by the server are '{0}'. + + + The server didn't accept the connection request. It is possible that the client side message encoding format doesn't match the setting on the server side. Please check your binding settings. + + + The server didn't accept the connection request. It is possible that the client side message encoding format or message transfer mode doesn't match the setting on the server side. Please check your binding settings. + + + This collection holds request headers and cannot contain the specified response header '{0}'. + + + This collection holds response headers and cannot contain the specified request header '{0}'. + + + Support for {0} and {1} can not be enabled with {2} when the {3} of the {4} is '{5}'. Ensure the {4} used with the binding has a {3} of '{6}'. + + + Enumeration has either not started or has already finished. + + + The parameter '{0}' cannot be an empty string. + + + Specified value has invalid Control characters. + + + Specified value has invalid CRLF characters. + + + Specified value has invalid HTTP Header characters. + + + Specified value has invalid non-ASCII characters. + + + Specified argument was out of the range of valid values. + + + Failed to copy the HTTP header '{0}' with value '{1}' to '{2}'. + + + The size quota for this stream ({0}) has been exceeded. + + + The IAsyncResult implementation '{0}' tried to complete a single operation multiple times. This could be caused by an incorrect application IAsyncResult implementation or other extensibility code, such as an IAsyncResult that returns incorrect CompletedSynchronously values or invokes the AsyncCallback multiple times. + + + Async Callback threw an exception. + + + A null value was returned from an async 'Begin' method or passed to an AsyncCallback. Async 'Begin' implementations must return a non-null IAsyncResult and pass the same IAsyncResult object as the parameter to the AsyncCallback. + + + An incorrect implementation of the IAsyncResult interface may be returning incorrect values from the CompletedSynchronously property or calling the AsyncCallback more than once. The type {0} could be the incorrect implementation. + + + An incorrect implementation of the IAsyncResult interface may be returning incorrect values from the CompletedSynchronously property or calling the AsyncCallback more than once. + + + An incorrect IAsyncResult was provided to an 'End' method. The IAsyncResult object passed to 'End' must be the one returned from the matching 'Begin' or passed to the callback provided to 'Begin'. + + + End cannot be called twice on an AsyncResult. + + + This buffer cannot be returned to the buffer manager because it is the wrong size. + + + The argument must be a non-empty string. + + + The ActionItem was already scheduled for execution that hasn't been completed yet. + + + A Dequeue operation timed out after {0}. The time allotted to this operation may have been a portion of a longer timeout. + + + The task timed out after {0}. The time allotted to this operation may have been a portion of a longer timeout. + + + '{0}' is an invalid XmlNodeType. + + + Object synchronization method was called from an unsynchronized block of code. + + + Argument {0} must be a non-negative timeout value. Provided value was {1}. + + + The specified method handle is incorrect for the proxy of type '{0}' + + + Failed to create a typed proxy for type '{0}' + + + Stream returned by OperationStreamProvider cannot be null. + + + The value '{0}' cannot be parsed as the type '{1}'. + + + Argument {0} must be a positive timeout value. Provided value was {1}. + + + Cannot claim lock within the allotted timeout of {0}. The time allotted to this operation may have been a portion of a longer timeout. + + \ No newline at end of file diff --git a/src/CoreWCF.Http/tests/ClientContract/IEchoService.cs b/src/CoreWCF.Http/tests/ClientContract/IEchoService.cs new file mode 100644 index 000000000..48f8512da --- /dev/null +++ b/src/CoreWCF.Http/tests/ClientContract/IEchoService.cs @@ -0,0 +1,34 @@ +using System.IO; +using System.ServiceModel; +using System.Threading.Tasks; + +namespace ClientContract +{ + internal static class Constants + { + public const string NS = "http://tempuri.org/"; + public const string ECHOSERVICE_NAME = nameof(IEchoService); + public const string OPERATION_BASE = NS + ECHOSERVICE_NAME + "/"; + } + + [ServiceContract(Namespace = "http://tempuri.org/", Name = "IEchoService")] + public interface IEchoService + { + [OperationContract(Name = "EchoString", Action = Constants.OPERATION_BASE + "EchoString", + ReplyAction = Constants.OPERATION_BASE + "EchoStringResponse")] + string EchoString(string echo); + + [OperationContract(Name = "EchoStream", Action = Constants.OPERATION_BASE + "EchoStream", + ReplyAction = Constants.OPERATION_BASE + "EchoStreamResponse")] + Stream EchoStream(Stream echo); + + [OperationContract(Name = "EchoStringAsync", Action = Constants.OPERATION_BASE + "EchoStringAsync", + ReplyAction = Constants.OPERATION_BASE + "EchoStringAsyncResponse")] + string EchoStringAsync(string echo); + + [OperationContract(Name = "EchoStreamAsync", Action = Constants.OPERATION_BASE + "EchoStreamAsync", + ReplyAction = Constants.OPERATION_BASE + "EchoStreamAsyncResponse")] + Stream EchoStreamAsync(Stream echo); + + } +} \ No newline at end of file diff --git a/src/CoreWCF.Http/tests/CoreWCF.Http.Tests.csproj b/src/CoreWCF.Http/tests/CoreWCF.Http.Tests.csproj new file mode 100644 index 000000000..d84d265f5 --- /dev/null +++ b/src/CoreWCF.Http/tests/CoreWCF.Http.Tests.csproj @@ -0,0 +1,25 @@ + + + netcoreapp2.2 + + CoreWCF.Http.Tests + CoreWCF.Http.Tests + 40 + D:\git\wcfcore\src\Backup\CoreWCF.Http\tests\ + 2.0 + + + + + + + + all + runtime; build; native; contentfiles; analyzers + + + + + + + \ No newline at end of file diff --git a/src/CoreWCF.Http/tests/ISimpleService.cs b/src/CoreWCF.Http/tests/ISimpleService.cs new file mode 100644 index 000000000..2e94835c8 --- /dev/null +++ b/src/CoreWCF.Http/tests/ISimpleService.cs @@ -0,0 +1,11 @@ +using CoreWCF; +using System; +using System.Collections.Generic; +using System.Text; + +[ServiceContract] +interface ISimpleService +{ + [OperationContract] + string Echo(string echo); +} diff --git a/src/CoreWCF.Http/tests/Properties/launchSettings.json b/src/CoreWCF.Http/tests/Properties/launchSettings.json new file mode 100644 index 000000000..85ed26f8c --- /dev/null +++ b/src/CoreWCF.Http/tests/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:1857/", + "sslPort": 0 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "Microsoft.ServiceModel.Http.Tests": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "http://localhost:1858/" + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Http/tests/ServiceContract/IEchoService.cs b/src/CoreWCF.Http/tests/ServiceContract/IEchoService.cs new file mode 100644 index 000000000..05a79c9e5 --- /dev/null +++ b/src/CoreWCF.Http/tests/ServiceContract/IEchoService.cs @@ -0,0 +1,33 @@ +using System.IO; +using System.Threading.Tasks; +using CoreWCF; + +namespace ServiceContract +{ + internal static class Constants + { + public const string NS = "http://tempuri.org/"; + public const string ECHOSERVICE_NAME = nameof(IEchoService); + public const string OPERATION_BASE = NS + ECHOSERVICE_NAME + "/"; + } + + [ServiceContract(Namespace = Constants.NS, Name = Constants.ECHOSERVICE_NAME)] + public interface IEchoService + { + [OperationContract(Name = "EchoString", Action = Constants.OPERATION_BASE + "EchoString", + ReplyAction = Constants.OPERATION_BASE + "EchoStringResponse")] + string EchoString(string echo); + + [OperationContract(Name = "EchoStream", Action = Constants.OPERATION_BASE + "EchoStream", + ReplyAction = Constants.OPERATION_BASE + "EchoStreamResponse")] + Stream EchoStream(Stream echo); + + [OperationContract(Name = "EchoStringAsync", Action = Constants.OPERATION_BASE + "EchoStringAsync", + ReplyAction = Constants.OPERATION_BASE + "EchoStringAsyncResponse")] + Task EchoStringAsync(string echo); + + [OperationContract(Name = "EchoStreamAsync", Action = Constants.OPERATION_BASE + "EchoStreamAsync", + ReplyAction = Constants.OPERATION_BASE + "EchoStreamAsyncResponse")] + Task EchoStreamAsync(Stream echo); + } +} \ No newline at end of file diff --git a/src/CoreWCF.Http/tests/Services/EchoService.cs b/src/CoreWCF.Http/tests/Services/EchoService.cs new file mode 100644 index 000000000..a6d033aa2 --- /dev/null +++ b/src/CoreWCF.Http/tests/Services/EchoService.cs @@ -0,0 +1,35 @@ +using System.IO; +using System.Threading.Tasks; + +namespace Services +{ + public class EchoService : ServiceContract.IEchoService + { + public string EchoString(string echo) + { + return echo; + } + + public Stream EchoStream(Stream echo) + { + var stream = new MemoryStream(); + echo.CopyTo(stream); + stream.Position = 0; + return stream; + } + + public async Task EchoStringAsync(string echo) + { + await Task.Yield(); + return echo; + } + + public async Task EchoStreamAsync(Stream echo) + { + var stream = new MemoryStream(); + await echo.CopyToAsync(stream); + stream.Position = 0; + return stream; + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Http/tests/SimpleService.cs b/src/CoreWCF.Http/tests/SimpleService.cs new file mode 100644 index 000000000..a85bf6c08 --- /dev/null +++ b/src/CoreWCF.Http/tests/SimpleService.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +class SimpleService : ISimpleService +{ + public string Echo(string echo) + { + return echo; + } +} diff --git a/src/CoreWCF.Http/tests/SimpleTest.cs b/src/CoreWCF.Http/tests/SimpleTest.cs new file mode 100644 index 000000000..fc3e6f653 --- /dev/null +++ b/src/CoreWCF.Http/tests/SimpleTest.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore; +using Microsoft.AspNetCore.Hosting; +using Xunit; + +public static class SimpleTest +{ + [Fact] + public static void BasicHttpRequestReplyEchoString() + { + string testString = new string('a', 3000); + var host = CreateWebHostBuilder(new string[0]).Build(); + using (host) + { + host.Start(); + var httpBinding = new System.ServiceModel.BasicHttpBinding(); + var factory = new System.ServiceModel.ChannelFactory(httpBinding, + new System.ServiceModel.EndpointAddress(new Uri("http://localhost:8080/BasicWcfService/basichttp.svc"))); + var channel = factory.CreateChannel(); + var result = channel.EchoString(testString); + Assert.Equal(testString, result); + } + } + + public static IWebHostBuilder CreateWebHostBuilder(string[] args) => + WebHost.CreateDefaultBuilder(args) + .UseKestrel() + .UseUrls("http://localhost:8080") + .UseStartup(); +} diff --git a/src/CoreWCF.Http/tests/Startup.cs b/src/CoreWCF.Http/tests/Startup.cs new file mode 100644 index 000000000..2d43a4c94 --- /dev/null +++ b/src/CoreWCF.Http/tests/Startup.cs @@ -0,0 +1,36 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Hosting.Server.Features; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using CoreWCF; +using CoreWCF.Configuration; +using System; +using System.Collections.Generic; +using System.Text; + +public class Startup +{ + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + services.AddServiceModelServices(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IHostingEnvironment env) + { + app.UseServiceModel(builder => + { + builder.AddService(); + builder.AddServiceEndpoint(new BasicHttpBinding(), "/BasicWcfService/basichttp.svc"); + }); + + app.Run(async (context) => + { + await context.Response.WriteAsync("Hello World!"); + }); + } +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF.NetTcp.csproj b/src/CoreWCF.NetTcp/src/CoreWCF.NetTcp.csproj new file mode 100644 index 000000000..8793c4db2 --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF.NetTcp.csproj @@ -0,0 +1,21 @@ + + + netcoreapp2.2 + CoreWCF.NetTcp + CoreWCF.NetTcp + + 7.2 + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Channels/AddressingVersionExtensions.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/AddressingVersionExtensions.cs new file mode 100644 index 000000000..4c83cd953 --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/AddressingVersionExtensions.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Text; + +namespace CoreWCF.Channels +{ + internal static class AddressingVersionExtensions + { + static AddressingVersionExtensions() + { + s_namespaceGetter = GetStringGetterForProperty("Namespace"); + s_faultActionGetter = GetStringGetterForProperty("FaultAction"); + s_anonymousUriGetter = GetUriGetterForProperty("AnonymousUri"); + s_noneUriGetter = GetUriGetterForProperty("NoneUri"); + } + + private static StringGetter GetStringGetterForProperty(string propName) + { + // NonPublic and public in case they are made public in the future. Currently they are internal + var addrVerType = typeof(AddressingVersion); + var getterPropInfo = typeof(AddressingVersion).GetProperty(propName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); + var getterMethodInfo = getterPropInfo.GetGetMethod(true); + return (StringGetter)getterMethodInfo.CreateDelegate(typeof(StringGetter)); + } + + private delegate string StringGetter(AddressingVersion addressingVersion); + + private static StringGetter s_namespaceGetter; + private static StringGetter s_faultActionGetter; + + public static string Namespace(this AddressingVersion addressingVersion) + { + return s_namespaceGetter(addressingVersion); + } + + public static string FaultAction(this AddressingVersion addressingVersion) + { + return s_faultActionGetter(addressingVersion); + } + + private static UriGetter GetUriGetterForProperty(string propName) + { + // NonPublic and public in case they are made public in the future. Currently they are internal + var getterPropInfo = typeof(AddressingVersion).GetProperty(propName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); + var getterMethodInfo = getterPropInfo.GetGetMethod(true); + return (UriGetter)getterMethodInfo.CreateDelegate(typeof(UriGetter)); + } + + private delegate Uri UriGetter(AddressingVersion addressingVersion); + + private static UriGetter s_anonymousUriGetter; + private static UriGetter s_noneUriGetter; + + public static Uri AnonymousUri(this AddressingVersion addressingVersion) + { + return s_anonymousUriGetter(addressingVersion); + } + + public static Uri NoneUri(this AddressingVersion addressingVersion) + { + return s_noneUriGetter(addressingVersion); + } + } +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Channels/BufferedConnectionListener.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/BufferedConnectionListener.cs new file mode 100644 index 000000000..04242143a --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/BufferedConnectionListener.cs @@ -0,0 +1,301 @@ +using System; +using CoreWCF.Runtime; +using CoreWCF; +using System.Threading; +using System.Threading.Tasks; + +namespace CoreWCF.Channels +{ + class BufferedConnection : DelegatingConnection + { + byte[] writeBuffer; + int writeBufferSize; + int pendingWriteSize; + Exception pendingWriteException; + IOThreadTimer flushTimer; + long flushTimeout; + TimeSpan pendingTimeout; + const int maxFlushSkew = 100; + + public BufferedConnection(IConnection connection, TimeSpan flushTimeout, int writeBufferSize) + : base(connection) + { + this.flushTimeout = Ticks.FromTimeSpan(flushTimeout); + this.writeBufferSize = writeBufferSize; + } + + object ThisLock + { + get { return this; } + } + + public override void Close(TimeSpan timeout, bool asyncAndLinger) + { + TimeoutHelper timeoutHelper = new TimeoutHelper(timeout); + Flush(timeoutHelper.RemainingTime()); + base.Close(timeoutHelper.RemainingTime(), asyncAndLinger); + } + + void CancelFlushTimer() + { + if (flushTimer != null) + { + flushTimer.Cancel(); + pendingTimeout = TimeSpan.Zero; + } + } + + void Flush(TimeSpan timeout) + { + ThrowPendingWriteException(); + + lock (ThisLock) + { + FlushCore(timeout); + } + } + + void FlushCore(TimeSpan timeout) + { + if (pendingWriteSize > 0) + { + Connection.Write(writeBuffer, 0, pendingWriteSize, false, timeout); + pendingWriteSize = 0; + } + } + + void OnFlushTimer(object state) + { + lock (ThisLock) + { + try + { + FlushCore(pendingTimeout); + pendingTimeout = TimeSpan.Zero; + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + + pendingWriteException = e; + CancelFlushTimer(); + } + } + } + + void SetFlushTimer() + { + if (flushTimer == null) + { + int flushSkew = Ticks.ToMilliseconds(Math.Min(flushTimeout / 10, Ticks.FromMilliseconds(maxFlushSkew))); + flushTimer = new IOThreadTimer(new Action(OnFlushTimer), null, true, flushSkew); + } + flushTimer.Set(Ticks.ToTimeSpan(flushTimeout)); + } + + public override void Write(byte[] buffer, int offset, int size, bool immediate, TimeSpan timeout, BufferManager bufferManager) + { + if (size <= 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("size", size, SR.ValueMustBePositive)); + } + + ThrowPendingWriteException(); + + if (immediate || flushTimeout == 0) + { + WriteNow(buffer, offset, size, timeout, bufferManager); + } + else + { + WriteLater(buffer, offset, size, timeout); + bufferManager.ReturnBuffer(buffer); + } + } + + public override void Write(byte[] buffer, int offset, int size, bool immediate, TimeSpan timeout) + { + if (size <= 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("size", size, SR.ValueMustBePositive)); + } + + ThrowPendingWriteException(); + + if (immediate || flushTimeout == 0) + { + WriteNow(buffer, offset, size, timeout); + } + else + { + WriteLater(buffer, offset, size, timeout); + } + } + + void WriteNow(byte[] buffer, int offset, int size, TimeSpan timeout) + { + WriteNow(buffer, offset, size, timeout, null); + } + + void WriteNow(byte[] buffer, int offset, int size, TimeSpan timeout, BufferManager bufferManager) + { + lock (ThisLock) + { + if (pendingWriteSize > 0) + { + int remainingSize = writeBufferSize - pendingWriteSize; + CancelFlushTimer(); + if (size <= remainingSize) + { + Buffer.BlockCopy(buffer, offset, writeBuffer, pendingWriteSize, size); + if (bufferManager != null) + { + bufferManager.ReturnBuffer(buffer); + } + pendingWriteSize += size; + FlushCore(timeout); + return; + } + else + { + TimeoutHelper timeoutHelper = new TimeoutHelper(timeout); + FlushCore(timeoutHelper.RemainingTime()); + timeout = timeoutHelper.RemainingTime(); + } + } + + if (bufferManager == null) + { + Connection.Write(buffer, offset, size, true, timeout); + } + else + { + Connection.Write(buffer, offset, size, true, timeout, bufferManager); + } + } + } + + void WriteLater(byte[] buffer, int offset, int size, TimeSpan timeout) + { + lock (ThisLock) + { + bool setTimer = (pendingWriteSize == 0); + TimeoutHelper timeoutHelper = new TimeoutHelper(timeout); + + while (size > 0) + { + if (size >= writeBufferSize && pendingWriteSize == 0) + { + Connection.Write(buffer, offset, size, false, timeoutHelper.RemainingTime()); + size = 0; + } + else + { + if (writeBuffer == null) + { + writeBuffer = Fx.AllocateByteArray(writeBufferSize); + } + + int remainingSize = writeBufferSize - pendingWriteSize; + int copySize = size; + if (copySize > remainingSize) + { + copySize = remainingSize; + } + + Buffer.BlockCopy(buffer, offset, writeBuffer, pendingWriteSize, copySize); + pendingWriteSize += copySize; + if (pendingWriteSize == writeBufferSize) + { + FlushCore(timeoutHelper.RemainingTime()); + setTimer = true; + } + size -= copySize; + offset += copySize; + } + } + if (pendingWriteSize > 0) + { + if (setTimer) + { + SetFlushTimer(); + pendingTimeout = TimeoutHelper.Add(pendingTimeout, timeoutHelper.RemainingTime()); + } + } + else + { + CancelFlushTimer(); + } + } + } + + public override AsyncCompletionResult BeginWrite(byte[] buffer, int offset, int size, bool immediate, TimeSpan timeout, + Action callback, object state) + { + TimeoutHelper timeoutHelper = new TimeoutHelper(timeout); + Flush(timeoutHelper.RemainingTime()); + return base.BeginWrite(buffer, offset, size, immediate, timeoutHelper.RemainingTime(), callback, state); + } + + public override void EndWrite() + { + base.EndWrite(); + } + + public override void Shutdown(TimeSpan timeout) + { + TimeoutHelper timeoutHelper = new TimeoutHelper(timeout); + Flush(timeoutHelper.RemainingTime()); + base.Shutdown(timeoutHelper.RemainingTime()); + } + + void ThrowPendingWriteException() + { + if (pendingWriteException != null) + { + lock (ThisLock) + { + if (pendingWriteException != null) + { + Exception exceptionTothrow = pendingWriteException; + pendingWriteException = null; + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(exceptionTothrow); + } + } + } + } + } + + class BufferedConnectionListener : IConnectionListener + { + int writeBufferSize; + TimeSpan flushTimeout; + IConnectionListener connectionListener; + + public BufferedConnectionListener(IConnectionListener connectionListener, TimeSpan flushTimeout, int writeBufferSize) + { + this.connectionListener = connectionListener; + this.flushTimeout = flushTimeout; + this.writeBufferSize = writeBufferSize; + } + + public void Dispose() + { + connectionListener.Dispose(); + } + + public void Listen() + { + connectionListener.Listen(); + } + + public async Task AcceptAsync() + { + return new BufferedConnection(await connectionListener.AcceptAsync(), flushTimeout, writeBufferSize); + + } + } +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Channels/Connection.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/Connection.cs new file mode 100644 index 000000000..854234ca2 --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/Connection.cs @@ -0,0 +1,994 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Net; +using CoreWCF.Runtime; +using System.Threading; +using System.Threading.Tasks; +using System.Diagnostics.Contracts; + +namespace CoreWCF.Channels +{ + // Low level abstraction for a socket/pipe + interface IConnection + { + byte[] AsyncReadBuffer { get; } + int AsyncReadBufferSize { get; } + TraceEventType ExceptionEventType { get; set; } + IPEndPoint RemoteIPEndPoint { get; } + + void Abort(); + // TODO: Consider changing Close to CloseAsync + void Close(TimeSpan timeout, bool asyncAndLinger); + void Shutdown(TimeSpan timeout); + + // TODO: Modify IConnection.BeginWrite to take a CancellationToken instead of using it's own timer + AsyncCompletionResult BeginWrite(byte[] buffer, int offset, int size, bool immediate, TimeSpan timeout, Action callback, object state); + void EndWrite(); + void Write(byte[] buffer, int offset, int size, bool immediate, TimeSpan timeout); + void Write(byte[] buffer, int offset, int size, bool immediate, TimeSpan timeout, BufferManager bufferManager); + + int Read(byte[] buffer, int offset, int size, TimeSpan timeout); + + // TODO: Modify IConnection.BeginRead to take a CancellationToken instead of using it's own timer + AsyncCompletionResult BeginRead(int offset, int size, TimeSpan timeout, Action callback, object state); + int EndRead(); + + // very ugly listener stuff + object DuplicateAndClose(int targetProcessId); + object GetCoreTransport(); + Task ValidateAsync(Uri uri); + } + + internal static class ConnectionExtentions + { + public static Task ReadAsync(this IConnection connection, int offset, int size, TimeSpan timeout) + { + var tcs = new TaskCompletionSource(connection); + if(connection.BeginRead(offset, size, timeout, HandleReadComplete, tcs)==AsyncCompletionResult.Completed) + { + HandleReadComplete(tcs); + } + + return tcs.Task; + } + + private static void HandleReadComplete(object state) + { + var tcs = (TaskCompletionSource)state; + var connection = (IConnection)tcs.Task.AsyncState; + try + { + tcs.TrySetResult(connection.EndRead()); + } + catch (Exception e) + { + tcs.TrySetException(e); + } + } + + public static Task WriteAsync(this IConnection connection, byte[] buffer, int offset, int size, bool immediate, TimeSpan timeout) + { + var tcs = new TaskCompletionSource(connection); + if (connection.BeginWrite(buffer, offset, size, immediate, timeout, HandleWriteComplete, tcs)==AsyncCompletionResult.Completed) + { + HandleWriteComplete(tcs); + } + + return tcs.Task; + } + + public static async Task WriteAsync(this IConnection connection, byte[] buffer, int offset, int size, bool immediate, TimeSpan timeout, BufferManager bufferManager) + { + try + { + await WriteAsync(connection, buffer, offset, size, immediate, timeout); + } + finally + { + bufferManager.ReturnBuffer(buffer); + } + } + + private static void HandleWriteComplete(object state) + { + var tcs = (TaskCompletionSource)state; + var connection = (IConnection)tcs.Task.AsyncState; + try + { + connection.EndWrite(); + tcs.TrySetResult(true); + } + catch (Exception e) + { + tcs.TrySetException(e); + } + } + } + + // Low level abstraction for listening for sockets/pipes + interface IConnectionListener : IDisposable + { + void Listen(); + Task AcceptAsync(); + } + + abstract class DelegatingConnection : IConnection + { + IConnection connection; + + protected DelegatingConnection(IConnection connection) + { + this.connection = connection; + } + + public virtual byte[] AsyncReadBuffer + { + get { return connection.AsyncReadBuffer; } + } + + public virtual int AsyncReadBufferSize + { + get { return connection.AsyncReadBufferSize; } + } + + public TraceEventType ExceptionEventType + { + get { return connection.ExceptionEventType; } + set { connection.ExceptionEventType = value; } + } + + protected IConnection Connection + { + get { return connection; } + } + + public IPEndPoint RemoteIPEndPoint + { + get { return connection.RemoteIPEndPoint; } + } + + public virtual void Abort() + { + connection.Abort(); + } + + public virtual void Close(TimeSpan timeout, bool asyncAndLinger) + { + connection.Close(timeout, asyncAndLinger); + } + + public virtual void Shutdown(TimeSpan timeout) + { + connection.Shutdown(timeout); + } + + public virtual object DuplicateAndClose(int targetProcessId) + { + return connection.DuplicateAndClose(targetProcessId); + } + + public virtual object GetCoreTransport() + { + return connection.GetCoreTransport(); + } + + public virtual Task ValidateAsync(Uri uri) + { + return connection.ValidateAsync(uri); + } + + public virtual AsyncCompletionResult BeginWrite(byte[] buffer, int offset, int size, bool immediate, TimeSpan timeout, + Action callback, object state) + { + return connection.BeginWrite(buffer, offset, size, immediate, timeout, callback, state); + } + + public virtual void EndWrite() + { + connection.EndWrite(); + } + + public virtual void Write(byte[] buffer, int offset, int size, bool immediate, TimeSpan timeout) + { + connection.Write(buffer, offset, size, immediate, timeout); + } + + public virtual void Write(byte[] buffer, int offset, int size, bool immediate, TimeSpan timeout, BufferManager bufferManager) + { + connection.Write(buffer, offset, size, immediate, timeout, bufferManager); + } + + public virtual int Read(byte[] buffer, int offset, int size, TimeSpan timeout) + { + return connection.Read(buffer, offset, size, timeout); + } + + public virtual AsyncCompletionResult BeginRead(int offset, int size, TimeSpan timeout, + Action callback, object state) + { + return connection.BeginRead(offset, size, timeout, callback, state); + } + + public virtual int EndRead() + { + return connection.EndRead(); + } + } + + class PreReadConnection : DelegatingConnection + { + int asyncBytesRead; + byte[] preReadData; + int preReadOffset; + int preReadCount; + + public PreReadConnection(IConnection innerConnection, byte[] initialData, int initialOffset, int initialSize) + : base(innerConnection) + { + preReadData = initialData; + preReadOffset = initialOffset; + preReadCount = initialSize; + } + + public PreReadConnection(IConnection innerConnection, int initialOffset, int initialSize) + : base(innerConnection) + { + preReadOffset = initialOffset; + preReadCount = initialSize; + } + + public void AddPreReadData(byte[] initialData, int initialOffset, int initialSize) + { + if (preReadCount > 0) + { + byte[] tempBuffer = preReadData ?? base.Connection.AsyncReadBuffer; + preReadData = Fx.AllocateByteArray(initialSize + preReadCount); + Buffer.BlockCopy(tempBuffer, preReadOffset, preReadData, 0, preReadCount); + Buffer.BlockCopy(initialData, initialOffset, preReadData, preReadCount, initialSize); + preReadOffset = 0; + preReadCount += initialSize; + } + else + { + preReadData = initialData; + preReadOffset = initialOffset; + preReadCount = initialSize; + } + } + + public override int Read(byte[] buffer, int offset, int size, TimeSpan timeout) + { + ConnectionUtilities.ValidateBufferBounds(buffer, offset, size); + + if (preReadCount > 0) + { + int bytesToCopy = Math.Min(size, preReadCount); + Buffer.BlockCopy(base.Connection.AsyncReadBuffer, preReadOffset, buffer, offset, bytesToCopy); + preReadOffset += bytesToCopy; + preReadCount -= bytesToCopy; + return bytesToCopy; + } + + return base.Read(buffer, offset, size, timeout); + } + + public override AsyncCompletionResult BeginRead(int offset, int size, TimeSpan timeout, Action callback, object state) + { + ConnectionUtilities.ValidateBufferBounds(AsyncReadBufferSize, offset, size); + + if (preReadCount > 0) + { + int bytesToCopy = Math.Min(size, preReadCount); + if (preReadData == null) + { + if (offset != preReadOffset) + { + preReadData = Fx.AllocateByteArray(preReadCount); + Buffer.BlockCopy(base.Connection.AsyncReadBuffer, preReadOffset, preReadData, 0, preReadCount); + preReadOffset = 0; + Buffer.BlockCopy(preReadData, 0, base.Connection.AsyncReadBuffer, offset, bytesToCopy); + preReadOffset += bytesToCopy; + preReadCount -= bytesToCopy; + asyncBytesRead = bytesToCopy; + return AsyncCompletionResult.Completed; + } + + // Requested offset and preReadOffset are the same so no copy needed + preReadOffset += bytesToCopy; + preReadCount -= bytesToCopy; + asyncBytesRead = bytesToCopy; + return AsyncCompletionResult.Completed; + } + + Buffer.BlockCopy(preReadData, preReadOffset, AsyncReadBuffer, offset, bytesToCopy); + preReadOffset += bytesToCopy; + preReadCount -= bytesToCopy; + asyncBytesRead = bytesToCopy; + return AsyncCompletionResult.Completed; + } + + return base.BeginRead(offset, size, timeout, callback, state); + } + + public override int EndRead() + { + if (asyncBytesRead > 0) + { + int retValue = asyncBytesRead; + asyncBytesRead = 0; + return retValue; + } + + return base.EndRead(); + } + } + + class ConnectionStream : Stream + { + TimeSpan closeTimeout; + int readTimeout; + int writeTimeout; + IConnection connection; + bool immediate; + private static Action s_onWriteComplete = OnWriteComplete; + private static Action s_onReadComplete = OnReadComplete; + + public ConnectionStream(IConnection connection, IDefaultCommunicationTimeouts defaultTimeouts) + { + this.connection = connection; + closeTimeout = defaultTimeouts.CloseTimeout; + ReadTimeout = TimeoutHelper.ToMilliseconds(defaultTimeouts.ReceiveTimeout); + WriteTimeout = TimeoutHelper.ToMilliseconds(defaultTimeouts.SendTimeout); + immediate = true; + } + + public IConnection Connection + { + get { return connection; } + } + + public override bool CanRead + { + get { return true; } + } + + public override bool CanSeek + { + get { return false; } + } + + public override bool CanTimeout + { + get { return true; } + } + + public override bool CanWrite + { + get { return true; } + } + + public TimeSpan CloseTimeout + { + get { return closeTimeout; } + set { closeTimeout = value; } + } + + public override int ReadTimeout + { + get { return readTimeout; } + set + { + if (value < -1) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value, + SR.Format(SR.ValueMustBeInRange, -1, int.MaxValue))); + } + + readTimeout = value; + } + } + + public override int WriteTimeout + { + get { return writeTimeout; } + set + { + if (value < -1) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value, + SR.Format(SR.ValueMustBeInRange, -1, int.MaxValue))); + } + + writeTimeout = value; + } + } + + public bool Immediate + { + get { return immediate; } + set { immediate = value; } + } + + public override long Length + { + get + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.SeekNotSupported)); + } + } + + public override long Position + { + get + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.SeekNotSupported)); + } + set + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.SeekNotSupported)); + } + } + + public TraceEventType ExceptionEventType + { + get { return connection.ExceptionEventType; } + set { connection.ExceptionEventType = value; } + } + + public void Abort() + { + connection.Abort(); + } + + public override void Close() + { + connection.Close(CloseTimeout, false); + } + + public override void Flush() + { + // NOP + } + + public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) + { + return WriteAsync(buffer, offset, count).ToApm(callback, state); + } + + public override void EndWrite(IAsyncResult asyncResult) + { + asyncResult.ToApmEnd(); + } + + public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + var tcs = new TaskCompletionSource(this); + var asyncCompletionResult = connection.BeginWrite(buffer, offset, count, Immediate, + TimeoutHelper.FromMilliseconds(WriteTimeout), s_onWriteComplete, tcs); + if (asyncCompletionResult == AsyncCompletionResult.Completed) + { + connection.EndWrite(); + tcs.TrySetResult(true); + } + + return tcs.Task; + } + + private static void OnWriteComplete(object state) + { + if (state == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("state"); + } + + var tcs = state as TaskCompletionSource; + if (tcs == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("state", SR.SPS_InvalidAsyncResult); + } + + var thisPtr = tcs.Task.AsyncState as ConnectionStream; + if (thisPtr == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("state", SR.SPS_InvalidAsyncResult); + } + + try + { + thisPtr.connection.EndWrite(); + tcs.TrySetResult(true); + } + catch (Exception e) + { + tcs.TrySetException(e); + } + } + + public override void Write(byte[] buffer, int offset, int count) + { + connection.Write(buffer, offset, count, Immediate, TimeoutHelper.FromMilliseconds(WriteTimeout)); + } + + public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state) + { + Contract.Assert(false, $"APM methods shouldn't be used:{new StackFrame()}"); + return ReadAsync(buffer, offset, count, CancellationToken.None).ToApm(callback, state); + } + + public override int EndRead(IAsyncResult asyncResult) + { + return asyncResult.ToApmEnd(); + } + + public override async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + var tcs = new TaskCompletionSource(this); + AsyncCompletionResult asyncCompletionResult = connection.BeginRead(0, Math.Min(count, connection.AsyncReadBufferSize), + TimeoutHelper.FromMilliseconds(ReadTimeout), s_onReadComplete, tcs); + + if (asyncCompletionResult == AsyncCompletionResult.Completed) + { + tcs.TrySetResult(connection.EndRead()); + } + + int bytesRead = await tcs.Task; + Buffer.BlockCopy(connection.AsyncReadBuffer, 0, buffer, offset, bytesRead); + return bytesRead; + } + + private static void OnReadComplete(object state) + { + if (state == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("state"); + } + + var tcs = state as TaskCompletionSource; + if (tcs == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("state", SR.SPS_InvalidAsyncResult); + } + + var thisPtr = tcs.Task.AsyncState as ConnectionStream; + if (thisPtr == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("state", SR.SPS_InvalidAsyncResult); + } + + try + { + tcs.TrySetResult(thisPtr.connection.EndRead()); + } + catch (Exception e) + { + tcs.TrySetException(e); + } + } + + public override int Read(byte[] buffer, int offset, int count) + { + return Read(buffer, offset, count, TimeoutHelper.FromMilliseconds(ReadTimeout)); + } + + protected int Read(byte[] buffer, int offset, int count, TimeSpan timeout) + { + return connection.Read(buffer, offset, count, timeout); + } + + public override long Seek(long offset, SeekOrigin origin) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.SeekNotSupported)); + } + + + public override void SetLength(long value) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.SeekNotSupported)); + } + + public void Shutdown(TimeSpan timeout) + { + connection.Shutdown(timeout); + } + + public Task ValidateAsync(Uri uri) + { + return connection.ValidateAsync(uri); + } + } + + class StreamConnection : IConnection + { + byte[] asyncReadBuffer; + int bytesRead; + ConnectionStream innerStream; + private Action, object> onRead; + private Action onWrite; + private Task readResult; + private Task writeResult; + Action readCallback; + Action writeCallback; + Stream stream; + + public StreamConnection(Stream stream, ConnectionStream innerStream) + { + Fx.Assert(stream != null, "StreamConnection: Stream cannot be null."); + Fx.Assert(innerStream != null, "StreamConnection: Inner stream cannot be null."); + + this.stream = stream; + this.innerStream = innerStream; + + onRead = Fx.ThunkCallback, object>(OnRead); + onWrite = Fx.ThunkCallback(OnWrite); + } + + public byte[] AsyncReadBuffer + { + get + { + if (asyncReadBuffer == null) + { + lock (ThisLock) + { + if (asyncReadBuffer == null) + { + asyncReadBuffer = Fx.AllocateByteArray(innerStream.Connection.AsyncReadBufferSize); + } + } + } + + return asyncReadBuffer; + } + } + + public int AsyncReadBufferSize + { + get { return innerStream.Connection.AsyncReadBufferSize; } + } + + public Stream Stream + { + get { return stream; } + } + + public object ThisLock + { + get { return this; } + } + + public TraceEventType ExceptionEventType + { + get { return innerStream.ExceptionEventType; } + set { innerStream.ExceptionEventType = value; } + } + + public IPEndPoint RemoteIPEndPoint + { + get + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException()); + } + } + + public void Abort() + { + innerStream.Abort(); + } + + Exception ConvertIOException(IOException ioException) + { + if (ioException.InnerException is TimeoutException) + { + return new TimeoutException(ioException.InnerException.Message, ioException); + } + else if (ioException.InnerException is CommunicationObjectAbortedException) + { + return new CommunicationObjectAbortedException(ioException.InnerException.Message, ioException); + } + else if (ioException.InnerException is CommunicationException) + { + return new CommunicationException(ioException.InnerException.Message, ioException); + } + else + { + return new CommunicationException(SR.StreamError, ioException); + } + } + + public void Close(TimeSpan timeout, bool asyncAndLinger) + { + innerStream.CloseTimeout = timeout; + try + { + stream.Close(); + } + catch (IOException ioException) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(ConvertIOException(ioException)); + } + } + + public void Shutdown(TimeSpan timeout) + { + innerStream.Shutdown(timeout); + } + + public object DuplicateAndClose(int targetProcessId) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException()); + } + + public virtual object GetCoreTransport() + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException()); + } + + public Task ValidateAsync(Uri uri) + { + return innerStream.ValidateAsync(uri); + } + + public AsyncCompletionResult BeginWrite(byte[] buffer, int offset, int size, bool immediate, TimeSpan timeout, + Action callback, object state) + { + Contract.Requires(callback != null, "Cannot call BeginWrite without a callback"); + Contract.Requires(writeCallback == null, "BeginWrite cannot be called twice"); + + writeCallback = callback; + bool throwing = true; + + try + { + innerStream.Immediate = immediate; + SetWriteTimeout(timeout); + Task localTask = stream.WriteAsync(buffer, offset, size); + + if (!localTask.IsCompleted) + { + throwing = false; + localTask.ContinueWith(onWrite, state); + return AsyncCompletionResult.Queued; + } + + localTask.GetAwaiter().GetResult(); + throwing = false; + } + catch (IOException ioException) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(ConvertIOException(ioException)); + } + finally + { + if (throwing) + { + writeCallback = null; + } + } + + return AsyncCompletionResult.Completed; + } + + public void EndWrite() + { + IAsyncResult localResult = writeResult; + writeResult = null; + writeCallback = null; + + if (localResult != null) + { + try + { + stream.EndWrite(localResult); + } + catch (IOException ioException) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(ConvertIOException(ioException)); + } + } + } + + private void OnWrite(Task antecedant, object state) + { + Fx.Assert(writeResult != null, "StreamConnection: OnWrite called twice."); + writeResult = antecedant; + writeCallback(state); + } + + public void Write(byte[] buffer, int offset, int size, bool immediate, TimeSpan timeout) + { + try + { + innerStream.Immediate = immediate; + SetWriteTimeout(timeout); + stream.Write(buffer, offset, size); + } + catch (IOException ioException) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(ConvertIOException(ioException)); + } + } + + public void Write(byte[] buffer, int offset, int size, bool immediate, TimeSpan timeout, BufferManager bufferManager) + { + Write(buffer, offset, size, immediate, timeout); + bufferManager.ReturnBuffer(buffer); + } + + void SetReadTimeout(TimeSpan timeout) + { + int timeoutInMilliseconds = TimeoutHelper.ToMilliseconds(timeout); + if (stream.CanTimeout) + { + stream.ReadTimeout = timeoutInMilliseconds; + } + innerStream.ReadTimeout = timeoutInMilliseconds; + } + + void SetWriteTimeout(TimeSpan timeout) + { + int timeoutInMilliseconds = TimeoutHelper.ToMilliseconds(timeout); + if (stream.CanTimeout) + { + stream.WriteTimeout = timeoutInMilliseconds; + } + innerStream.WriteTimeout = timeoutInMilliseconds; + } + + public int Read(byte[] buffer, int offset, int size, TimeSpan timeout) + { + try + { + SetReadTimeout(timeout); + return stream.Read(buffer, offset, size); + } + catch (IOException ioException) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(ConvertIOException(ioException)); + } + } + + public AsyncCompletionResult BeginRead(int offset, int size, TimeSpan timeout, Action callback, object state) + { + ConnectionUtilities.ValidateBufferBounds(AsyncReadBufferSize, offset, size); + readCallback = callback; + + try + { + SetReadTimeout(timeout); + Task localTask = stream.ReadAsync(AsyncReadBuffer, offset, size); + + if (!localTask.IsCompleted) + { + localTask.ContinueWith(onRead, state); + return AsyncCompletionResult.Queued; + } + + bytesRead = localTask.GetAwaiter().GetResult(); + } + catch (IOException ioException) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(ConvertIOException(ioException)); + } + + return AsyncCompletionResult.Completed; + } + + public int EndRead() + { + Task localResult = readResult; + readResult = null; + + if (localResult != null) + { + try + { + bytesRead = localResult.GetAwaiter().GetResult(); + } + catch (IOException ioException) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(ConvertIOException(ioException)); + } + } + + return bytesRead; + } + + void OnRead(Task antecedant, object state) + { + Fx.Assert(readResult != null, "StreamConnection: OnRead called twice."); + readResult = antecedant; + readCallback(state); + } + } + + class ConnectionMessageProperty + { + IConnection connection; + + public ConnectionMessageProperty(IConnection connection) + { + this.connection = connection; + } + + public static string Name + { + get { return "iconnection"; } + } + + public IConnection Connection + { + get { return connection; } + } + } + + static class ConnectionUtilities + { + internal static void CloseNoThrow(IConnection connection, TimeSpan timeout) + { + bool success = false; + try + { + // TODO: Change IConnection.Close to async and switch to async here + connection.Close(timeout, false); + success = true; + } + catch (TimeoutException e) + { + DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); + } + catch (CommunicationException e) + { + DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); + } + finally + { + if (!success) + { + connection.Abort(); + } + } + } + + internal static void ValidateBufferBounds(ArraySegment buffer) + { + ValidateBufferBounds(buffer.Array, buffer.Offset, buffer.Count); + } + + internal static void ValidateBufferBounds(byte[] buffer, int offset, int size) + { + if (buffer == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("buffer"); + } + + ValidateBufferBounds(buffer.Length, offset, size); + } + + internal static void ValidateBufferBounds(int bufferSize, int offset, int size) + { + if (offset < 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("offset", offset, SR.ValueMustBeNonNegative)); + } + + if (offset > bufferSize) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("offset", offset, SR.Format( + SR.OffsetExceedsBufferSize, bufferSize))); + } + + if (size <= 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("size", size, SR.ValueMustBePositive)); + } + + int remainingBufferSpace = bufferSize - offset; + if (size > remainingBufferSpace) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("size", size, SR.Format( + SR.SizeExceedsRemainingBufferSpace, remainingBufferSpace))); + } + } + } +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Channels/ConnectionAcceptor.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/ConnectionAcceptor.cs new file mode 100644 index 000000000..3cf362892 --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/ConnectionAcceptor.cs @@ -0,0 +1,215 @@ +using System; +using System.Diagnostics; +using CoreWCF.Runtime; +using CoreWCF; +using CoreWCF.Diagnostics; +using CoreWCF.Dispatcher; +using System.Threading; +using CoreWCF.Runtime.Diagnostics; +using System.Threading.Tasks; + +namespace CoreWCF.Channels +{ + delegate void ConnectionAvailableCallback(IConnection connection, Action connectionDequeuedCallback); + delegate void ErrorCallback(Exception exception); + + class ConnectionAcceptor : IDisposable + { + int maxAccepts; + int maxPendingConnections; + int connections; + int pendingAccepts; + IConnectionListener listener; + Action> handleCompletedAcceptAsync; + Action onConnectionDequeued; + bool isDisposed; + ConnectionAvailableCallback callback; + ErrorCallback errorCallback; + AsyncLock asyncLock = new AsyncLock(); + + public ConnectionAcceptor(IConnectionListener listener, int maxAccepts, int maxPendingConnections, + ConnectionAvailableCallback callback) + : this(listener, maxAccepts, maxPendingConnections, callback, null) + { + // empty + } + + public ConnectionAcceptor(IConnectionListener listener, int maxAccepts, int maxPendingConnections, + ConnectionAvailableCallback callback, ErrorCallback errorCallback) + { + if (maxAccepts <= 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("maxAccepts", maxAccepts, + SR.Format(SR.ValueMustBePositive))); + } + + Fx.Assert(maxPendingConnections > 0, "maxPendingConnections must be positive"); + + this.listener = listener; + this.maxAccepts = maxAccepts; + this.maxPendingConnections = maxPendingConnections; + this.callback = callback; + this.errorCallback = errorCallback; + onConnectionDequeued = new Action(OnConnectionDequeued); + handleCompletedAcceptAsync = Fx.ThunkCallback>(HandleCompletedAcceptAsync); + } + + bool IsAcceptNecessary + { + get + { + return (pendingAccepts < maxAccepts) + && ((connections + pendingAccepts) < maxPendingConnections) + && !isDisposed; + } + } + + public int ConnectionCount + { + get { return connections; } + } + + AsyncLock ThisLock + { + get { return asyncLock; } + } + + async void AcceptIfNecessaryAsync(bool startAccepting) + { + if (IsAcceptNecessary) + { + using (await ThisLock.TakeLockAsync()) + { + while (IsAcceptNecessary) + { + Exception unexpectedException = null; + try + { + var acceptTask = listener.AcceptAsync(); + // Assigning task to variable to supress warning about not awaiting the task + var continuation = acceptTask.ContinueWith( + handleCompletedAcceptAsync, + CancellationToken.None, + TaskContinuationOptions.RunContinuationsAsynchronously, // don't block our accept processing loop + ActionItem.IOTaskScheduler); // Run the continuation on the IO Thread Scheduler. Protects against thread starvation + pendingAccepts++; + } + catch (CommunicationException exception) + { + DiagnosticUtility.TraceHandledException(exception, TraceEventType.Information); + } + catch (Exception exception) + { + if (Fx.IsFatal(exception)) + { + throw; + } + if (startAccepting) + { + // Since we're under a call to StartAccepting(), just throw the exception up the stack. + throw; + } + if ((errorCallback == null) && !ExceptionHandlerHelper.HandleTransportExceptionHelper(exception)) + { + throw; + } + unexpectedException = exception; + } + + if ((unexpectedException != null) && (errorCallback != null)) + { + errorCallback(unexpectedException); + } + } + } + } + } + + public void Dispose() + { + using (ThisLock.TakeLock()) + { + if (!isDisposed) + { + isDisposed = true; + listener.Dispose(); + } + } + } + + async void HandleCompletedAcceptAsync(Task antecendant) + { + IConnection connection = null; + + using (await ThisLock.TakeLockAsync()) + { + bool success = false; + Exception unexpectedException = null; + try + { + if (!isDisposed) + { + connection = await antecendant; + if (connection != null) + { + connections++; + } + } + success = true; + } + catch (CommunicationException exception) + { + DiagnosticUtility.TraceHandledException(exception, TraceEventType.Information); + } + catch (Exception exception) + { + if (Fx.IsFatal(exception)) + { + throw; + } + if ((errorCallback == null) && !ExceptionHandlerHelper.HandleTransportExceptionHelper(exception)) + { + throw; + } + unexpectedException = exception; + } + finally + { + if (!success) + { + connection = null; + } + pendingAccepts--; + } + + if ((unexpectedException != null) && (errorCallback != null)) + { + errorCallback(unexpectedException); + } + } + + AcceptIfNecessaryAsync(false); + + if (connection != null) + { + callback(connection, onConnectionDequeued); + } + } + + void OnConnectionDequeued() + { + using (ThisLock.TakeLock()) + { + connections--; + } + AcceptIfNecessaryAsync(false); + } + + public void StartAccepting() + { + listener.Listen(); + AcceptIfNecessaryAsync(true); + } + } + +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Channels/ConnectionBufferPool.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/ConnectionBufferPool.cs new file mode 100644 index 000000000..f06bd1d39 --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/ConnectionBufferPool.cs @@ -0,0 +1,71 @@ +using System; +using CoreWCF.Runtime; + +namespace CoreWCF.Channels +{ + class ConnectionBufferPool : QueuedObjectPool + { + const int SingleBatchSize = 128 * 1024; + const int MaxBatchCount = 16; + const int MaxFreeCountFactor = 4; + int bufferSize; + + public ConnectionBufferPool(int bufferSize) + { + int batchCount = ComputeBatchCount(bufferSize); + Initialize(bufferSize, batchCount, batchCount * MaxFreeCountFactor); + } + + public ConnectionBufferPool(int bufferSize, int maxFreeCount) + { + Initialize(bufferSize, ComputeBatchCount(bufferSize), maxFreeCount); + } + + void Initialize(int bufferSize, int batchCount, int maxFreeCount) + { + Fx.Assert(bufferSize >= 0, "bufferSize must be non-negative"); + Fx.Assert(batchCount > 0, "batchCount must be positive"); + Fx.Assert(maxFreeCount >= 0, "maxFreeCount must be non-negative"); + + this.bufferSize = bufferSize; + if (maxFreeCount < batchCount) + { + maxFreeCount = batchCount; + } + base.Initialize(batchCount, maxFreeCount); + } + + public int BufferSize + { + get + { + return bufferSize; + } + } + + protected override byte[] Create() + { + return Fx.AllocateByteArray(bufferSize); + } + + static int ComputeBatchCount(int bufferSize) + { + int batchCount; + if (bufferSize != 0) + { + batchCount = (SingleBatchSize + bufferSize - 1) / bufferSize; + if (batchCount > MaxBatchCount) + { + batchCount = MaxBatchCount; + } + } + else + { + // It's OK to have zero bufferSize + batchCount = MaxBatchCount; + } + return batchCount; + } + } + +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Channels/ConnectionDemuxer.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/ConnectionDemuxer.cs new file mode 100644 index 000000000..1c46e85ce --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/ConnectionDemuxer.cs @@ -0,0 +1,436 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using CoreWCF.Runtime; +using CoreWCF.Runtime.Diagnostics; +using CoreWCF; +using CoreWCF.Diagnostics; +using CoreWCF.Dispatcher; +using System.Threading; +using System.Threading.Tasks; + +namespace CoreWCF.Channels +{ + sealed class ConnectionDemuxer : IDisposable + { + ConnectionAcceptor acceptor; + + // we use this list to track readers that don't have a clear owner (so they don't get GC'ed) + List connectionReaders; + + bool isDisposed; + ConnectionModeCallback onConnectionModeKnown; + ConnectionModeCallback onCachedConnectionModeKnown; + ConnectionClosedCallback onConnectionClosed; + ServerSessionPreambleCallback onSessionPreambleKnown; + ServerSingletonPreambleCallback onSingletonPreambleKnown; + Action reuseConnectionCallback; + ServerSessionPreambleDemuxCallback serverSessionPreambleCallback; + SingletonPreambleDemuxCallback singletonPreambleCallback; + TransportSettingsCallback transportSettingsCallback; + Action pooledConnectionDequeuedCallback; + Action viaDelegate; + TimeSpan channelInitializationTimeout; + TimeSpan idleTimeout; + int maxPooledConnections; + int pooledConnectionCount; + + public ConnectionDemuxer(IConnectionListener listener, int maxAccepts, int maxPendingConnections, + TimeSpan channelInitializationTimeout, TimeSpan idleTimeout, int maxPooledConnections, + TransportSettingsCallback transportSettingsCallback, + SingletonPreambleDemuxCallback singletonPreambleCallback, + ServerSessionPreambleDemuxCallback serverSessionPreambleCallback, ErrorCallback errorCallback) + { + connectionReaders = new List(); + acceptor = + new ConnectionAcceptor(listener, maxAccepts, maxPendingConnections, OnConnectionAvailable, errorCallback); + this.channelInitializationTimeout = channelInitializationTimeout; + this.idleTimeout = idleTimeout; + this.maxPooledConnections = maxPooledConnections; + onConnectionClosed = new ConnectionClosedCallback(OnConnectionClosed); + this.transportSettingsCallback = transportSettingsCallback; + this.singletonPreambleCallback = singletonPreambleCallback; + this.serverSessionPreambleCallback = serverSessionPreambleCallback; + } + + object ThisLock + { + get { return this; } + } + + public void Dispose() + { + lock (ThisLock) + { + if (isDisposed) + return; + + isDisposed = true; + } + + for (int i = 0; i < connectionReaders.Count; i++) + { + connectionReaders[i].Dispose(); + } + connectionReaders.Clear(); + + acceptor.Dispose(); + } + + ConnectionModeReader SetupModeReader(IConnection connection, bool isCached) + { + ConnectionModeReader modeReader; + if (isCached) + { + if (onCachedConnectionModeKnown == null) + { + onCachedConnectionModeKnown = new ConnectionModeCallback(OnCachedConnectionModeKnown); + } + + modeReader = new ConnectionModeReader(connection, onCachedConnectionModeKnown, onConnectionClosed); + } + else + { + if (onConnectionModeKnown == null) + { + onConnectionModeKnown = new ConnectionModeCallback(OnConnectionModeKnown); + } + + modeReader = new ConnectionModeReader(connection, onConnectionModeKnown, onConnectionClosed); + } + + lock (ThisLock) + { + if (isDisposed) + { + modeReader.Dispose(); + return null; + } + + connectionReaders.Add(modeReader); + return modeReader; + } + } + + public void ReuseConnection(IConnection connection, TimeSpan closeTimeout) + { + connection.ExceptionEventType = TraceEventType.Information; + ConnectionModeReader modeReader = SetupModeReader(connection, true); + + if (modeReader != null) + { + if (reuseConnectionCallback == null) + { + reuseConnectionCallback = new Action(ReuseConnectionCallback); + } + + ActionItem.Schedule(reuseConnectionCallback, new ReuseConnectionState(modeReader, closeTimeout)); + } + } + + void ReuseConnectionCallback(object state) + { + ReuseConnectionState connectionState = (ReuseConnectionState)state; + bool closeReader = false; + lock (ThisLock) + { + if (pooledConnectionCount >= maxPooledConnections) + { + closeReader = true; + } + else + { + pooledConnectionCount++; + } + } + + if (closeReader) + { + connectionState.ModeReader.CloseFromPool(connectionState.CloseTimeout); + } + else + { + if (pooledConnectionDequeuedCallback == null) + { + pooledConnectionDequeuedCallback = new Action(PooledConnectionDequeuedCallback); + } + connectionState.ModeReader.StartReading(idleTimeout, pooledConnectionDequeuedCallback); + } + } + + void PooledConnectionDequeuedCallback() + { + lock (ThisLock) + { + pooledConnectionCount--; + Fx.Assert(pooledConnectionCount >= 0, "Connection Throttle should never be negative"); + } + } + + void OnConnectionAvailable(IConnection connection, Action connectionDequeuedCallback) + { + ConnectionModeReader modeReader = SetupModeReader(connection, false); + + if (modeReader != null) + { + // StartReading() will never throw non-fatal exceptions; + // it propagates all exceptions into the onConnectionModeKnown callback, + // which is where we need our robust handling + modeReader.StartReading(channelInitializationTimeout, connectionDequeuedCallback); + } + else + { + connectionDequeuedCallback(); + } + } + + void OnCachedConnectionModeKnown(ConnectionModeReader modeReader) + { + OnConnectionModeKnownCore(modeReader, true); + } + + void OnConnectionModeKnown(ConnectionModeReader modeReader) + { + OnConnectionModeKnownCore(modeReader, false); + } + + void OnConnectionModeKnownCore(ConnectionModeReader modeReader, bool isCached) + { + lock (ThisLock) + { + if (isDisposed) + return; + + connectionReaders.Remove(modeReader); + } + + bool closeReader = true; + try + { + FramingMode framingMode; + try + { + framingMode = modeReader.GetConnectionMode(); + } + catch (CommunicationException exception) + { + TraceEventType eventType = modeReader.Connection.ExceptionEventType; + DiagnosticUtility.TraceHandledException(exception, eventType); + return; + } + catch (TimeoutException exception) + { + if (!isCached) + { + exception = new TimeoutException(SR.Format(SR.ChannelInitializationTimeout, channelInitializationTimeout), exception); + ErrorBehaviorHelper.ThrowAndCatch(exception); + } + + TraceEventType eventType = modeReader.Connection.ExceptionEventType; + DiagnosticUtility.TraceHandledException(exception, eventType); + return; + } + + switch (framingMode) + { + case FramingMode.Duplex: + OnDuplexConnection(modeReader.Connection, modeReader.ConnectionDequeuedCallback, + modeReader.StreamPosition, modeReader.BufferOffset, modeReader.BufferSize, + modeReader.GetRemainingTimeout()); + break; + + case FramingMode.Singleton: + OnSingletonConnection(modeReader.Connection, modeReader.ConnectionDequeuedCallback, + modeReader.StreamPosition, modeReader.BufferOffset, modeReader.BufferSize, + modeReader.GetRemainingTimeout()); + break; + + default: + { + Exception inner = new InvalidDataException(SR.Format( + SR.FramingModeNotSupported, framingMode)); + Exception exception = new ProtocolException(inner.Message, inner); + FramingEncodingString.AddFaultString(exception, FramingEncodingString.UnsupportedModeFault); + ErrorBehaviorHelper.ThrowAndCatch(exception); + return; + } + } + + closeReader = false; + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + if (!ExceptionHandlerHelper.HandleTransportExceptionHelper(e)) + { + throw; + } + + // containment -- the reader is aborted, no need for additional containment + } + finally + { + if (closeReader) + { + modeReader.Dispose(); + } + } + } + + void OnConnectionClosed(InitialServerConnectionReader connectionReader) + { + lock (ThisLock) + { + if (isDisposed) + return; + + connectionReaders.Remove(connectionReader); + } + } + + void OnSingletonConnection(IConnection connection, Action connectionDequeuedCallback, + long streamPosition, int offset, int size, TimeSpan timeout) + { + if (onSingletonPreambleKnown == null) + { + onSingletonPreambleKnown = OnSingletonPreambleKnown; + } + ServerSingletonPreambleConnectionReader singletonPreambleReader = + new ServerSingletonPreambleConnectionReader(connection, connectionDequeuedCallback, streamPosition, offset, size, + transportSettingsCallback, onConnectionClosed, onSingletonPreambleKnown); + + lock (ThisLock) + { + if (isDisposed) + { + singletonPreambleReader.Dispose(); + return; + } + + connectionReaders.Add(singletonPreambleReader); + } + //TODO: This might block a thread. Work out if it's safe to make this method async void + // or make the caller async. + singletonPreambleReader.StartReadingAsync(viaDelegate, timeout).GetAwaiter().GetResult(); + } + + void OnSingletonPreambleKnown(ServerSingletonPreambleConnectionReader serverSingletonPreambleReader) + { + lock (ThisLock) + { + if (isDisposed) + { + return; + } + + connectionReaders.Remove(serverSingletonPreambleReader); + } + + ISingletonChannelListener singletonChannelListener = singletonPreambleCallback(serverSingletonPreambleReader); + Fx.Assert(singletonChannelListener != null, + "singletonPreambleCallback must return a listener or send a Fault/throw"); + + // transfer ownership of the connection from the preamble reader to the message handler + +#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed + CompleteSingletonPreambleAsync(serverSingletonPreambleReader, singletonChannelListener); +#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed + } + + async Task CompleteSingletonPreambleAsync(ServerSingletonPreambleConnectionReader serverSingletonPreambleReader, ISingletonChannelListener singletonChannelListener) + { + var timeoutHelper = new TimeoutHelper(singletonChannelListener.ReceiveTimeout); + IConnection upgradedConnection = await serverSingletonPreambleReader.CompletePreambleAsync(singletonChannelListener.ReceiveTimeout); + ServerSingletonConnectionReader singletonReader = new ServerSingletonConnectionReader(serverSingletonPreambleReader, upgradedConnection, this); + + //singletonReader doesn't have async version of ReceiveRequest, so just call the sync method for now. + RequestContext requestContext = await singletonReader.ReceiveRequestAsync(timeoutHelper.GetCancellationToken()); + singletonChannelListener.ReceiveRequest(requestContext, serverSingletonPreambleReader.ConnectionDequeuedCallback, true); + + } + + void OnSessionPreambleKnown(ServerSessionPreambleConnectionReader serverSessionPreambleReader) + { + lock (ThisLock) + { + if (isDisposed) + { + return; + } + + connectionReaders.Remove(serverSessionPreambleReader); + } + + TraceOnSessionPreambleKnown(serverSessionPreambleReader); + + serverSessionPreambleCallback(serverSessionPreambleReader, this); + } + + static void TraceOnSessionPreambleKnown(ServerSessionPreambleConnectionReader serverSessionPreambleReader) + { + } + + void OnDuplexConnection(IConnection connection, Action connectionDequeuedCallback, + long streamPosition, int offset, int size, TimeSpan timeout) + { + if (onSessionPreambleKnown == null) + { + onSessionPreambleKnown = OnSessionPreambleKnown; + } + ServerSessionPreambleConnectionReader sessionPreambleReader = new ServerSessionPreambleConnectionReader( + connection, connectionDequeuedCallback, streamPosition, offset, size, + transportSettingsCallback, onConnectionClosed, onSessionPreambleKnown); + lock (ThisLock) + { + if (isDisposed) + { + sessionPreambleReader.Dispose(); + return; + } + + connectionReaders.Add(sessionPreambleReader); + } + + sessionPreambleReader.StartReading(viaDelegate, timeout); + } + + public void StartDemuxing() + { + StartDemuxing(null); + } + + public void StartDemuxing(Action viaDelegate) + { + this.viaDelegate = viaDelegate; + acceptor.StartAccepting(); + } + + class ReuseConnectionState + { + ConnectionModeReader modeReader; + TimeSpan closeTimeout; + + public ReuseConnectionState(ConnectionModeReader modeReader, TimeSpan closeTimeout) + { + this.modeReader = modeReader; + this.closeTimeout = closeTimeout; + } + + public ConnectionModeReader ModeReader + { + get { return modeReader; } + } + + public TimeSpan CloseTimeout + { + get { return closeTimeout; } + } + } + } + +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Channels/ConnectionModeReader.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/ConnectionModeReader.cs new file mode 100644 index 000000000..c8701cb86 --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/ConnectionModeReader.cs @@ -0,0 +1,219 @@ +using CoreWCF.Runtime; +using CoreWCF; +using System; +using System.Diagnostics; +using System.Threading; + +namespace CoreWCF.Channels +{ + delegate void ConnectionModeCallback(ConnectionModeReader connectionModeReader); + + sealed class ConnectionModeReader : InitialServerConnectionReader + { + Exception readException; + ServerModeDecoder decoder; + byte[] buffer; + int offset; + int size; + ConnectionModeCallback callback; + static Action readCallback; + TimeoutHelper receiveTimeoutHelper; + + public ConnectionModeReader(IConnection connection, ConnectionModeCallback callback, ConnectionClosedCallback closedCallback) + : base(connection, closedCallback) + { + this.callback = callback; + } + + public int BufferOffset + { + get { return offset; } + } + + public int BufferSize + { + get { return size; } + } + + public long StreamPosition + { + get { return decoder.StreamPosition; } + } + + public TimeSpan GetRemainingTimeout() + { + return receiveTimeoutHelper.RemainingTime(); + } + + void Complete(Exception e) + { + // exception will be logged by the caller + readException = e; + Complete(); + } + + void Complete() + { + callback(this); + } + + bool ContinueReading() + { + for (;;) + { + if (size == 0) + { + if (readCallback == null) + { + readCallback = ReadCallback; + } + + if (Connection.BeginRead(0, Connection.AsyncReadBufferSize, GetRemainingTimeout(), + readCallback, this) == AsyncCompletionResult.Queued) + { + break; + } + if (!GetReadResult()) // we're at EOF, bail + { + return false; + } + } + + for (;;) + { + int bytesDecoded; + try + { + bytesDecoded = decoder.Decode(buffer, offset, size); + } + catch (CommunicationException e) + { + // see if we need to send back a framing fault + string framingFault; + if (FramingEncodingString.TryGetFaultString(e, out framingFault)) + { + byte[] drainBuffer = new byte[128]; + InitialServerConnectionReader.SendFault( + Connection, framingFault, drainBuffer, GetRemainingTimeout(), + MaxViaSize + MaxContentTypeSize); + base.Close(GetRemainingTimeout()); + } + throw; + } + + if (bytesDecoded > 0) + { + offset += bytesDecoded; + size -= bytesDecoded; + } + if (decoder.CurrentState == ServerModeDecoder.State.Done) + { + return true; + } + if (size == 0) + { + break; + } + } + } + + return false; + } + + static void ReadCallback(object state) + { + ConnectionModeReader reader = (ConnectionModeReader)state; + bool completeSelf = false; + Exception completionException = null; + try + { + if (reader.GetReadResult()) + { + completeSelf = reader.ContinueReading(); + } + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + + completeSelf = true; + completionException = e; + } + + if (completeSelf) + { + reader.Complete(completionException); + } + } + + bool GetReadResult() + { + offset = 0; + size = Connection.EndRead(); + if (size == 0) + { + if (decoder.StreamPosition == 0) // client timed out a cached connection + { + base.Close(GetRemainingTimeout()); + return false; + } + else + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(decoder.CreatePrematureEOFException()); + } + } + + // restore ExceptionEventType to Error after the initial read for cached connections + Connection.ExceptionEventType = TraceEventType.Error; + + if (buffer == null) + { + buffer = Connection.AsyncReadBuffer; + } + + return true; + } + + public FramingMode GetConnectionMode() + { + if (readException != null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelper(readException, Connection.ExceptionEventType); + } + + return decoder.Mode; + } + + public void StartReading(TimeSpan receiveTimeout, Action connectionDequeuedCallback) + { + decoder = new ServerModeDecoder(); + receiveTimeoutHelper = new TimeoutHelper(receiveTimeout); + ConnectionDequeuedCallback = connectionDequeuedCallback; + bool completeSelf = false; + Exception completionException = null; + try + { + completeSelf = ContinueReading(); + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + + completeSelf = true; + completionException = e; + } + + if (completeSelf) + { + Complete(completionException); + } + } + } + +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Channels/ConnectionOrientedTransportBindingElement.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/ConnectionOrientedTransportBindingElement.cs new file mode 100644 index 000000000..1a83ca1f4 --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/ConnectionOrientedTransportBindingElement.cs @@ -0,0 +1,313 @@ +using CoreWCF.Runtime; +using System; +using System.ComponentModel; + +namespace CoreWCF.Channels +{ + // TODO: Consider moving to primitives + public abstract class ConnectionOrientedTransportBindingElement : TransportBindingElement + { + int connectionBufferSize; + bool exposeConnectionProperty; + HostNameComparisonMode hostNameComparisonMode; + bool inheritBaseAddressSettings; + TimeSpan channelInitializationTimeout; + int maxBufferSize; + bool maxBufferSizeInitialized; + int maxPendingConnections; + TimeSpan maxOutputDelay; + int maxPendingAccepts; + TransferMode transferMode; + bool isMaxPendingConnectionsSet; + bool isMaxPendingAcceptsSet; + + internal ConnectionOrientedTransportBindingElement() + : base() + { + connectionBufferSize = ConnectionOrientedTransportDefaults.ConnectionBufferSize; + hostNameComparisonMode = ConnectionOrientedTransportDefaults.HostNameComparisonMode; + channelInitializationTimeout = ConnectionOrientedTransportDefaults.ChannelInitializationTimeout; + maxBufferSize = TransportDefaults.MaxBufferSize; + maxPendingConnections = ConnectionOrientedTransportDefaults.GetMaxPendingConnections(); + maxOutputDelay = ConnectionOrientedTransportDefaults.MaxOutputDelay; + maxPendingAccepts = ConnectionOrientedTransportDefaults.GetMaxPendingAccepts(); + transferMode = ConnectionOrientedTransportDefaults.TransferMode; + } + + internal ConnectionOrientedTransportBindingElement(ConnectionOrientedTransportBindingElement elementToBeCloned) + : base(elementToBeCloned) + { + connectionBufferSize = elementToBeCloned.connectionBufferSize; + exposeConnectionProperty = elementToBeCloned.exposeConnectionProperty; + hostNameComparisonMode = elementToBeCloned.hostNameComparisonMode; + inheritBaseAddressSettings = elementToBeCloned.InheritBaseAddressSettings; + channelInitializationTimeout = elementToBeCloned.ChannelInitializationTimeout; + maxBufferSize = elementToBeCloned.maxBufferSize; + maxBufferSizeInitialized = elementToBeCloned.maxBufferSizeInitialized; + maxPendingConnections = elementToBeCloned.maxPendingConnections; + maxOutputDelay = elementToBeCloned.maxOutputDelay; + maxPendingAccepts = elementToBeCloned.maxPendingAccepts; + transferMode = elementToBeCloned.transferMode; + isMaxPendingConnectionsSet = elementToBeCloned.isMaxPendingConnectionsSet; + isMaxPendingAcceptsSet = elementToBeCloned.isMaxPendingAcceptsSet; + } + + [DefaultValue(ConnectionOrientedTransportDefaults.ConnectionBufferSize)] + public int ConnectionBufferSize + { + get + { + return connectionBufferSize; + } + set + { + if (value < 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value, + SR.ValueMustBeNonNegative)); + } + + connectionBufferSize = value; + } + } + + [DefaultValue(ConnectionOrientedTransportDefaults.HostNameComparisonMode)] + public HostNameComparisonMode HostNameComparisonMode + { + get + { + return hostNameComparisonMode; + } + + set + { + HostNameComparisonModeHelper.Validate(value); + hostNameComparisonMode = value; + } + } + + [DefaultValue(TransportDefaults.MaxBufferSize)] + public int MaxBufferSize + { + get + { + if (maxBufferSizeInitialized || TransferMode != TransferMode.Buffered) + { + return maxBufferSize; + } + + long maxReceivedMessageSize = MaxReceivedMessageSize; + if (maxReceivedMessageSize > int.MaxValue) + { + return int.MaxValue; + } + else + { + return (int)maxReceivedMessageSize; + } + } + set + { + if (value <= 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value, + SR.ValueMustBePositive)); + } + + maxBufferSizeInitialized = true; + maxBufferSize = value; + } + } + + public int MaxPendingConnections + { + get + { + return maxPendingConnections; + } + set + { + if (value <= 0) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value, + SR.ValueMustBePositive)); + + maxPendingConnections = value; + isMaxPendingConnectionsSet = true; + } + } + + internal bool IsMaxPendingConnectionsSet + { + get { return isMaxPendingConnectionsSet; } + } + + // used by MEX to ensure that we don't conflict on base-address scoped settings + internal bool InheritBaseAddressSettings + { + get + { + return inheritBaseAddressSettings; + } + set + { + inheritBaseAddressSettings = value; + } + } + + public TimeSpan ChannelInitializationTimeout + { + get + { + return channelInitializationTimeout; + } + + set + { + if (value <= TimeSpan.Zero) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value, + SR.TimeSpanMustbeGreaterThanTimeSpanZero)); + } + + if (TimeoutHelper.IsTooLarge(value)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value, + SR.SFxTimeoutOutOfRangeTooBig)); + } + + channelInitializationTimeout = value; + } + } + + public TimeSpan MaxOutputDelay + { + get + { + return maxOutputDelay; + } + + set + { + if (value < TimeSpan.Zero) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value, + SR.SFxTimeoutOutOfRange0)); + } + + if (TimeoutHelper.IsTooLarge(value)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value, + SR.SFxTimeoutOutOfRangeTooBig)); + } + + maxOutputDelay = value; + } + } + + public int MaxPendingAccepts + { + get + { + return maxPendingAccepts; + } + + set + { + if (value <= 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value, + SR.ValueMustBePositive)); + } + + maxPendingAccepts = value; + isMaxPendingAcceptsSet = true; + } + } + + internal bool IsMaxPendingAcceptsSet + { + get { return isMaxPendingAcceptsSet; } + } + + [DefaultValue(ConnectionOrientedTransportDefaults.TransferMode)] + public TransferMode TransferMode + { + get + { + return transferMode; + } + set + { + TransferModeHelper.Validate(value); + transferMode = value; + } + } + + public override bool CanBuildChannelListener(BindingContext context) + { + if (context == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("context"); + } + + if (TransferMode == TransferMode.Buffered) + { + return (typeof(TChannel) == typeof(IDuplexSessionChannel)); + } + else + { + return (typeof(TChannel) == typeof(IReplyChannel)); + } + } + + public override T GetProperty(BindingContext context) + { + if (context == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("context"); + } + + if (typeof(T) == typeof(TransferMode)) + { + return (T)(object)TransferMode; + } + else + { + return base.GetProperty(context); + } + } + + protected override bool IsMatch(BindingElement b) + { + if (!base.IsMatch(b)) + return false; + + ConnectionOrientedTransportBindingElement connection = b as ConnectionOrientedTransportBindingElement; + if (connection == null) + return false; + + if (connectionBufferSize != connection.connectionBufferSize) + return false; + if (hostNameComparisonMode != connection.hostNameComparisonMode) + return false; + if (inheritBaseAddressSettings != connection.inheritBaseAddressSettings) + return false; + if (channelInitializationTimeout != connection.channelInitializationTimeout) + { + return false; + } + if (maxBufferSize != connection.maxBufferSize) + return false; + if (maxPendingConnections != connection.maxPendingConnections) + return false; + if (maxOutputDelay != connection.maxOutputDelay) + return false; + if (maxPendingAccepts != connection.maxPendingAccepts) + return false; + if (transferMode != connection.transferMode) + return false; + + return true; + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Channels/ConnectionOrientedTransportChannelListener.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/ConnectionOrientedTransportChannelListener.cs new file mode 100644 index 000000000..157dcebfa --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/ConnectionOrientedTransportChannelListener.cs @@ -0,0 +1,396 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace CoreWCF.Channels +{ + abstract class ConnectionOrientedTransportChannelListener + : TransportChannelListener, + IConnectionOrientedTransportFactorySettings, + IConnectionOrientedListenerSettings + { + int connectionBufferSize; + bool exposeConnectionProperty; + TimeSpan channelInitializationTimeout; + int maxBufferSize; + int maxPendingConnections; + TimeSpan maxOutputDelay; + int maxPendingAccepts; + TimeSpan idleTimeout; + int maxPooledConnections; + TransferMode transferMode; + ISecurityCapabilities securityCapabilities; + StreamUpgradeProvider upgrade; + bool ownUpgrade; + EndpointIdentity identity; + + protected ConnectionOrientedTransportChannelListener(ConnectionOrientedTransportBindingElement bindingElement, + BindingContext context) + : base(bindingElement, context, bindingElement.HostNameComparisonMode) + { + if (bindingElement.TransferMode == TransferMode.Buffered) + { + if (bindingElement.MaxReceivedMessageSize > int.MaxValue) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new ArgumentOutOfRangeException("bindingElement.MaxReceivedMessageSize", + SR.MaxReceivedMessageSizeMustBeInIntegerRange)); + } + + if (bindingElement.MaxBufferSize != bindingElement.MaxReceivedMessageSize) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("bindingElement", + SR.MaxBufferSizeMustMatchMaxReceivedMessageSize); + } + } + else + { + if (bindingElement.MaxBufferSize > bindingElement.MaxReceivedMessageSize) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("bindingElement", + SR.MaxBufferSizeMustNotExceedMaxReceivedMessageSize); + } + } + + + connectionBufferSize = bindingElement.ConnectionBufferSize; + exposeConnectionProperty = false; //bindingElement.ExposeConnectionProperty; + InheritBaseAddressSettings = false; //bindingElement.InheritBaseAddressSettings; + channelInitializationTimeout = bindingElement.ChannelInitializationTimeout; + maxBufferSize = bindingElement.MaxBufferSize; + maxPendingConnections = bindingElement.MaxPendingConnections; + maxOutputDelay = bindingElement.MaxOutputDelay; + maxPendingAccepts = bindingElement.MaxPendingAccepts; + transferMode = bindingElement.TransferMode; + + Collection upgradeBindingElements = + context.BindingParameters.FindAll(); + + if (upgradeBindingElements.Count > 1) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.MultipleStreamUpgradeProvidersInParameters)); + } + else if ((upgradeBindingElements.Count == 1) && SupportsUpgrade(upgradeBindingElements[0])) + { + upgrade = upgradeBindingElements[0].BuildServerStreamUpgradeProvider(context); + ownUpgrade = true; + context.BindingParameters.Remove(); + securityCapabilities = upgradeBindingElements[0].GetProperty(context); + } + } + + public int ConnectionBufferSize + { + get + { + return connectionBufferSize; + } + } + + public TimeSpan IdleTimeout + { + get { return idleTimeout; } + } + + public int MaxPooledConnections + { + get { return maxPooledConnections; } + } + + internal void SetIdleTimeout(TimeSpan idleTimeout) + { + this.idleTimeout = idleTimeout; + } + + internal void InitializeMaxPooledConnections() + { + maxPooledConnections = ConnectionOrientedTransportDefaults.GetMaxConnections(); + } + + internal bool ExposeConnectionProperty + { + get { return exposeConnectionProperty; } + } + + public HostNameComparisonMode HostNameComparisonMode + { + get + { + return HostNameComparisonModeInternal; + } + } + + public override T GetProperty() + { + if (typeof(T) == typeof(EndpointIdentity)) + { + return (T)(object)(identity); + } + else if (typeof(T) == typeof(ISecurityCapabilities)) + { + return (T)(object)securityCapabilities; + } + else + { + T result = base.GetProperty(); + + if (result == null && upgrade != null) + { + result = upgrade.GetProperty(); + } + + return result; + } + } + + public TimeSpan ChannelInitializationTimeout + { + get + { + return channelInitializationTimeout; + } + } + + public int MaxBufferSize + { + get + { + return maxBufferSize; + } + } + + public int MaxPendingConnections + { + get + { + return maxPendingConnections; + } + } + + public TimeSpan MaxOutputDelay + { + get + { + return maxOutputDelay; + } + } + + public int MaxPendingAccepts + { + get + { + return maxPendingAccepts; + } + } + + public StreamUpgradeProvider Upgrade + { + get + { + return upgrade; + } + } + + public TransferMode TransferMode + { + get + { + return transferMode; + } + } + + int IConnectionOrientedTransportFactorySettings.MaxBufferSize + { + get { return MaxBufferSize; } + } + + TransferMode IConnectionOrientedTransportFactorySettings.TransferMode + { + get { return TransferMode; } + } + + StreamUpgradeProvider IConnectionOrientedTransportFactorySettings.Upgrade + { + get { return Upgrade; } + } + + internal override int GetMaxBufferSize() + { + return MaxBufferSize; + } + + protected override async Task OnOpenAsync(CancellationToken token) + { + await base.OnOpenAsync(token); + StreamUpgradeProvider localUpgrade = Upgrade; + if (localUpgrade != null) + { + await localUpgrade.OpenAsync(token); + } + } + + protected override void OnOpened() + { + base.OnOpened(); + StreamSecurityUpgradeProvider security = Upgrade as StreamSecurityUpgradeProvider; + if (security != null) + { + identity = security.Identity; + } + } + + protected override void OnAbort() + { + StreamUpgradeProvider localUpgrade = GetUpgrade(); + if (localUpgrade != null) + { + localUpgrade.Abort(); + } + base.OnAbort(); + } + + protected override async Task OnCloseAsync(CancellationToken token) + { + StreamUpgradeProvider localUpgrade = GetUpgrade(); + if (localUpgrade != null) + { + await localUpgrade.CloseAsync(token); + await base.OnCloseAsync(token); + } + else + { + await base.OnCloseAsync(token); + } + } + + StreamUpgradeProvider GetUpgrade() + { + StreamUpgradeProvider result = null; + + lock (ThisLock) + { + if (ownUpgrade) + { + result = upgrade; + ownUpgrade = false; + } + } + + return result; + } + + protected override void ValidateUri(Uri uri) + { + base.ValidateUri(uri); + int maxViaSize = ConnectionOrientedTransportDefaults.MaxViaSize; + int encodedSize = Encoding.UTF8.GetByteCount(uri.AbsoluteUri); + if (encodedSize > maxViaSize) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new QuotaExceededException(SR.Format(SR.UriLengthExceedsMaxSupportedSize, uri, encodedSize, maxViaSize))); + } + } + + protected virtual bool SupportsUpgrade(StreamUpgradeBindingElement upgradeBindingElement) + { + return true; + } + + // transfers around the StreamUpgradeProvider from an ownership perspective + protected class ConnectionOrientedTransportReplyChannelAcceptor : TransportReplyChannelAcceptor + { + StreamUpgradeProvider upgrade; + + public ConnectionOrientedTransportReplyChannelAcceptor(ConnectionOrientedTransportChannelListener listener) + : base(listener) + { + upgrade = listener.GetUpgrade(); + } + + protected override ReplyChannel OnCreateChannel() + { + return new ConnectionOrientedTransportReplyChannel(ChannelManager, null); + } + + protected override void OnAbort() + { + base.OnAbort(); + if (upgrade != null && !TransferUpgrade()) + { + upgrade.Abort(); + } + } + + protected override Task OnCloseAsync(CancellationToken token) + { + if (upgrade != null && !TransferUpgrade()) + { + return upgrade.CloseAsync(token); + } + + return Task.CompletedTask; + } + + // used to decouple our channel and listener lifetimes + bool TransferUpgrade() + { + ConnectionOrientedTransportReplyChannel singletonChannel = (ConnectionOrientedTransportReplyChannel)base.GetCurrentChannel(); + if (singletonChannel == null) + { + return false; + } + else + { + return singletonChannel.TransferUpgrade(upgrade); + } + } + + // tracks StreamUpgradeProvider so that the channel can outlive the Listener + class ConnectionOrientedTransportReplyChannel : TransportReplyChannel + { + StreamUpgradeProvider upgrade; + + public ConnectionOrientedTransportReplyChannel(ChannelManagerBase channelManager, EndpointAddress localAddress) + : base(channelManager, localAddress) + { + } + + public bool TransferUpgrade(StreamUpgradeProvider upgrade) + { + lock (ThisLock) + { + if (State != CommunicationState.Opened) + { + return false; + } + + this.upgrade = upgrade; + return true; + } + } + + protected override void OnAbort() + { + if (upgrade != null) + { + upgrade.Abort(); + } + base.OnAbort(); + } + + protected override async Task OnCloseAsync(CancellationToken token) + { + if (upgrade != null) + { + await upgrade.CloseAsync(token); + } + + await base.OnCloseAsync(token); + } + } + } + } +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Channels/ConnectionOrientedTransportManager.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/ConnectionOrientedTransportManager.cs new file mode 100644 index 000000000..6f1bf23e0 --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/ConnectionOrientedTransportManager.cs @@ -0,0 +1,224 @@ +using System; +using CoreWCF; +using System.Net; +using System.Net.Sockets; +using System.Collections.Generic; + +namespace CoreWCF.Channels +{ + abstract class ConnectionOrientedTransportManager : TransportManager + where TChannelListener : ConnectionOrientedTransportChannelListener + { + UriPrefixTable addressTable; + int connectionBufferSize; + TimeSpan channelInitializationTimeout; + int maxPendingConnections; + TimeSpan maxOutputDelay; + int maxPendingAccepts; + TimeSpan idleTimeout; + int maxPooledConnections; + Action messageReceivedCallback; + + protected ConnectionOrientedTransportManager() + { + addressTable = new UriPrefixTable(); + } + + UriPrefixTable AddressTable + { + get { return addressTable; } + } + + protected TimeSpan ChannelInitializationTimeout + { + get + { + return channelInitializationTimeout; + } + } + + internal void ApplyListenerSettings(IConnectionOrientedListenerSettings listenerSettings) + { + connectionBufferSize = listenerSettings.ConnectionBufferSize; + channelInitializationTimeout = listenerSettings.ChannelInitializationTimeout; + maxPendingConnections = listenerSettings.MaxPendingConnections; + maxOutputDelay = listenerSettings.MaxOutputDelay; + maxPendingAccepts = listenerSettings.MaxPendingAccepts; + idleTimeout = listenerSettings.IdleTimeout; + maxPooledConnections = listenerSettings.MaxPooledConnections; + } + + internal int ConnectionBufferSize + { + get + { + return connectionBufferSize; + } + } + + internal int MaxPendingConnections + { + get + { + return maxPendingConnections; + } + } + + internal TimeSpan MaxOutputDelay + { + get + { + return maxOutputDelay; + } + } + + internal int MaxPendingAccepts + { + get + { + return maxPendingAccepts; + } + } + + internal TimeSpan IdleTimeout + { + get { return idleTimeout; } + } + + internal int MaxPooledConnections + { + get { return maxPooledConnections; } + } + + internal bool IsCompatible(ConnectionOrientedTransportChannelListener channelListener) + { + if (channelListener.InheritBaseAddressSettings) + return true; + + return ( + (ChannelInitializationTimeout == channelListener.ChannelInitializationTimeout) && + (ConnectionBufferSize == channelListener.ConnectionBufferSize) && + (MaxPendingConnections == channelListener.MaxPendingConnections) && + (MaxOutputDelay == channelListener.MaxOutputDelay) && + (MaxPendingAccepts == channelListener.MaxPendingAccepts) && + (idleTimeout == channelListener.IdleTimeout) && + (maxPooledConnections == channelListener.MaxPooledConnections) + ); + } + + TChannelListener GetChannelListener(Uri via) + { + TChannelListener channelListener = null; + if (AddressTable.TryLookupUri(via, HostNameComparisonMode.StrongWildcard, out channelListener)) + { + return channelListener; + } + + if (AddressTable.TryLookupUri(via, HostNameComparisonMode.Exact, out channelListener)) + { + return channelListener; + } + + AddressTable.TryLookupUri(via, HostNameComparisonMode.WeakWildcard, out channelListener); + return channelListener; + } + + internal void OnDemuxerError(Exception exception) + { + lock (ThisLock) + { + Fault(AddressTable, exception); + } + } + + internal ISingletonChannelListener OnGetSingletonMessageHandler(ServerSingletonPreambleConnectionReader serverSingletonPreambleReader) + { + Uri via = serverSingletonPreambleReader.Via; + TChannelListener channelListener = GetChannelListener(via); + + if (channelListener != null) + { + if (channelListener is IChannelListener) + { + channelListener.RaiseMessageReceived(); + return (ISingletonChannelListener)channelListener; + } + else + { + serverSingletonPreambleReader.SendFault(FramingEncodingString.UnsupportedModeFault); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new ProtocolException(SR.Format(SR.FramingModeNotSupported, FramingMode.Singleton))); + } + } + else + { + serverSingletonPreambleReader.SendFault(FramingEncodingString.EndpointNotFoundFault); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new EndpointNotFoundException(SR.Format(SR.EndpointNotFound, via))); + } + } + + internal void OnHandleServerSessionPreamble(ServerSessionPreambleConnectionReader serverSessionPreambleReader, + ConnectionDemuxer connectionDemuxer) + { + Uri via = serverSessionPreambleReader.Via; + TChannelListener channelListener = GetChannelListener(via); + + if (channelListener != null) + { + ISessionPreambleHandler sessionPreambleHandler = channelListener as ISessionPreambleHandler; + + if (sessionPreambleHandler != null && channelListener is IChannelListener) + { + sessionPreambleHandler.HandleServerSessionPreamble(serverSessionPreambleReader, connectionDemuxer); + } + else + { + serverSessionPreambleReader.SendFault(FramingEncodingString.UnsupportedModeFault); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new ProtocolException(SR.Format(SR.FramingModeNotSupported, FramingMode.Duplex))); + } + } + else + { + serverSessionPreambleReader.SendFault(FramingEncodingString.EndpointNotFoundFault); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new EndpointNotFoundException(SR.Format(SR.DuplexSessionListenerNotFound, via.ToString()))); + } + } + + internal IConnectionOrientedTransportFactorySettings OnGetTransportFactorySettings(Uri via) + { + return GetChannelListener(via); + } + + internal override void Register(TransportChannelListener channelListener) + { + AddressTable.RegisterUri(channelListener.Uri, channelListener.HostNameComparisonModeInternal, + (TChannelListener)channelListener); + + channelListener.SetMessageReceivedCallback(new Action(OnMessageReceived)); + } + + internal override void Unregister(TransportChannelListener channelListener) + { + EnsureRegistered(AddressTable, (TChannelListener)channelListener, channelListener.HostNameComparisonModeInternal); + AddressTable.UnregisterUri(channelListener.Uri, channelListener.HostNameComparisonModeInternal); + channelListener.SetMessageReceivedCallback(null); + } + + internal void SetMessageReceivedCallback(Action messageReceivedCallback) + { + this.messageReceivedCallback = messageReceivedCallback; + } + + void OnMessageReceived() + { + Action callback = messageReceivedCallback; + if (callback != null) + { + callback(); + } + } + } + +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Channels/DuplexRequestContext.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/DuplexRequestContext.cs new file mode 100644 index 000000000..22e8c41b6 --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/DuplexRequestContext.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace CoreWCF.Channels +{ + class DuplexRequestContext : RequestContextBase + { + IDuplexChannel channel; + + internal DuplexRequestContext(IDuplexChannel channel, Message request, IDefaultCommunicationTimeouts timeouts) + : base(request, timeouts.CloseTimeout, timeouts.SendTimeout) + { + this.channel = channel; + } + + protected override void OnAbort() + { + } + + protected override Task OnCloseAsync(CancellationToken token) + { + return Task.CompletedTask; + } + + protected override Task OnReplyAsync(Message message, CancellationToken token) + { + if (message != null) + { + return channel.SendAsync(message, token); + } + + return Task.CompletedTask; + } + } +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Channels/ExclusiveTcpTransportManager.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/ExclusiveTcpTransportManager.cs new file mode 100644 index 000000000..48057ff39 --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/ExclusiveTcpTransportManager.cs @@ -0,0 +1,266 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.Sockets; +using CoreWCF.Runtime; +using CoreWCF; +using System.Threading.Tasks; +using System.Threading; + +namespace CoreWCF.Channels +{ + sealed class ExclusiveTcpTransportManager : TcpTransportManager, ISocketListenerSettings + { + bool closed; + ConnectionDemuxer connectionDemuxer; + IConnectionListener connectionListener; + IPAddress ipAddress; + int listenBacklog; + Socket listenSocket; + ExclusiveTcpTransportManagerRegistration registration; + + public ExclusiveTcpTransportManager(ExclusiveTcpTransportManagerRegistration registration, + TcpChannelListener channelListener, IPAddress ipAddressAny, UriHostNameType ipHostNameType) + { + ApplyListenerSettings(channelListener); + + listenSocket = channelListener.GetListenSocket(ipHostNameType); + if (listenSocket != null) + { + ipAddress = ((IPEndPoint)listenSocket.LocalEndPoint).Address; + } + else if (channelListener.Uri.HostNameType == ipHostNameType) + { + ipAddress = IPAddress.Parse(channelListener.Uri.DnsSafeHost); + } + else + { + ipAddress = ipAddressAny; + } + + listenBacklog = channelListener.ListenBacklog; + this.registration = registration; + } + + public IPAddress IPAddress + { + get + { + return ipAddress; + } + } + + public int ListenBacklog + { + get + { + return listenBacklog; + } + } + + int ISocketListenerSettings.BufferSize + { + get { return ConnectionBufferSize; } + } + + int ISocketListenerSettings.ListenBacklog + { + get { return ListenBacklog; } + } + + internal override Task OnOpenAsync() + { + SocketConnectionListener socketListener = null; + + if (listenSocket != null) + { + socketListener = new SocketConnectionListener(listenSocket, this, false); + listenSocket = null; + } + else + { + int port = registration.ListenUri.Port; + if (port == -1) + port = TcpUri.DefaultPort; + + socketListener = new SocketConnectionListener(new IPEndPoint(ipAddress, port), this, false); + } + + connectionListener = new BufferedConnectionListener(socketListener, MaxOutputDelay, ConnectionBufferSize); + connectionDemuxer = new ConnectionDemuxer(connectionListener, + MaxPendingAccepts, MaxPendingConnections, ChannelInitializationTimeout, + IdleTimeout, MaxPooledConnections, + OnGetTransportFactorySettings, + OnGetSingletonMessageHandler, + OnHandleServerSessionPreamble, + OnDemuxerError); + + bool startedDemuxing = false; + try + { + connectionDemuxer.StartDemuxing(); + startedDemuxing = true; + } + finally + { + if (!startedDemuxing) + { + connectionDemuxer.Dispose(); + } + } + + return Task.CompletedTask; + } + + internal override Task OnCloseAsync(CancellationToken token) + { + if(token.IsCancellationRequested) + { + return Task.FromCanceled(token); + } + + Cleanup(); + return Task.CompletedTask; + } + + internal override void OnAbort() + { + Cleanup(); + base.OnAbort(); + } + + void Cleanup() + { + lock (ThisLock) + { + if (closed) + { + return; + } + + closed = true; + } + + if (connectionDemuxer != null) + { + connectionDemuxer.Dispose(); + } + + if (connectionListener != null) + { + connectionListener.Dispose(); + } + + registration.OnClose(this); + } + } + + class ExclusiveTcpTransportManagerRegistration : TransportManagerRegistration + { + int connectionBufferSize; + TimeSpan channelInitializationTimeout; + TimeSpan idleTimeout; + int maxPooledConnections; + int listenBacklog; + TimeSpan maxOutputDelay; + int maxPendingConnections; + int maxPendingAccepts; + + ExclusiveTcpTransportManager ipv4TransportManager; + ExclusiveTcpTransportManager ipv6TransportManager; + + public ExclusiveTcpTransportManagerRegistration(Uri listenUri, TcpChannelListener channelListener) + : base(listenUri, channelListener.HostNameComparisonMode) + { + connectionBufferSize = channelListener.ConnectionBufferSize; + channelInitializationTimeout = channelListener.ChannelInitializationTimeout; + listenBacklog = channelListener.ListenBacklog; + maxOutputDelay = channelListener.MaxOutputDelay; + maxPendingConnections = channelListener.MaxPendingConnections; + maxPendingAccepts = channelListener.MaxPendingAccepts; + idleTimeout = channelListener.IdleTimeout; + maxPooledConnections = channelListener.MaxPooledConnections; + } + + public void OnClose(TcpTransportManager manager) + { + if (manager == ipv4TransportManager) + { + ipv4TransportManager = null; + } + else if (manager == ipv6TransportManager) + { + ipv6TransportManager = null; + } + else + { + Fx.Assert("Unknown transport manager passed to OnClose()."); + } + + if ((ipv4TransportManager == null) && (ipv6TransportManager == null)) + { + TcpChannelListener.StaticTransportManagerTable.UnregisterUri(ListenUri, HostNameComparisonMode); + } + } + + bool IsCompatible(TcpChannelListener channelListener, bool useIPv4, bool useIPv6) + { + if (channelListener.InheritBaseAddressSettings) + return true; + + if (useIPv6) + { + if (!channelListener.IsScopeIdCompatible(HostNameComparisonMode, ListenUri)) + { + return false; + } + } + + return (/*!channelListener.PortSharingEnabled + &&*/ (useIPv4 || useIPv6) + && (channelInitializationTimeout == channelListener.ChannelInitializationTimeout) + && (idleTimeout == channelListener.IdleTimeout) + && (maxPooledConnections == channelListener.MaxPooledConnections) + && (connectionBufferSize == channelListener.ConnectionBufferSize) + && (listenBacklog == channelListener.ListenBacklog) + && (maxPendingConnections == channelListener.MaxPendingConnections) + && (maxOutputDelay == channelListener.MaxOutputDelay) + && (maxPendingAccepts == channelListener.MaxPendingAccepts)); + } + + void ProcessSelection(TcpChannelListener channelListener, IPAddress ipAddressAny, UriHostNameType ipHostNameType, + ref ExclusiveTcpTransportManager transportManager, IList result) + { + if (transportManager == null) + { + transportManager = new ExclusiveTcpTransportManager(this, channelListener, ipAddressAny, ipHostNameType); + } + result.Add(transportManager); + } + + public override IList Select(TransportChannelListener channelListener) + { + bool useIPv4 = (ListenUri.HostNameType != UriHostNameType.IPv6) && Socket.OSSupportsIPv4; + bool useIPv6 = (ListenUri.HostNameType != UriHostNameType.IPv4) && Socket.OSSupportsIPv6; + + TcpChannelListener tcpListener = (TcpChannelListener)channelListener; + if (!IsCompatible(tcpListener, useIPv4, useIPv6)) + { + return null; + } + + IList result = new List(); + if (useIPv4) + { + ProcessSelection(tcpListener, IPAddress.Any, UriHostNameType.IPv4, + ref ipv4TransportManager, result); + } + if (useIPv6) + { + ProcessSelection(tcpListener, IPAddress.IPv6Any, UriHostNameType.IPv6, + ref ipv6TransportManager, result); + } + return result; + } + } +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Channels/Framing/ConnectionContextExtensions.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/Framing/ConnectionContextExtensions.cs new file mode 100644 index 000000000..a2fda5ba1 --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/Framing/ConnectionContextExtensions.cs @@ -0,0 +1,40 @@ +using Microsoft.AspNetCore.Connections; +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoreWCF.Channels.Framing +{ + internal static class ConnectionContextExtensions + { + public static void Set(this ConnectionContext context, T value) + { + if (context.Items.ContainsKey(typeof(T))) + { + throw new ArgumentException(nameof(T)); + } + + context.Items[typeof(T)] = value; + } + + public static void Replace(this ConnectionContext context, T value) + { + if (!context.Items.ContainsKey(typeof(T))) + { + throw new ArgumentException(nameof(T)); + } + + context.Items[typeof(T)] = value; + } + + public static T Get(this ConnectionContext context) + { + if (context.Items.TryGetValue(typeof(T), out object item)) + { + if (item is T) return (T)item; + } + + return default(T); + } + } +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Channels/Framing/DuplexFramingMiddleware.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/Framing/DuplexFramingMiddleware.cs new file mode 100644 index 000000000..b21c5d296 --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/Framing/DuplexFramingMiddleware.cs @@ -0,0 +1,71 @@ +using Microsoft.AspNetCore.Connections; +using CoreWCF.Configuration; +using System.Buffers; +using System.Threading.Tasks; + +namespace CoreWCF.Channels.Framing +{ + internal class DuplexFramingMiddleware + { + private HandshakeDelegate _next; + + public DuplexFramingMiddleware(HandshakeDelegate next) + { + _next = next; + } + + public async Task OnConnectedAsync(FramingConnection connection) + { + var decoder = new ServerSessionDecoder(ConnectionOrientedTransportDefaults.MaxViaSize, ConnectionOrientedTransportDefaults.MaxContentTypeSize); + ReadOnlySequence buffer; + while (decoder.CurrentState != ServerSessionDecoder.State.PreUpgradeStart) + { + var readResult = await connection.Input.ReadAsync(); + buffer = readResult.Buffer; + + while (buffer.Length > 0) + { + int bytesDecoded; + try + { + bytesDecoded = decoder.Decode(buffer); + } + catch (CommunicationException e) + { + // see if we need to send back a framing fault + string framingFault; + if (FramingEncodingString.TryGetFaultString(e, out framingFault)) + { + // TODO: Drain the rest of the data and send a fault then close the connection + //byte[] drainBuffer = new byte[128]; + //InitialServerConnectionReader.SendFault( + // Connection, framingFault, drainBuffer, GetRemainingTimeout(), + // MaxViaSize + MaxContentTypeSize); + //base.Close(GetRemainingTimeout()); + } + throw; + } + + if (bytesDecoded > 0) + { + buffer = buffer.Slice(bytesDecoded); + } + + if (decoder.CurrentState == ServerSessionDecoder.State.PreUpgradeStart) + { + // We now know the Via address (which endpoint the client is connecting to). + // The connection now needs to be handled by the correct endpoint which can + // handle upgrades etc. + break; //exit loop + } + } + + connection.Input.AdvanceTo(buffer.Start); + } + + connection.ServerSessionDecoder = decoder; + + await _next(connection); + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Channels/Framing/FramingConnection.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/Framing/FramingConnection.cs new file mode 100644 index 000000000..110e022b4 --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/Framing/FramingConnection.cs @@ -0,0 +1,42 @@ +using Microsoft.AspNetCore.Connections; +using CoreWCF.Configuration; +using CoreWCF.Security; +using System; +using System.Buffers; +using System.Collections.Generic; +using System.IO.Pipelines; +using System.Text; +using System.Threading.Tasks; + +namespace CoreWCF.Channels.Framing +{ + public class FramingConnection + { + private ConnectionContext _context; + + public FramingConnection(ConnectionContext context) + { + _context = context; + Transport = _context.Transport; + } + + public MessageEncoderFactory MessageEncoderFactory { get; internal set; } + public StreamUpgradeAcceptor StreamUpgradeAcceptor { get; internal set; } + public ISecurityCapabilities SecurityCapabilities { get; internal set; } + public IServiceDispatcher ServiceDispatcher { get; internal set; } + public PipeReader Input => Transport.Input; + public PipeWriter Output => Transport.Output; + public IDuplexPipe Transport { get; internal set; } + public IDuplexPipe RawTransport => _context.Transport; + internal ServerSessionDecoder ServerSessionDecoder { get; set; } + public Uri Via => ServerSessionDecoder?.Via; + internal FramingMode FramingMode { get; set; } + public MessageEncoder MessageEncoder { get; internal set; } + public SecurityMessageProperty SecurityMessageProperty { get; internal set; } + public bool EOF { get; internal set; } + public Memory EnvelopeBuffer { get; internal set; } + public int EnvelopeOffset { get; internal set; } + public BufferManager BufferManager { get; internal set; } + public int EnvelopeSize { get; internal set; } + } +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Channels/Framing/FramingConnectionContext.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/Framing/FramingConnectionContext.cs new file mode 100644 index 000000000..a15bdac5a --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/Framing/FramingConnectionContext.cs @@ -0,0 +1,45 @@ +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Connections; + +namespace CoreWCF.Channels.Framing +{ + internal abstract class FramingConnectionContext + { + private const string ViaKey = "connection.Via"; + + private ConnectionContext _connection; + + protected FramingConnectionContext(ConnectionContext connection) + { + Connection = connection; + } + + protected ConnectionContext Connection { get; } + + public Uri Via + { + get + { + return GetProperty(ViaKey); + } + set + { + SetProperty(ViaKey, value); + } + } + + protected T GetProperty(string key) + { + return Connection.Items.TryGetValue(key, out object value) ? (T)value : default(T); + } + + protected void SetProperty(string key, T value) + { + Connection.Items[key] = value; + } + + internal abstract Task CompleteHandshakeAsync(); + + } +} \ No newline at end of file diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Channels/Framing/FramingDecoder.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/Framing/FramingDecoder.cs new file mode 100644 index 000000000..3589d1812 --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/Framing/FramingDecoder.cs @@ -0,0 +1,1682 @@ +using System; +using System.Globalization; +using CoreWCF; +using System.IO; +using System.Text; +using CoreWCF.Runtime; +using System.Buffers; +using System.IO.Pipelines; +using System.Threading.Tasks; + +namespace CoreWCF.Channels.Framing +{ + static class DecoderHelper + { + public static void ValidateSize(long size) + { + if (size <= 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("size", size, SR.ValueMustBePositive)); + } + } + } + + struct IntDecoder + { + int value; + short index; + bool isValueDecoded; + const int LastIndex = 4; + + public int Value + { + get + { + if (!isValueDecoded) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.FramingValueNotAvailable)); + return value; + } + } + + public bool IsValueDecoded + { + get { return isValueDecoded; } + } + + public void Reset() + { + index = 0; + value = 0; + isValueDecoded = false; + } + + public int Decode(ReadOnlySequence buffer) + { + DecoderHelper.ValidateSize(buffer.Length); + if (isValueDecoded) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.FramingValueNotAvailable)); + } + int bytesConsumed = 0; + + while (bytesConsumed < buffer.Length) + { + var data = buffer.First.Span; + int next = data[0]; + value |= (next & 0x7F) << (index * 7); + bytesConsumed++; + if (index == LastIndex && (next & 0xF8) != 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataException(SR.FramingSizeTooLarge)); + } + index++; + if ((next & 0x80) == 0) + { + isValueDecoded = true; + break; + } + buffer = buffer.Slice(buffer.GetPosition(1)); + } + return bytesConsumed; + } + } + + abstract class StringDecoder + { + int encodedSize; + byte[] encodedBytes; + int bytesNeeded; + string value; + State currentState; + IntDecoder sizeDecoder; + int sizeQuota; + int valueLengthInBytes; + + public StringDecoder(int sizeQuota) + { + this.sizeQuota = sizeQuota; + sizeDecoder = new IntDecoder(); + Reset(); + } + + public bool IsValueDecoded + { + get { return currentState == State.Done; } + } + + public string Value + { + get + { + if (currentState != State.Done) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.FramingValueNotAvailable)); + return value; + } + } + + public int Decode(ReadOnlySequence buffer) + { + DecoderHelper.ValidateSize(buffer.Length); + + int bytesConsumed; + switch (currentState) + { + case State.ReadingSize: + bytesConsumed = sizeDecoder.Decode(buffer); + if (sizeDecoder.IsValueDecoded) + { + encodedSize = sizeDecoder.Value; + if (encodedSize > sizeQuota) + { + Exception quotaExceeded = OnSizeQuotaExceeded(encodedSize); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(quotaExceeded); + } + if (encodedBytes == null || encodedBytes.Length < encodedSize) + { + encodedBytes = Fx.AllocateByteArray(encodedSize); + value = null; + } + currentState = State.ReadingBytes; + bytesNeeded = encodedSize; + } + break; + case State.ReadingBytes: + if (value != null && valueLengthInBytes == encodedSize && bytesNeeded == encodedSize && + buffer.Length >= encodedSize && CompareBuffers(encodedBytes, buffer)) + { + bytesConsumed = bytesNeeded; + OnComplete(value); + } + else + { + bytesConsumed = bytesNeeded; + if (buffer.Length < bytesNeeded) + bytesConsumed = (int)buffer.Length; + + Span span = encodedBytes; + Span slicedBytes = span.Slice(encodedSize - bytesNeeded, bytesConsumed); + var tempBuffer = buffer.Slice(0, bytesConsumed); + tempBuffer.CopyTo(slicedBytes); + bytesNeeded -= bytesConsumed; + if (bytesNeeded == 0) + { + value = Encoding.UTF8.GetString(encodedBytes, 0, encodedSize); + valueLengthInBytes = encodedSize; + OnComplete(value); + } + } + break; + default: + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataException(SR.InvalidDecoderStateMachine)); + } + + return bytesConsumed; + } + + protected virtual void OnComplete(string value) + { + currentState = State.Done; + } + + static bool CompareBuffers(byte[] buffer1, ReadOnlySequence buffer2) + { + var buff = buffer2.ToArray(); + for (int i = 0; i < buffer1.Length; i++) + { + if (buffer1[i] != buff[i]) + { + return false; + } + } + return true; + } + + protected abstract Exception OnSizeQuotaExceeded(int size); + + public void Reset() + { + currentState = State.ReadingSize; + sizeDecoder.Reset(); + } + + enum State + { + ReadingSize, + ReadingBytes, + Done, + } + } + + class ViaStringDecoder : StringDecoder + { + Uri via; + + public ViaStringDecoder(int sizeQuota) + : base(sizeQuota) + { + } + + protected override Exception OnSizeQuotaExceeded(int size) + { + Exception result = new InvalidDataException(SR.Format(SR.FramingViaTooLong, size)); + FramingEncodingString.AddFaultString(result, FramingEncodingString.ViaTooLongFault); + return result; + } + + protected override void OnComplete(string value) + { + try + { + via = new Uri(value); + base.OnComplete(value); + } + catch (UriFormatException exception) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataException(SR.Format(SR.FramingViaNotUri, value), exception)); + } + } + + public Uri ValueAsUri + { + get + { + if (!IsValueDecoded) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.FramingValueNotAvailable)); + return via; + } + } + } + + class FaultStringDecoder : StringDecoder + { + internal const int FaultSizeQuota = 256; + + public FaultStringDecoder() + : base(FaultSizeQuota) + { + } + + protected override Exception OnSizeQuotaExceeded(int size) + { + return new InvalidDataException(SR.Format(SR.FramingFaultTooLong, size)); + } + + public static Exception GetFaultException(string faultString, string via, string contentType) + { + if (faultString == FramingEncodingString.EndpointNotFoundFault) + { + return new EndpointNotFoundException(SR.Format(SR.EndpointNotFound, via)); + } + else if (faultString == FramingEncodingString.ContentTypeInvalidFault) + { + return new ProtocolException(SR.Format(SR.FramingContentTypeMismatch, contentType, via)); + } + else if (faultString == FramingEncodingString.ServiceActivationFailedFault) + { + return new ServiceActivationException(SR.Format(SR.Hosting_ServiceActivationFailed, via)); + } + else if (faultString == FramingEncodingString.ConnectionDispatchFailedFault) + { + return new CommunicationException(SR.Format(SR.Sharing_ConnectionDispatchFailed, via)); + } + else if (faultString == FramingEncodingString.EndpointUnavailableFault) + { + return new EndpointNotFoundException(SR.Format(SR.Sharing_EndpointUnavailable, via)); + } + else if (faultString == FramingEncodingString.MaxMessageSizeExceededFault) + { + Exception inner = new QuotaExceededException(SR.FramingMaxMessageSizeExceeded); + return new CommunicationException(inner.Message, inner); + } + else if (faultString == FramingEncodingString.UnsupportedModeFault) + { + return new ProtocolException(SR.Format(SR.FramingModeNotSupportedFault, via)); + } + else if (faultString == FramingEncodingString.UnsupportedVersionFault) + { + return new ProtocolException(SR.Format(SR.FramingVersionNotSupportedFault, via)); + } + else if (faultString == FramingEncodingString.ContentTypeTooLongFault) + { + Exception inner = new QuotaExceededException(SR.Format(SR.FramingContentTypeTooLongFault, contentType)); + return new CommunicationException(inner.Message, inner); + } + else if (faultString == FramingEncodingString.ViaTooLongFault) + { + Exception inner = new QuotaExceededException(SR.Format(SR.FramingViaTooLongFault, via)); + return new CommunicationException(inner.Message, inner); + } + else if (faultString == FramingEncodingString.ServerTooBusyFault) + { + return new ServerTooBusyException(SR.Format(SR.ServerTooBusy, via)); + } + else if (faultString == FramingEncodingString.UpgradeInvalidFault) + { + return new ProtocolException(SR.Format(SR.FramingUpgradeInvalid, via)); + } + else + { + return new ProtocolException(SR.Format(SR.FramingFaultUnrecognized, faultString)); + } + } + } + + class ContentTypeStringDecoder : StringDecoder + { + public ContentTypeStringDecoder(int sizeQuota) + : base(sizeQuota) + { + } + + protected override Exception OnSizeQuotaExceeded(int size) + { + Exception result = new InvalidDataException(SR.Format(SR.FramingContentTypeTooLong, size)); + FramingEncodingString.AddFaultString(result, FramingEncodingString.ContentTypeTooLongFault); + return result; + } + + public static string GetString(FramingEncodingType type) + { + switch (type) + { + case FramingEncodingType.Soap11Utf8: + return FramingEncodingString.Soap11Utf8; + case FramingEncodingType.Soap11Utf16: + return FramingEncodingString.Soap11Utf16; + case FramingEncodingType.Soap11Utf16FFFE: + return FramingEncodingString.Soap11Utf16FFFE; + case FramingEncodingType.Soap12Utf8: + return FramingEncodingString.Soap12Utf8; + case FramingEncodingType.Soap12Utf16: + return FramingEncodingString.Soap12Utf16; + case FramingEncodingType.Soap12Utf16FFFE: + return FramingEncodingString.Soap12Utf16FFFE; + case FramingEncodingType.MTOM: + return FramingEncodingString.MTOM; + case FramingEncodingType.Binary: + return FramingEncodingString.Binary; + case FramingEncodingType.BinarySession: + return FramingEncodingString.BinarySession; + default: + return "unknown" + ((int)type).ToString(CultureInfo.InvariantCulture); + } + } + } + + abstract class FramingDecoder + { + long streamPosition; + + protected FramingDecoder() + { + } + + protected FramingDecoder(long streamPosition) + { + this.streamPosition = streamPosition; + } + + protected abstract string CurrentStateAsString { get; } + + public long StreamPosition + { + get { return streamPosition; } + set { streamPosition = value; } + } + + protected void ValidateFramingMode(FramingMode mode) + { + switch (mode) + { + case FramingMode.Singleton: + case FramingMode.Duplex: + case FramingMode.Simplex: + case FramingMode.SingletonSized: + break; + default: + { + Exception exception = CreateException(new InvalidDataException(SR.Format( + SR.FramingModeNotSupported, mode.ToString())), FramingEncodingString.UnsupportedModeFault); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(exception); + } + } + } + + protected void ValidateRecordType(FramingRecordType expectedType, FramingRecordType foundType) + { + if (foundType != expectedType) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateInvalidRecordTypeException(expectedType, foundType)); + } + } + + // special validation for Preamble Ack for usability purposes (MB#39593) + protected void ValidatePreambleAck(FramingRecordType foundType) + { + if (foundType != FramingRecordType.PreambleAck) + { + Exception inner = CreateInvalidRecordTypeException(FramingRecordType.PreambleAck, foundType); + string exceptionString; + if (((byte)foundType == 'h') || ((byte)foundType == 'H')) + { + exceptionString = SR.PreambleAckIncorrectMaybeHttp; + } + else + { + exceptionString = SR.PreambleAckIncorrect; + } + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ProtocolException(exceptionString, inner)); + } + } + + Exception CreateInvalidRecordTypeException(FramingRecordType expectedType, FramingRecordType foundType) + { + return new InvalidDataException(SR.Format(SR.FramingRecordTypeMismatch, expectedType.ToString(), foundType.ToString())); + } + + protected void ValidateMajorVersion(int majorVersion) + { + if (majorVersion != FramingVersion.Major) + { + Exception exception = CreateException(new InvalidDataException(SR.Format( + SR.FramingVersionNotSupported, majorVersion)), FramingEncodingString.UnsupportedVersionFault); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(exception); + } + } + + public Exception CreatePrematureEOFException() + { + return CreateException(new InvalidDataException(SR.FramingPrematureEOF)); + } + + protected Exception CreateException(InvalidDataException innerException, string framingFault) + { + Exception result = CreateException(innerException); + FramingEncodingString.AddFaultString(result, framingFault); + return result; + } + + protected Exception CreateException(InvalidDataException innerException) + { + return new ProtocolException(SR.Format(SR.FramingError, StreamPosition, CurrentStateAsString), + innerException); + } + } + + // Pattern: + // Done + class ServerModeDecoder : FramingDecoder + { + State currentState; + int majorVersion; + int minorVersion; + FramingMode mode; + + public ServerModeDecoder() + { + Reset(); + } + + public int Decode(ReadOnlySequence buffer) + { + DecoderHelper.ValidateSize(buffer.Length); + var data = buffer.First.Span; + + try + { + int bytesConsumed; + switch (currentState) + { + case State.ReadingVersionRecord: + ValidateRecordType(FramingRecordType.Version, (FramingRecordType)data[0]); + currentState = State.ReadingMajorVersion; + bytesConsumed = 1; + break; + case State.ReadingMajorVersion: + majorVersion = data[0]; + ValidateMajorVersion(majorVersion); + currentState = State.ReadingMinorVersion; + bytesConsumed = 1; + break; + case State.ReadingMinorVersion: + minorVersion = data[0]; + currentState = State.ReadingModeRecord; + bytesConsumed = 1; + break; + case State.ReadingModeRecord: + ValidateRecordType(FramingRecordType.Mode, (FramingRecordType)data[0]); + currentState = State.ReadingModeValue; + bytesConsumed = 1; + break; + case State.ReadingModeValue: + mode = (FramingMode)data[0]; + ValidateFramingMode(mode); + currentState = State.Done; + bytesConsumed = 1; + break; + default: + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + CreateException(new InvalidDataException(SR.InvalidDecoderStateMachine))); + } + + return bytesConsumed; + } + catch (InvalidDataException e) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateException(e)); + } + } + + public void Reset() + { + currentState = State.ReadingVersionRecord; + } + + internal async Task ReadModeAsync(PipeReader inputPipe) + { + ReadOnlySequence buffer; + while (true) + { + var readResult = await inputPipe.ReadAsync(); + buffer = readResult.Buffer; + + while(buffer.Length > 0) + { + int bytesDecoded; + try + { + bytesDecoded = Decode(buffer); + } + catch (CommunicationException e) + { + // see if we need to send back a framing fault + string framingFault; + if (FramingEncodingString.TryGetFaultString(e, out framingFault)) + { + // TODO: Drain the rest of the data and send a fault then close the connection + //byte[] drainBuffer = new byte[128]; + //InitialServerConnectionReader.SendFault( + // Connection, framingFault, drainBuffer, GetRemainingTimeout(), + // MaxViaSize + MaxContentTypeSize); + //base.Close(GetRemainingTimeout()); + } + throw; + } + + if (bytesDecoded > 0) + { + buffer = buffer.Slice(bytesDecoded); + } + + if (CurrentState == State.Done) + { + inputPipe.AdvanceTo(buffer.Start); + return; + } + } + + inputPipe.AdvanceTo(buffer.End); + } + } + + public State CurrentState + { + get { return currentState; } + } + + protected override string CurrentStateAsString + { + get { return currentState.ToString(); } + } + + public FramingMode Mode + { + get + { + if (currentState != State.Done) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.FramingValueNotAvailable)); + return mode; + } + } + + public int MajorVersion + { + get + { + if (currentState != State.Done) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.FramingValueNotAvailable)); + return majorVersion; + } + } + + public int MinorVersion + { + get + { + if (currentState != State.Done) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.FramingValueNotAvailable)); + return minorVersion; + } + } + + public enum State + { + ReadingVersionRecord, + ReadingMajorVersion, + ReadingMinorVersion, + ReadingModeRecord, + ReadingModeValue, + Done, + } + } + + // Used for Duplex/Simplex + // Pattern: + // Start, + // (UpgradeRequest, upgrade-content-type)*, + // (EnvelopeStart, ReadingEnvelopeBytes*, EnvelopeEnd)*, + // End + class ServerSessionDecoder : FramingDecoder + { + ViaStringDecoder viaDecoder; + StringDecoder contentTypeDecoder; + IntDecoder sizeDecoder; + State currentState; + string contentType; + int envelopeBytesNeeded; + int envelopeSize; + string upgrade; + + public ServerSessionDecoder(int maxViaLength, int maxContentTypeLength) + { + viaDecoder = new ViaStringDecoder(maxViaLength); + contentTypeDecoder = new ContentTypeStringDecoder(maxContentTypeLength); + sizeDecoder = new IntDecoder(); + Reset(); + } + + public State CurrentState + { + get { return currentState; } + } + + protected override string CurrentStateAsString + { + get { return currentState.ToString(); } + } + + public string ContentType + { + get + { + if (currentState < State.PreUpgradeStart) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.FramingValueNotAvailable)); + return contentType; + } + } + + public Uri Via + { + get + { + if (currentState < State.ReadingContentTypeRecord) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.FramingValueNotAvailable)); + return viaDecoder.ValueAsUri; + } + } + + public void Reset() + { + currentState = State.ReadingViaRecord; + } + + public string Upgrade + { + get + { + if (currentState != State.UpgradeRequest) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.FramingValueNotAvailable)); + return upgrade; + } + } + + public int EnvelopeSize + { + get + { + if (currentState < State.EnvelopeStart) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.FramingValueNotAvailable)); + return envelopeSize; + } + } + + public int Decode(ReadOnlySequence buffer) + { + DecoderHelper.ValidateSize(buffer.Length); + var data = buffer.First.Span; + try + { + int bytesConsumed; + FramingRecordType recordType; + switch (currentState) + { + case State.ReadingViaRecord: + recordType = (FramingRecordType)data[0]; + ValidateRecordType(FramingRecordType.Via, recordType); + bytesConsumed = 1; + viaDecoder.Reset(); + currentState = State.ReadingViaString; + break; + case State.ReadingViaString: + bytesConsumed = viaDecoder.Decode(buffer); + if (viaDecoder.IsValueDecoded) + { + currentState = State.ReadingContentTypeRecord; + } + break; + case State.ReadingContentTypeRecord: + recordType = (FramingRecordType)data[0]; + if (recordType == FramingRecordType.KnownEncoding) + { + bytesConsumed = 1; + currentState = State.ReadingContentTypeByte; + } + else + { + ValidateRecordType(FramingRecordType.ExtensibleEncoding, recordType); + bytesConsumed = 1; + contentTypeDecoder.Reset(); + currentState = State.ReadingContentTypeString; + } + break; + case State.ReadingContentTypeByte: + contentType = ContentTypeStringDecoder.GetString((FramingEncodingType)data[0]); + bytesConsumed = 1; + currentState = State.PreUpgradeStart; + break; + case State.ReadingContentTypeString: + bytesConsumed = contentTypeDecoder.Decode(buffer); + if (contentTypeDecoder.IsValueDecoded) + { + currentState = State.PreUpgradeStart; + contentType = contentTypeDecoder.Value; + } + break; + case State.PreUpgradeStart: + bytesConsumed = 0; + currentState = State.ReadingUpgradeRecord; + break; + case State.ReadingUpgradeRecord: + recordType = (FramingRecordType)data[0]; + if (recordType == FramingRecordType.UpgradeRequest) + { + bytesConsumed = 1; + contentTypeDecoder.Reset(); + currentState = State.ReadingUpgradeString; + } + else + { + bytesConsumed = 0; + currentState = State.ReadingPreambleEndRecord; + } + break; + case State.ReadingUpgradeString: + bytesConsumed = contentTypeDecoder.Decode(buffer); + if (contentTypeDecoder.IsValueDecoded) + { + currentState = State.UpgradeRequest; + upgrade = contentTypeDecoder.Value; + } + break; + case State.UpgradeRequest: + bytesConsumed = 0; + currentState = State.ReadingUpgradeRecord; + break; + case State.ReadingPreambleEndRecord: + recordType = (FramingRecordType)data[0]; + ValidateRecordType(FramingRecordType.PreambleEnd, recordType); + bytesConsumed = 1; + currentState = State.Start; + break; + case State.Start: + bytesConsumed = 0; + currentState = State.ReadingEndRecord; + break; + case State.ReadingEndRecord: + recordType = (FramingRecordType)data[0]; + if (recordType == FramingRecordType.End) + { + bytesConsumed = 1; + currentState = State.End; + } + else + { + bytesConsumed = 0; + currentState = State.ReadingEnvelopeRecord; + } + break; + case State.ReadingEnvelopeRecord: + ValidateRecordType(FramingRecordType.SizedEnvelope, (FramingRecordType)data[0]); + bytesConsumed = 1; + currentState = State.ReadingEnvelopeSize; + sizeDecoder.Reset(); + break; + case State.ReadingEnvelopeSize: + bytesConsumed = sizeDecoder.Decode(buffer); + if (sizeDecoder.IsValueDecoded) + { + currentState = State.EnvelopeStart; + envelopeSize = sizeDecoder.Value; + envelopeBytesNeeded = envelopeSize; + } + break; + case State.EnvelopeStart: + bytesConsumed = 0; + currentState = State.ReadingEnvelopeBytes; + break; + case State.ReadingEnvelopeBytes: + bytesConsumed = (int)buffer.Length; + if (bytesConsumed > envelopeBytesNeeded) + bytesConsumed = envelopeBytesNeeded; + envelopeBytesNeeded -= bytesConsumed; + if (envelopeBytesNeeded == 0) + currentState = State.EnvelopeEnd; + break; + case State.EnvelopeEnd: + bytesConsumed = 0; + currentState = State.ReadingEndRecord; + break; + case State.End: + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + CreateException(new InvalidDataException(SR.FramingAtEnd))); + default: + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + CreateException(new InvalidDataException(SR.InvalidDecoderStateMachine))); + } + + return bytesConsumed; + } + catch (InvalidDataException e) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateException(e)); + } + } + + public enum State + { + ReadingViaRecord, + ReadingViaString, + ReadingContentTypeRecord, + ReadingContentTypeString, + ReadingContentTypeByte, + PreUpgradeStart, + ReadingUpgradeRecord, + ReadingUpgradeString, + UpgradeRequest, + ReadingPreambleEndRecord, + Start, + ReadingEnvelopeRecord, + ReadingEnvelopeSize, + EnvelopeStart, + ReadingEnvelopeBytes, + EnvelopeEnd, + ReadingEndRecord, + End, + } + } + + class SingletonMessageDecoder : FramingDecoder + { + IntDecoder sizeDecoder; + int chunkBytesNeeded; + int chunkSize; + State currentState; + + public SingletonMessageDecoder(long streamPosition) + : base(streamPosition) + { + sizeDecoder = new IntDecoder(); + currentState = State.ChunkStart; + } + + public void Reset() + { + currentState = State.ChunkStart; + } + + public State CurrentState + { + get { return currentState; } + } + + protected override string CurrentStateAsString + { + get { return currentState.ToString(); } + } + + public int ChunkSize + { + get + { + if (currentState < State.ChunkStart) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.FramingValueNotAvailable)); + } + + return chunkSize; + } + } + + public int Decode(ReadOnlySequence buffer) + { + DecoderHelper.ValidateSize(buffer.Length); + var data = buffer.First.Span; + try + { + int bytesConsumed; + switch (currentState) + { + case State.ReadingEnvelopeChunkSize: + bytesConsumed = sizeDecoder.Decode(buffer); + if (sizeDecoder.IsValueDecoded) + { + chunkSize = sizeDecoder.Value; + sizeDecoder.Reset(); + + if (chunkSize == 0) + { + currentState = State.EnvelopeEnd; + } + else + { + currentState = State.ChunkStart; + chunkBytesNeeded = chunkSize; + } + } + break; + case State.ChunkStart: + bytesConsumed = 0; + currentState = State.ReadingEnvelopeBytes; + break; + case State.ReadingEnvelopeBytes: + bytesConsumed = (int)buffer.Length; + if (bytesConsumed > chunkBytesNeeded) + { + bytesConsumed = chunkBytesNeeded; + } + chunkBytesNeeded -= bytesConsumed; + if (chunkBytesNeeded == 0) + { + currentState = State.ChunkEnd; + } + break; + case State.ChunkEnd: + bytesConsumed = 0; + currentState = State.ReadingEnvelopeChunkSize; + break; + case State.EnvelopeEnd: + ValidateRecordType(FramingRecordType.End, (FramingRecordType)data[0]); + bytesConsumed = 1; + currentState = State.End; + break; + case State.End: + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + CreateException(new InvalidDataException(SR.FramingAtEnd))); + + default: + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + CreateException(new InvalidDataException(SR.InvalidDecoderStateMachine))); + } + + StreamPosition += bytesConsumed; + return bytesConsumed; + } + catch (InvalidDataException e) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateException(e)); + } + } + + public enum State + { + ReadingEnvelopeChunkSize, + ChunkStart, + ReadingEnvelopeBytes, + ChunkEnd, + EnvelopeEnd, + End, + } + } + + // Pattern: + // Start, + // (UpgradeRequest, upgrade-bytes)*, + // EnvelopeStart, + class ServerSingletonDecoder : FramingDecoder + { + ViaStringDecoder viaDecoder; + ContentTypeStringDecoder contentTypeDecoder; + State currentState; + string contentType; + string upgrade; + + public ServerSingletonDecoder(int maxViaLength, int maxContentTypeLength) + { + viaDecoder = new ViaStringDecoder(maxViaLength); + contentTypeDecoder = new ContentTypeStringDecoder(maxContentTypeLength); + Reset(); + } + + public void Reset() + { + currentState = State.ReadingViaRecord; + } + + public State CurrentState + { + get { return currentState; } + } + + protected override string CurrentStateAsString + { + get { return currentState.ToString(); } + } + + public Uri Via + { + get + { + if (currentState < State.ReadingContentTypeRecord) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.FramingValueNotAvailable)); + return viaDecoder.ValueAsUri; + } + } + + public string ContentType + { + get + { + if (currentState < State.PreUpgradeStart) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.FramingValueNotAvailable)); + return contentType; + } + } + + public string Upgrade + { + get + { + if (currentState != State.UpgradeRequest) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.FramingValueNotAvailable)); + return upgrade; + } + } + + public int Decode(ReadOnlySequence buffer) + { + DecoderHelper.ValidateSize(buffer.Length); + var data = buffer.First.Span; + try + { + int bytesConsumed; + FramingRecordType recordType; + switch (currentState) + { + case State.ReadingViaRecord: + recordType = (FramingRecordType)data[0]; + ValidateRecordType(FramingRecordType.Via, recordType); + bytesConsumed = 1; + viaDecoder.Reset(); + currentState = State.ReadingViaString; + break; + case State.ReadingViaString: + bytesConsumed = viaDecoder.Decode(buffer); + if (viaDecoder.IsValueDecoded) + { + currentState = State.ReadingContentTypeRecord; + } + break; + case State.ReadingContentTypeRecord: + recordType = (FramingRecordType)data[0]; + if (recordType == FramingRecordType.KnownEncoding) + { + bytesConsumed = 1; + currentState = State.ReadingContentTypeByte; + } + else + { + ValidateRecordType(FramingRecordType.ExtensibleEncoding, recordType); + bytesConsumed = 1; + contentTypeDecoder.Reset(); + currentState = State.ReadingContentTypeString; + } + break; + case State.ReadingContentTypeByte: + contentType = ContentTypeStringDecoder.GetString((FramingEncodingType)data[0]); + bytesConsumed = 1; + currentState = State.PreUpgradeStart; + break; + case State.ReadingContentTypeString: + bytesConsumed = contentTypeDecoder.Decode(buffer); + if (contentTypeDecoder.IsValueDecoded) + { + currentState = State.PreUpgradeStart; + contentType = contentTypeDecoder.Value; + } + break; + case State.PreUpgradeStart: + bytesConsumed = 0; + currentState = State.ReadingUpgradeRecord; + break; + case State.ReadingUpgradeRecord: + recordType = (FramingRecordType)data[0]; + if (recordType == FramingRecordType.UpgradeRequest) + { + bytesConsumed = 1; + contentTypeDecoder.Reset(); + currentState = State.ReadingUpgradeString; + } + else + { + bytesConsumed = 0; + currentState = State.ReadingPreambleEndRecord; + } + break; + case State.ReadingUpgradeString: + bytesConsumed = contentTypeDecoder.Decode(buffer); + if (contentTypeDecoder.IsValueDecoded) + { + currentState = State.UpgradeRequest; + upgrade = contentTypeDecoder.Value; + } + break; + case State.UpgradeRequest: + bytesConsumed = 0; + currentState = State.ReadingUpgradeRecord; + break; + case State.ReadingPreambleEndRecord: + recordType = (FramingRecordType)data[0]; + ValidateRecordType(FramingRecordType.PreambleEnd, recordType); + bytesConsumed = 1; + currentState = State.Start; + break; + case State.Start: + bytesConsumed = 0; + currentState = State.ReadingEnvelopeRecord; + break; + case State.ReadingEnvelopeRecord: + ValidateRecordType(FramingRecordType.UnsizedEnvelope, (FramingRecordType)data[0]); + bytesConsumed = 1; + currentState = State.EnvelopeStart; + break; + case State.EnvelopeStart: + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + CreateException(new InvalidDataException(SR.FramingAtEnd))); + default: + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + CreateException(new InvalidDataException(SR.InvalidDecoderStateMachine))); + } + + StreamPosition += bytesConsumed; + return bytesConsumed; + } + catch (InvalidDataException e) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateException(e)); + } + } + + public enum State + { + ReadingViaRecord, + ReadingViaString, + ReadingContentTypeRecord, + ReadingContentTypeString, + ReadingContentTypeByte, + PreUpgradeStart, + ReadingUpgradeRecord, + ReadingUpgradeString, + UpgradeRequest, + ReadingPreambleEndRecord, + Start, + ReadingEnvelopeRecord, + EnvelopeStart, + ReadingEnvelopeChunkSize, + ChunkStart, + ReadingEnvelopeChunk, + ChunkEnd, + End, + } + } + + // Pattern: + // Start, + // EnvelopeStart, + class ServerSingletonSizedDecoder : FramingDecoder + { + ViaStringDecoder viaDecoder; + ContentTypeStringDecoder contentTypeDecoder; + State currentState; + string contentType; + + public ServerSingletonSizedDecoder(long streamPosition, int maxViaLength, int maxContentTypeLength) + : base(streamPosition) + { + viaDecoder = new ViaStringDecoder(maxViaLength); + contentTypeDecoder = new ContentTypeStringDecoder(maxContentTypeLength); + currentState = State.ReadingViaRecord; + } + + public int Decode(ReadOnlySequence buffer) + { + DecoderHelper.ValidateSize(buffer.Length); + var data = buffer.First.Span; + try + { + int bytesConsumed; + FramingRecordType recordType; + switch (currentState) + { + case State.ReadingViaRecord: + recordType = (FramingRecordType)data[0]; + ValidateRecordType(FramingRecordType.Via, recordType); + bytesConsumed = 1; + viaDecoder.Reset(); + currentState = State.ReadingViaString; + break; + case State.ReadingViaString: + bytesConsumed = viaDecoder.Decode(buffer); + if (viaDecoder.IsValueDecoded) + currentState = State.ReadingContentTypeRecord; + break; + case State.ReadingContentTypeRecord: + recordType = (FramingRecordType)data[0]; + if (recordType == FramingRecordType.KnownEncoding) + { + bytesConsumed = 1; + currentState = State.ReadingContentTypeByte; + } + else + { + ValidateRecordType(FramingRecordType.ExtensibleEncoding, recordType); + bytesConsumed = 1; + contentTypeDecoder.Reset(); + currentState = State.ReadingContentTypeString; + } + break; + case State.ReadingContentTypeByte: + contentType = ContentTypeStringDecoder.GetString((FramingEncodingType)data[0]); + bytesConsumed = 1; + currentState = State.Start; + break; + case State.ReadingContentTypeString: + bytesConsumed = contentTypeDecoder.Decode(buffer); + if (contentTypeDecoder.IsValueDecoded) + { + currentState = State.Start; + contentType = contentTypeDecoder.Value; + } + break; + case State.Start: + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + CreateException(new InvalidDataException(SR.FramingAtEnd))); + default: + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + CreateException(new InvalidDataException(SR.InvalidDecoderStateMachine))); + } + + StreamPosition += bytesConsumed; + return bytesConsumed; + } + catch (InvalidDataException e) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateException(e)); + } + } + + public void Reset(long streamPosition) + { + StreamPosition = streamPosition; + currentState = State.ReadingViaRecord; + } + + public State CurrentState + { + get { return currentState; } + } + + protected override string CurrentStateAsString + { + get { return currentState.ToString(); } + } + + public Uri Via + { + get + { + if (currentState < State.ReadingContentTypeRecord) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.FramingValueNotAvailable)); + return viaDecoder.ValueAsUri; + } + } + + public string ContentType + { + get + { + if (currentState < State.Start) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.FramingValueNotAvailable)); + return contentType; + } + } + + public enum State + { + ReadingViaRecord, + ReadingViaString, + ReadingContentTypeRecord, + ReadingContentTypeString, + ReadingContentTypeByte, + Start, + } + } + + // common set of states used on the client-side. + enum ClientFramingDecoderState + { + ReadingUpgradeRecord, + ReadingUpgradeMode, + UpgradeResponse, + ReadingAckRecord, + Start, + ReadingFault, + ReadingFaultString, + Fault, + ReadingEnvelopeRecord, + ReadingEnvelopeSize, + EnvelopeStart, + ReadingEnvelopeBytes, + EnvelopeEnd, + ReadingEndRecord, + End, + } + + abstract class ClientFramingDecoder : FramingDecoder + { + ClientFramingDecoderState currentState; + + protected ClientFramingDecoder(long streamPosition) + : base(streamPosition) + { + currentState = ClientFramingDecoderState.ReadingUpgradeRecord; + } + + public ClientFramingDecoderState CurrentState + { + get + { + return currentState; + } + + protected set + { + currentState = value; + } + } + + protected override string CurrentStateAsString + { + get { return currentState.ToString(); } + } + + public abstract string Fault + { + get; + } + + public abstract int Decode(ReadOnlySequence buffer); + } + + // Pattern: + // (UpgradeResponse, upgrade-bytes)*, (Ack | Fault), + // ((EnvelopeStart, ReadingEnvelopeBytes*, EnvelopeEnd) | Fault)*, + // End + class ClientDuplexDecoder : ClientFramingDecoder + { + IntDecoder sizeDecoder; + FaultStringDecoder faultDecoder; + int envelopeBytesNeeded; + int envelopeSize; + + public ClientDuplexDecoder(long streamPosition) + : base(streamPosition) + { + sizeDecoder = new IntDecoder(); + } + + public int EnvelopeSize + { + get + { + if (CurrentState < ClientFramingDecoderState.EnvelopeStart) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.FramingValueNotAvailable)); + return envelopeSize; + } + } + + public override string Fault + { + get + { + if (CurrentState < ClientFramingDecoderState.Fault) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.FramingValueNotAvailable)); + return faultDecoder.Value; + } + } + + public override int Decode(ReadOnlySequence buffer) + { + DecoderHelper.ValidateSize(buffer.Length); + var data = buffer.First.Span; + try + { + int bytesConsumed; + FramingRecordType recordType; + switch (CurrentState) + { + case ClientFramingDecoderState.ReadingUpgradeRecord: + recordType = (FramingRecordType)data[0]; + if (recordType == FramingRecordType.UpgradeResponse) + { + bytesConsumed = 1; + base.CurrentState = ClientFramingDecoderState.UpgradeResponse; + } + else + { + bytesConsumed = 0; + base.CurrentState = ClientFramingDecoderState.ReadingAckRecord; + } + break; + case ClientFramingDecoderState.UpgradeResponse: + bytesConsumed = 0; + base.CurrentState = ClientFramingDecoderState.ReadingUpgradeRecord; + break; + case ClientFramingDecoderState.ReadingAckRecord: + recordType = (FramingRecordType)data[0]; + if (recordType == FramingRecordType.Fault) + { + bytesConsumed = 1; + faultDecoder = new FaultStringDecoder(); + base.CurrentState = ClientFramingDecoderState.ReadingFaultString; + break; + } + ValidatePreambleAck(recordType); + bytesConsumed = 1; + base.CurrentState = ClientFramingDecoderState.Start; + break; + case ClientFramingDecoderState.Start: + bytesConsumed = 0; + base.CurrentState = ClientFramingDecoderState.ReadingEnvelopeRecord; + break; + case ClientFramingDecoderState.ReadingEnvelopeRecord: + recordType = (FramingRecordType)data[0]; + if (recordType == FramingRecordType.End) + { + bytesConsumed = 1; + base.CurrentState = ClientFramingDecoderState.End; + break; + } + else if (recordType == FramingRecordType.Fault) + { + bytesConsumed = 1; + faultDecoder = new FaultStringDecoder(); + base.CurrentState = ClientFramingDecoderState.ReadingFaultString; + break; + } + ValidateRecordType(FramingRecordType.SizedEnvelope, recordType); + bytesConsumed = 1; + base.CurrentState = ClientFramingDecoderState.ReadingEnvelopeSize; + sizeDecoder.Reset(); + break; + case ClientFramingDecoderState.ReadingEnvelopeSize: + bytesConsumed = sizeDecoder.Decode(buffer); + if (sizeDecoder.IsValueDecoded) + { + base.CurrentState = ClientFramingDecoderState.EnvelopeStart; + envelopeSize = sizeDecoder.Value; + envelopeBytesNeeded = envelopeSize; + } + break; + case ClientFramingDecoderState.EnvelopeStart: + bytesConsumed = 0; + base.CurrentState = ClientFramingDecoderState.ReadingEnvelopeBytes; + break; + case ClientFramingDecoderState.ReadingEnvelopeBytes: + bytesConsumed = (int)buffer.Length; + if (bytesConsumed > envelopeBytesNeeded) + bytesConsumed = envelopeBytesNeeded; + envelopeBytesNeeded -= bytesConsumed; + if (envelopeBytesNeeded == 0) + base.CurrentState = ClientFramingDecoderState.EnvelopeEnd; + break; + case ClientFramingDecoderState.EnvelopeEnd: + bytesConsumed = 0; + base.CurrentState = ClientFramingDecoderState.ReadingEnvelopeRecord; + break; + case ClientFramingDecoderState.ReadingFaultString: + bytesConsumed = faultDecoder.Decode(buffer); + if (faultDecoder.IsValueDecoded) + { + base.CurrentState = ClientFramingDecoderState.Fault; + } + break; + case ClientFramingDecoderState.Fault: + bytesConsumed = 0; + base.CurrentState = ClientFramingDecoderState.ReadingEndRecord; + break; + case ClientFramingDecoderState.ReadingEndRecord: + ValidateRecordType(FramingRecordType.End, (FramingRecordType)data[0]); + bytesConsumed = 1; + base.CurrentState = ClientFramingDecoderState.End; + break; + case ClientFramingDecoderState.End: + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + CreateException(new InvalidDataException(SR.FramingAtEnd))); + default: + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + CreateException(new InvalidDataException(SR.InvalidDecoderStateMachine))); + } + + StreamPosition += bytesConsumed; + return bytesConsumed; + } + catch (InvalidDataException e) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateException(e)); + } + } + } + + // Pattern: + // (UpgradeResponse, upgrade-bytes)*, (Ack | Fault), + // End + class ClientSingletonDecoder : ClientFramingDecoder + { + FaultStringDecoder faultDecoder; + + public ClientSingletonDecoder(long streamPosition) + : base(streamPosition) + { + } + + public override string Fault + { + get + { + if (CurrentState < ClientFramingDecoderState.Fault) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.FramingValueNotAvailable)); + return faultDecoder.Value; + } + } + + public override int Decode(ReadOnlySequence buffer) + { + DecoderHelper.ValidateSize(buffer.Length); + var data = buffer.First.Span; + try + { + int bytesConsumed; + FramingRecordType recordType; + switch (CurrentState) + { + case ClientFramingDecoderState.ReadingUpgradeRecord: + recordType = (FramingRecordType)data[0]; + if (recordType == FramingRecordType.UpgradeResponse) + { + bytesConsumed = 1; + base.CurrentState = ClientFramingDecoderState.UpgradeResponse; + } + else + { + bytesConsumed = 0; + base.CurrentState = ClientFramingDecoderState.ReadingAckRecord; + } + break; + case ClientFramingDecoderState.UpgradeResponse: + bytesConsumed = 0; + base.CurrentState = ClientFramingDecoderState.ReadingUpgradeRecord; + break; + case ClientFramingDecoderState.ReadingAckRecord: + recordType = (FramingRecordType)data[0]; + if (recordType == FramingRecordType.Fault) + { + bytesConsumed = 1; + faultDecoder = new FaultStringDecoder(); + base.CurrentState = ClientFramingDecoderState.ReadingFaultString; + break; + } + ValidatePreambleAck(recordType); + bytesConsumed = 1; + base.CurrentState = ClientFramingDecoderState.Start; + break; + + case ClientFramingDecoderState.Start: + bytesConsumed = 0; + base.CurrentState = ClientFramingDecoderState.ReadingEnvelopeRecord; + break; + + case ClientFramingDecoderState.ReadingEnvelopeRecord: + recordType = (FramingRecordType)data[0]; + if (recordType == FramingRecordType.End) + { + bytesConsumed = 1; + base.CurrentState = ClientFramingDecoderState.End; + break; + } + else if (recordType == FramingRecordType.Fault) + { + bytesConsumed = 0; + base.CurrentState = ClientFramingDecoderState.ReadingFault; + break; + } + ValidateRecordType(FramingRecordType.UnsizedEnvelope, recordType); + bytesConsumed = 1; + base.CurrentState = ClientFramingDecoderState.EnvelopeStart; + break; + + case ClientFramingDecoderState.EnvelopeStart: + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + CreateException(new InvalidDataException(SR.FramingAtEnd))); + + case ClientFramingDecoderState.ReadingFault: + recordType = (FramingRecordType)data[0]; + ValidateRecordType(FramingRecordType.Fault, recordType); + bytesConsumed = 1; + faultDecoder = new FaultStringDecoder(); + base.CurrentState = ClientFramingDecoderState.ReadingFaultString; + break; + case ClientFramingDecoderState.ReadingFaultString: + bytesConsumed = faultDecoder.Decode(buffer); + if (faultDecoder.IsValueDecoded) + { + base.CurrentState = ClientFramingDecoderState.Fault; + } + break; + case ClientFramingDecoderState.Fault: + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + CreateException(new InvalidDataException(SR.FramingAtEnd))); + default: + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + CreateException(new InvalidDataException(SR.InvalidDecoderStateMachine))); + } + + StreamPosition += bytesConsumed; + return bytesConsumed; + } + catch (InvalidDataException e) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateException(e)); + } + } + } +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Channels/Framing/FramingModeHandshakeMiddleware.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/Framing/FramingModeHandshakeMiddleware.cs new file mode 100644 index 000000000..c21890919 --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/Framing/FramingModeHandshakeMiddleware.cs @@ -0,0 +1,25 @@ +using Microsoft.AspNetCore.Connections; +using CoreWCF.Configuration; +using System.Threading.Tasks; + +namespace CoreWCF.Channels.Framing +{ + internal class FramingModeHandshakeMiddleware + { + private HandshakeDelegate _next; + + public FramingModeHandshakeMiddleware(HandshakeDelegate next) + { + _next = next; + } + + public async Task OnConnectedAsync(FramingConnection connection) + { + var inputPipe = connection.Input; + var modeDecoder = new ServerModeDecoder(); + await modeDecoder.ReadModeAsync(inputPipe); + connection.FramingMode = modeDecoder.Mode; + await _next(connection); + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Channels/Framing/NetMessageFramingConnectionHandler.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/Framing/NetMessageFramingConnectionHandler.cs new file mode 100644 index 000000000..c33d6f6e8 --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/Framing/NetMessageFramingConnectionHandler.cs @@ -0,0 +1,145 @@ +using Microsoft.AspNetCore.Connections; +using Microsoft.AspNetCore.Hosting.Server.Features; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using CoreWCF.Configuration; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CoreWCF.Channels.Framing +{ + public class NetMessageFramingConnectionHandler : ConnectionHandler + { + private IServiceBuilder _serviceBuilder; + private IDispatcherBuilder _dispatcherBuilder; + private HandshakeDelegate _handshake; + + public NetMessageFramingConnectionHandler(IServiceBuilder serviceBuilder, IDispatcherBuilder dispatcherBuilder, IFramingConnectionHandshakeBuilder handshakeBuilder) + { + _serviceBuilder = serviceBuilder; + _dispatcherBuilder = dispatcherBuilder; + _handshake = BuildHandshake(handshakeBuilder); + } + + private HandshakeDelegate BuildHandshake(IFramingConnectionHandshakeBuilder handshakeBuilder) + { + handshakeBuilder.UseMiddleware(); + handshakeBuilder.Map(connection => connection.FramingMode == FramingMode.Duplex, + configuration => + { + configuration.UseMiddleware(); + configuration.Use(next => async (connection) => + { + var addressTable = configuration.HandshakeServices.GetRequiredService>(); + var serviceHandshake = GetServiceHandshakeDelegate(addressTable, connection.Via); + await serviceHandshake(connection); + await next(connection); + }); + configuration.UseMiddleware(); + configuration.UseMiddleware(); + }); + handshakeBuilder.Map(connection => connection.FramingMode == FramingMode.Singleton, + configuration => + { + configuration.UseMiddleware(); + }); + return handshakeBuilder.Build(); + } + + internal static UriPrefixTable BuildAddressTable(IServiceProvider services) + { + var serviceBuilder = services.GetRequiredService(); + var dispatcherBuilder = services.GetRequiredService(); + var addressTable = new UriPrefixTable(); + foreach (var serviceType in serviceBuilder.Services) + { + var dispatchers = dispatcherBuilder.BuildDispatchers(serviceType); + foreach (var dispatcher in dispatchers) + { + if (dispatcher.BaseAddress == null) + { + // TODO: Should we throw? Ignore? + continue; + } + + // TODO: Limit to specifically TcpTransportBindingElement if net.tcp etc + var be = dispatcher.Binding.CreateBindingElements(); + var cotbe = be.Find(); + if (cotbe == null) + { + // TODO: Should we throw? Ignore? + continue; + } + + var handshake = BuildHandshakeDelegateForDispatcher(dispatcher); + addressTable.RegisterUri(dispatcher.BaseAddress, cotbe.HostNameComparisonMode, handshake); + } + } + + return addressTable; + } + + private static HandshakeDelegate BuildHandshakeDelegateForDispatcher(IServiceDispatcher dispatcher) + { + var be = dispatcher.Binding.CreateBindingElements(); + var mebe = be.Find(); + MessageEncoderFactory mefact = mebe.CreateMessageEncoderFactory(); + var tbe = be.Find(); + int maxReceivedMessageSize = (int)Math.Min(tbe.MaxReceivedMessageSize, int.MaxValue); + var bufferManager = BufferManager.CreateBufferManager(tbe.MaxBufferPoolSize, maxReceivedMessageSize); + + var upgradeBindingElements = (from element in be where element is StreamUpgradeBindingElement select element).Cast().ToList(); + StreamUpgradeProvider streamUpgradeProvider = null; + ISecurityCapabilities securityCapabilities = null; + if (upgradeBindingElements.Count > 1) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.MultipleStreamUpgradeProvidersInParameters)); + } + // TODO: Limit NamedPipes to prevent it using SslStreamSecurityUpgradeProvider + else if ((upgradeBindingElements.Count == 1) /*&& this.SupportsUpgrade(upgradeBindingElements[0])*/) + { + var bindingContext = new BindingContext(new CustomBinding(dispatcher.Binding), new BindingParameterCollection()); + streamUpgradeProvider = upgradeBindingElements[0].BuildServerStreamUpgradeProvider(bindingContext); + streamUpgradeProvider.OpenAsync().GetAwaiter().GetResult(); + securityCapabilities = upgradeBindingElements[0].GetProperty(bindingContext); + } + return (connection) => + { + connection.MessageEncoderFactory = mefact; + connection.StreamUpgradeAcceptor = streamUpgradeProvider?.CreateUpgradeAcceptor(); + connection.SecurityCapabilities = securityCapabilities; + connection.ServiceDispatcher = dispatcher; + connection.BufferManager = bufferManager; + return Task.CompletedTask; + }; + } + + internal UriPrefixTable AddressTable { get; } = new UriPrefixTable(); + + internal static HandshakeDelegate GetServiceHandshakeDelegate(UriPrefixTable addressTable, Uri via) + { + HandshakeDelegate handshake = null; + if (addressTable.TryLookupUri(via, HostNameComparisonMode.StrongWildcard, out handshake)) + { + return handshake; + } + + if (addressTable.TryLookupUri(via, HostNameComparisonMode.Exact, out handshake)) + { + return handshake; + } + + addressTable.TryLookupUri(via, HostNameComparisonMode.WeakWildcard, out handshake); + return handshake; + } + + public override Task OnConnectedAsync(ConnectionContext context) + { + var connection = new FramingConnection(context); + return _handshake(connection); + } + } +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Channels/Framing/ServerFramingDuplexSessionChannel.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/Framing/ServerFramingDuplexSessionChannel.cs new file mode 100644 index 000000000..f77592f0c --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/Framing/ServerFramingDuplexSessionChannel.cs @@ -0,0 +1,803 @@ +using Microsoft.Extensions.DependencyInjection; +using CoreWCF.Runtime; +using CoreWCF.Security; +using System; +using System.Security.Authentication.ExtendedProtection; +using System.Threading; +using System.Threading.Tasks; +using System.Xml; + +namespace CoreWCF.Channels.Framing +{ + internal class ServerFramingDuplexSessionChannel : FramingDuplexSessionChannel + { + StreamUpgradeAcceptor upgradeAcceptor; + private IServiceProvider _serviceProvider; + IStreamUpgradeChannelBindingProvider channelBindingProvider; + + public ServerFramingDuplexSessionChannel(FramingConnection connection, ITransportFactorySettings settings, + bool exposeConnectionProperty, IServiceProvider serviceProvider) + : base(connection, settings, exposeConnectionProperty) + { + Connection = connection; + upgradeAcceptor = connection.StreamUpgradeAcceptor; + _serviceProvider = serviceProvider; + //if (upgradeAcceptor != null) + //{ + // this.channelBindingProvider = upgrade.GetProperty(); + // this.upgradeAcceptor = upgrade.CreateUpgradeAcceptor(); + //} + } + + protected override void ReturnConnectionIfNecessary(bool abort, CancellationToken token) + { + // TODO: Put connection back into the beginning of the middleware stack + // IConnection localConnection = null; + // if (this.sessionReader != null) + // { + // lock (ThisLock) + // { + // localConnection = this.sessionReader.GetRawConnection(); + // } + // } + + // if (localConnection != null) + // { + // if (abort) + // { + // localConnection.Abort(); + // } + // else + // { + // this.connectionDemuxer.ReuseConnection(localConnection, timeout); + // } + // this.connectionDemuxer = null; + // } + } + + public override T GetProperty() + { + if (typeof(T) == typeof(IChannelBindingProvider)) + { + return (T)(object)channelBindingProvider; + } + + T service = _serviceProvider.GetService(); + if (service != null) + { + return service; + } + + return base.GetProperty(); + } + + protected override Task OnOpenAsync(CancellationToken token) + { + return Task.CompletedTask; // NOOP + } + + } + + internal abstract class FramingDuplexSessionChannel : TransportDuplexSessionChannel + { + bool exposeConnectionProperty; + + private FramingDuplexSessionChannel(ITransportFactorySettings settings, + EndpointAddress localAddress, Uri localVia, EndpointAddress remoteAddresss, Uri via, bool exposeConnectionProperty) + : base(settings, localAddress, localVia, remoteAddresss, via) + { + this.exposeConnectionProperty = exposeConnectionProperty; + } + + protected FramingDuplexSessionChannel(FramingConnection connection, ITransportFactorySettings settings, bool exposeConnectionProperty) + : this(settings, new EndpointAddress(connection.ServiceDispatcher.BaseAddress), connection.Via, + EndpointAddress.AnonymousAddress, connection.MessageEncoder.MessageVersion.Addressing.AnonymousUri(), exposeConnectionProperty) + { + Session = FramingConnectionDuplexSession.CreateSession(this, connection.StreamUpgradeAcceptor); + } + + protected FramingConnection Connection { get; set; } + + protected override bool IsStreamedOutput + { + get { return false; } + } + + protected override async Task CloseOutputSessionCoreAsync(CancellationToken token) + { + var timeout = TimeoutHelper.GetOriginalTimeout(token); + await Connection.Output.WriteAsync(SessionEncoder.EndBytes, token); + await Connection.Output.FlushAsync(); + } + + protected override Task CompleteCloseAsync(CancellationToken token) + { + ReturnConnectionIfNecessary(false, token); + return Task.CompletedTask; + } + + protected override async Task OnSendCoreAsync(Message message, CancellationToken token) + { + bool allowOutputBatching; + ArraySegment messageData; + allowOutputBatching = message.Properties.AllowOutputBatching; + messageData = EncodeMessage(message); + await Connection.Output.WriteAsync(messageData, token); + if (!allowOutputBatching) + { + await Connection.Output.FlushAsync(); + } + } + + protected override async Task StartWritingBufferedMessageAsync(Message message, ArraySegment messageData, bool allowOutputBatching, CancellationToken token) + { + await Connection.Output.WriteAsync(messageData, token); + if (!allowOutputBatching) + { + await Connection.Output.FlushAsync(); + } + } + + protected override async Task CloseOutputAsync(CancellationToken token) + { + await Connection.Output.WriteAsync(SessionEncoder.EndBytes, token); + await Connection.Output.FlushAsync(); + } + + protected override Task StartWritingStreamedMessageAsync(Message message, CancellationToken token) + { + Fx.Assert(false, "Streamed output should never be called in this channel."); + return Task.FromException(Fx.Exception.AsError(new InvalidOperationException())); + } + + protected override ArraySegment EncodeMessage(Message message) + { + ArraySegment messageData = MessageEncoder.WriteMessage(message, + int.MaxValue, BufferManager, SessionEncoder.MaxMessageFrameSize); + + messageData = SessionEncoder.EncodeMessageFrame(messageData); + + return messageData; + } + + class FramingConnectionDuplexSession : ConnectionDuplexSession + { + + FramingConnectionDuplexSession(FramingDuplexSessionChannel channel) + : base(channel) + { + } + + public static FramingConnectionDuplexSession CreateSession(FramingDuplexSessionChannel channel, + StreamUpgradeAcceptor upgradeAcceptor) + { + StreamSecurityUpgradeAcceptor security = upgradeAcceptor as StreamSecurityUpgradeAcceptor; + if (security == null) + { + return new FramingConnectionDuplexSession(channel); + } + else + { + return new SecureConnectionDuplexSession(channel); + } + } + + class SecureConnectionDuplexSession : FramingConnectionDuplexSession, ISecuritySession + { + EndpointIdentity remoteIdentity; + + public SecureConnectionDuplexSession(FramingDuplexSessionChannel channel) + : base(channel) + { + // empty + } + + EndpointIdentity ISecuritySession.RemoteIdentity + { + get + { + if (remoteIdentity == null) + { + SecurityMessageProperty security = Channel.RemoteSecurity; + if (security != null && security.ServiceSecurityContext != null && + security.ServiceSecurityContext.IdentityClaim != null && + security.ServiceSecurityContext.PrimaryIdentity != null) + { + remoteIdentity = EndpointIdentity.CreateIdentity( + security.ServiceSecurityContext.IdentityClaim); + } + } + + return remoteIdentity; + } + } + } + } + } + + internal abstract class TransportDuplexSessionChannel : TransportOutputChannel, IDuplexSessionChannel + { + IDuplexSession _duplexSession; + bool _isInputSessionClosed; + bool _isOutputSessionClosed; + SynchronizedMessageSource _messageSource; + EndpointAddress _localAddress; + ChannelBinding _channelBindingToken; + + protected TransportDuplexSessionChannel( + ITransportFactorySettings settings, + EndpointAddress localAddress, + Uri localVia, + EndpointAddress remoteAddresss, + Uri via) + : base(settings, remoteAddresss, via, settings.ManualAddressing, settings.MessageVersion) + { + LocalAddress = localAddress; + LocalVia = localVia; + BufferManager = settings.BufferManager; + MessageEncoder = settings.MessageEncoderFactory.CreateSessionEncoder(); + Session = new ConnectionDuplexSession(this); + } + + public EndpointAddress LocalAddress { get; } + + public SecurityMessageProperty RemoteSecurity { get; protected set; } + + public IDuplexSession Session { get; protected set; } + + public SemaphoreSlim SendLock { get; } = new SemaphoreSlim(1); + + protected ChannelBinding ChannelBinding + { + get + { + return _channelBindingToken; + } + } + + protected BufferManager BufferManager { get; } + + protected Uri LocalVia { get; } + + protected MessageEncoder MessageEncoder { get; set; } + + protected abstract bool IsStreamedOutput { get; } + + public Task ReceiveAsync() + { + throw new NotImplementedException(); + } + + public Task ReceiveAsync(CancellationToken token) + { + throw new NotImplementedException(); + } + + public Task> TryReceiveAsync(CancellationToken token) + { + throw new NotImplementedException(); + } + + public Task WaitForMessageAsync(CancellationToken token) + { + throw new NotImplementedException(); + } + + protected void SetChannelBinding(ChannelBinding channelBinding) + { + Fx.Assert(_channelBindingToken == null, "ChannelBinding token can only be set once."); + _channelBindingToken = channelBinding; + } + + protected async Task CloseOutputSessionAsync(CancellationToken token) + { + ThrowIfNotOpened(); + ThrowIfFaulted(); + try + { + await SendLock.WaitAsync(token); + } + catch (OperationCanceledException) + { + // TODO: Fix the timeout value reported + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new TimeoutException( + SR.Format(SR.CloseTimedOut, TimeSpan.Zero), + TimeoutHelper.CreateEnterTimedOutException(TimeSpan.Zero))); + } + + try + { + // check again in case the previous send faulted while we were waiting for the lock + ThrowIfFaulted(); + + // we're synchronized by sendLock here + if (_isOutputSessionClosed) + { + return; + } + + _isOutputSessionClosed = true; + bool shouldFault = true; + try + { + await CloseOutputSessionCoreAsync(token); + OnOutputSessionClosed(token); + shouldFault = false; + } + finally + { + if (shouldFault) + { + Fault(); + } + } + } + finally + { + SendLock.Release(); + } + } + + protected abstract Task CloseOutputSessionCoreAsync(CancellationToken token); + + // used to return cached connection to the pool/reader pool + protected abstract void ReturnConnectionIfNecessary(bool abort, CancellationToken token); + + protected override void OnAbort() + { + ReturnConnectionIfNecessary(true, CancellationToken.None); + } + + protected override void OnFaulted() + { + base.OnFaulted(); + ReturnConnectionIfNecessary(true, CancellationToken.None); + } + + protected override async Task OnCloseAsync(CancellationToken token) + { + await CloseOutputSessionAsync(token); + + // close input session if necessary + if (!_isInputSessionClosed) + { + // TODO: Come up with some way to know when the input is closed. Maybe register something on the connection transport or have a Task which gets completed on close + //await EnsureInputClosedAsync(token); + OnInputSessionClosed(); + } + + await CompleteCloseAsync(token); + } + + protected override void OnClosed() + { + base.OnClosed(); + + // clean up the CBT after transitioning to the closed state + ChannelBindingUtility.Dispose(ref _channelBindingToken); + } + + protected void ApplyChannelBinding(Message message) + { + ChannelBindingUtility.TryAddToMessage(_channelBindingToken, message, false); + } + + protected abstract Task StartWritingBufferedMessageAsync(Message message, ArraySegment messageData, bool allowOutputBatching, CancellationToken token); + + protected abstract Task CloseOutputAsync(CancellationToken token); + + protected abstract ArraySegment EncodeMessage(Message message); + + protected abstract Task OnSendCoreAsync(Message message, CancellationToken token); + + protected abstract Task StartWritingStreamedMessageAsync(Message message, CancellationToken token); + + protected override async Task OnSendAsync(Message message, CancellationToken token) + { + ThrowIfDisposedOrNotOpen(); + + try + { + await SendLock.WaitAsync(token); + } + catch (OperationCanceledException) + { + // TODO: Fix the timeout value reported + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new TimeoutException( + SR.Format(SR.SendToViaTimedOut, TimeSpan.Zero), + TimeoutHelper.CreateEnterTimedOutException(TimeSpan.Zero))); + } + + try + { + // check again in case the previous send faulted while we were waiting for the lock + ThrowIfDisposedOrNotOpen(); + ThrowIfOutputSessionClosed(); + + bool success = false; + try + { + ApplyChannelBinding(message); + + await OnSendCoreAsync(message, token); + success = true; + } + finally + { + if (!success) + { + Fault(); + } + } + } + finally + { + SendLock.Release(); + } + } + + // cleanup after the framing handshake has completed + protected abstract Task CompleteCloseAsync(CancellationToken token); + + void ThrowIfOutputSessionClosed() + { + if (_isOutputSessionClosed) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.SendCannotBeCalledAfterCloseOutputSession)); + } + } + + void OnInputSessionClosed() + { + lock (ThisLock) + { + if (_isInputSessionClosed) + { + return; + } + + _isInputSessionClosed = true; + } + } + + void OnOutputSessionClosed(CancellationToken token) + { + bool releaseConnection = false; + lock (ThisLock) + { + if (_isInputSessionClosed) + { + // we're all done, release the connection + releaseConnection = true; + } + } + + if (releaseConnection) + { + ReturnConnectionIfNecessary(false, token); + } + } + + internal void ThrowIfFaulted() + { + ThrowPending(); + + switch (State) + { + case CommunicationState.Created: + break; + + case CommunicationState.Opening: + break; + + case CommunicationState.Opened: + break; + + case CommunicationState.Closing: + break; + + case CommunicationState.Closed: + break; + + case CommunicationState.Faulted: + throw TraceUtility.ThrowHelperError(CreateFaultedException(), Guid.Empty, this); + + default: + throw Fx.AssertAndThrow("ThrowIfFaulted: Unknown CommunicationObject.state"); + } + } + + internal Exception CreateFaultedException() + { + string message = SR.Format(SR.CommunicationObjectFaulted1, GetCommunicationObjectType().ToString()); + return new CommunicationObjectFaultedException(message); + } + + internal class ConnectionDuplexSession : IDuplexSession + { + static UriGenerator _uriGenerator; + string _id; + + public ConnectionDuplexSession(TransportDuplexSessionChannel channel) + : base() + { + Channel = channel; + } + + public string Id + { + get + { + if (_id == null) + { + lock (Channel) + { + if (_id == null) + { + _id = UriGenerator.Next(); + } + } + } + + return _id; + } + } + + public TransportDuplexSessionChannel Channel { get; } + + static UriGenerator UriGenerator + { + get + { + if (_uriGenerator == null) + { + _uriGenerator = new UriGenerator(); + } + + return _uriGenerator; + } + } + + public Task CloseOutputSessionAsync() + { + var timeoutHelper = new TimeoutHelper(Channel.DefaultCloseTimeout); + return CloseOutputSessionAsync(timeoutHelper.GetCancellationToken()); + } + + public Task CloseOutputSessionAsync(CancellationToken token) + { + return Channel.CloseOutputSessionAsync(token); + } + } + + } + + internal abstract class TransportOutputChannel : OutputChannel + { + private bool _anyHeadersToAdd; + private EndpointAddress _to; + private Uri _via; + private ToHeader _toHeader; + + protected TransportOutputChannel(IDefaultCommunicationTimeouts timeouts, EndpointAddress to, Uri via, bool manualAddressing, MessageVersion messageVersion) + : base(timeouts) + { + ManualAddressing = manualAddressing; + MessageVersion = messageVersion; + _to = to; + _via = via; + + if (!manualAddressing && _to != null) + { + Uri toUri; + if (_to.IsAnonymous) + { + toUri = MessageVersion.Addressing.AnonymousUri(); + } + else if (_to.IsNone) + { + toUri = MessageVersion.Addressing.NoneUri(); + } + else + { + toUri = _to.Uri; + } + + if (toUri != null) + { + XmlDictionaryString dictionaryTo = new ToDictionary(toUri.AbsoluteUri).To; + _toHeader = ToHeader.Create(toUri, dictionaryTo, messageVersion.Addressing); + } + + _anyHeadersToAdd = _to.Headers.Count > 0; + } + } + + protected bool ManualAddressing { get; } + + public MessageVersion MessageVersion { get; } + + public override EndpointAddress RemoteAddress + { + get + { + return _to; + } + } + + public override Uri Via + { + get + { + return _via; + } + } + + protected override void AddHeadersTo(Message message) + { + base.AddHeadersTo(message); + + if (_toHeader != null) + { + // TODO: Removed performance enhancement to avoid exposing another internal method. + // Evaluate whether we should do something to bring this back. My thoughts are we + // remove the SetToHeader method as we should be using the same mechanism as third + // parties transports have to use. + message.Headers.To = _toHeader.To; + + // Original comment and code: + // we don't use to.ApplyTo(message) since it's faster to cache and + // use the actual header then to call message.Headers.To = Uri... + //message.Headers.SetToHeader(toHeader); + + if (_anyHeadersToAdd) + { + _to.Headers.AddHeadersTo(message); + } + } + } + + private class ToDictionary : IXmlDictionary + { + private XmlDictionaryString to; + + public ToDictionary(string to) + { + this.to = new XmlDictionaryString(this, to, 0); + } + + public XmlDictionaryString To + { + get + { + return to; + } + } + + public bool TryLookup(string value, out XmlDictionaryString result) + { + if (value == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(value)); + if (value == to.Value) + { + result = to; + return true; + } + result = null; + return false; + } + + public bool TryLookup(int key, out XmlDictionaryString result) + { + if (key == 0) + { + result = to; + return true; + } + result = null; + return false; + } + + public bool TryLookup(XmlDictionaryString value, out XmlDictionaryString result) + { + if (value == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(value)); + if (value == to) + { + result = to; + return true; + } + result = null; + return false; + } + } + } + + internal abstract class OutputChannel : ServiceChannelBase, IOutputChannel + { + protected OutputChannel(IDefaultCommunicationTimeouts timeouts) : base(timeouts) { } + + public abstract EndpointAddress RemoteAddress { get; } + + public abstract Uri Via { get; } + + public override T GetProperty() + { + if (typeof(T) == typeof(IOutputChannel)) + { + return (T)(object)this; + } + + T baseProperty = base.GetProperty(); + if (baseProperty != null) + { + return baseProperty; + } + + return default(T); + } + + protected abstract Task OnSendAsync(Message message, CancellationToken token); + + public Task SendAsync(Message message) + { + return SendAsync(message, CancellationToken.None); + } + + public Task SendAsync(Message message, CancellationToken token) + { + if (message == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(message)); + + // TODO: Fix exception message as a negative timeout wasn't passed, a cancelled token was + if (token.IsCancellationRequested) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new ArgumentException(SR.SFxTimeoutOutOfRange0, nameof(token))); + + ThrowIfDisposedOrNotOpen(); + + AddHeadersTo(message); + EmitTrace(message); + return OnSendAsync(message, token); + } + + private void EmitTrace(Message message) + { + } + + protected virtual void AddHeadersTo(Message message) + { + } + } + + internal abstract class ServiceChannelBase : CommunicationObject, IChannel, IDefaultCommunicationTimeouts + { + private IDefaultCommunicationTimeouts _timeouts; + + protected ServiceChannelBase(IDefaultCommunicationTimeouts timeouts) + { + _timeouts = new ImmutableCommunicationTimeouts(timeouts); + } + + TimeSpan IDefaultCommunicationTimeouts.CloseTimeout => DefaultCloseTimeout; + + TimeSpan IDefaultCommunicationTimeouts.OpenTimeout => DefaultOpenTimeout; + + TimeSpan IDefaultCommunicationTimeouts.ReceiveTimeout => DefaultReceiveTimeout; + + TimeSpan IDefaultCommunicationTimeouts.SendTimeout => DefaultSendTimeout; + + protected override TimeSpan DefaultCloseTimeout => _timeouts.CloseTimeout; + + protected override TimeSpan DefaultOpenTimeout => _timeouts.OpenTimeout; + + protected TimeSpan DefaultReceiveTimeout => _timeouts.ReceiveTimeout; + + protected TimeSpan DefaultSendTimeout => _timeouts.SendTimeout; + + public virtual T GetProperty() where T : class + { + return null; + } + } +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Channels/Framing/ServerFramingDuplexSessionMiddleware.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/Framing/ServerFramingDuplexSessionMiddleware.cs new file mode 100644 index 000000000..419e8e9a5 --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/Framing/ServerFramingDuplexSessionMiddleware.cs @@ -0,0 +1,189 @@ +using Microsoft.AspNetCore.Connections; +using CoreWCF.Runtime; +using CoreWCF.Configuration; +using CoreWCF.Security; +using System; +using System.Buffers; +using System.IO; +using System.IO.Pipelines; +using System.Threading.Tasks; + +namespace CoreWCF.Channels.Framing +{ + internal class ServerFramingDuplexSessionMiddleware + { + private HandshakeDelegate _next; + + public ServerFramingDuplexSessionMiddleware(HandshakeDelegate next) + { + _next = next; + } + + public async Task OnConnectedAsync(FramingConnection connection) + { + bool success = false; + try + { + var decoder = connection.ServerSessionDecoder; + // first validate our content type + ValidateContentType(connection, decoder); + + // next read any potential upgrades and finish consuming the preamble + ReadOnlySequence buffer; + while (true) + { + var readResult = await connection.Input.ReadAsync(); + buffer = readResult.Buffer; + if (readResult.IsCompleted) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(decoder.CreatePrematureEOFException()); + } + + while (buffer.Length > 0) + { + int bytesDecoded = decoder.Decode(buffer); + if (bytesDecoded > 0) + { + buffer = buffer.Slice(bytesDecoded); + } + + switch (decoder.CurrentState) + { + case ServerSessionDecoder.State.UpgradeRequest: + ProcessUpgradeRequest(connection, decoder); + + // accept upgrade + await connection.Output.WriteAsync(ServerSessionEncoder.UpgradeResponseBytes); + await connection.Output.FlushAsync(); + //await context.Transport.Output.WriteAsync + //Connection.Write(ServerSessionEncoder.UpgradeResponseBytes, 0, ServerSessionEncoder.UpgradeResponseBytes.Length, true, timeoutHelper.RemainingTime()); + + try + { + connection.Input.AdvanceTo(buffer.Start); + buffer = ReadOnlySequence.Empty; + await UpgradeConnectionAsync(connection); + // TODO: ChannelBinding + //if (this.channelBindingProvider != null && this.channelBindingProvider.IsChannelBindingSupportEnabled) + //{ + // this.SetChannelBinding(this.channelBindingProvider.GetChannelBinding(this.upgradeAcceptor, ChannelBindingKind.Endpoint)); + //} + + //this.connectionBuffer = Connection.AsyncReadBuffer; + } + catch (Exception exception) + { + if (Fx.IsFatal(exception)) + throw; + + // Audit Authentication Failure + //WriteAuditFailure(upgradeAcceptor as StreamSecurityUpgradeAcceptor, exception); + throw; + } + break; + + case ServerSessionDecoder.State.Start: + SetupSecurityIfNecessary(connection); + + // we've finished the preamble. Ack and return. + await connection.Output.WriteAsync(ServerSessionEncoder.AckResponseBytes); + await connection.Output.FlushAsync(); + connection.Input.AdvanceTo(buffer.Start); + success = true; + await _next(connection); + return; + } + } + } + } + finally + { + if (!success) + { + // TODO: Work out abort code path + //Connection.Abort(); + } + } + } + + + private static void ValidateContentType(FramingConnection connection, ServerSessionDecoder decoder) + { + var messageEncoderFactory = connection.MessageEncoderFactory; + MessageEncoder messageEncoder = messageEncoderFactory.CreateSessionEncoder(); + connection.MessageEncoder = messageEncoder; + + if (!messageEncoder.IsContentTypeSupported(decoder.ContentType)) + { + // TODO: Send fault response + //SendFault(FramingEncodingString.ContentTypeInvalidFault, ref timeoutHelper); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ProtocolException(SR.Format( + SR.ContentTypeMismatch, decoder.ContentType, messageEncoder.ContentType))); + } + + ICompressedMessageEncoder compressedMessageEncoder = messageEncoder as ICompressedMessageEncoder; + if (compressedMessageEncoder != null && compressedMessageEncoder.CompressionEnabled) + { + compressedMessageEncoder.SetSessionContentType(decoder.ContentType); + } + } + + private static void ProcessUpgradeRequest(FramingConnection connection, ServerSessionDecoder decoder) + { + var upgradeAcceptor = connection.StreamUpgradeAcceptor; + if (upgradeAcceptor == null) + { + // TODO: SendFault + //SendFault(FramingEncodingString.UpgradeInvalidFault, ref timeoutHelper); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new ProtocolException(SR.Format(SR.UpgradeRequestToNonupgradableService, decoder.Upgrade))); + } + + if (!upgradeAcceptor.CanUpgrade(decoder.Upgrade)) + { + // TODO: SendFault + //SendFault(FramingEncodingString.UpgradeInvalidFault, ref timeoutHelper); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new ProtocolException(SR.Format(SR.UpgradeProtocolNotSupported, decoder.Upgrade))); + } + } + + public static async Task UpgradeConnectionAsync(FramingConnection connection) + { + Stream stream = new Microsoft.AspNetCore.Server.Kestrel.Core.Adapter.Internal.RawStream(connection.Input, connection.Output); + var upgradeAcceptor = connection.StreamUpgradeAcceptor; + stream = await upgradeAcceptor.AcceptUpgradeAsync(stream); + CreatePipelineFromStream(connection, stream); + } + + private static void SetupSecurityIfNecessary(FramingConnection connection) + { + StreamSecurityUpgradeAcceptor securityUpgradeAcceptor = connection.StreamUpgradeAcceptor as StreamSecurityUpgradeAcceptor; + if (securityUpgradeAcceptor != null) + { + var remoteSecurity = securityUpgradeAcceptor.GetRemoteSecurity(); + + if (remoteSecurity == null) + { + Exception securityFailedException = new ProtocolException( + SR.Format(SR.RemoteSecurityNotNegotiatedOnStreamUpgrade, connection.Via)); + //WriteAuditFailure(securityUpgradeAcceptor, securityFailedException); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(securityFailedException); + } + else + { + connection.SecurityMessageProperty = remoteSecurity; + // Audit Authentication Success + //WriteAuditEvent(securityUpgradeAcceptor, AuditLevel.Success, null); + } + } + } + + + private static void CreatePipelineFromStream(FramingConnection connection, Stream stream) + { + var wrappedPipeline = new StreamDuplexPipe(connection.Transport, stream); + connection.Transport = wrappedPipeline; + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Channels/Framing/ServerSessionConnectionReaderMiddleware.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/Framing/ServerSessionConnectionReaderMiddleware.cs new file mode 100644 index 000000000..20406336f --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/Framing/ServerSessionConnectionReaderMiddleware.cs @@ -0,0 +1,220 @@ +using Microsoft.AspNetCore.Connections; +using Microsoft.Extensions.DependencyInjection; +using CoreWCF.Runtime; +using CoreWCF.Configuration; +using CoreWCF.Security; +using System; +using System.Buffers; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Xml; + +namespace CoreWCF.Channels.Framing +{ + internal class ServerSessionConnectionReaderMiddleware + { + private HandshakeDelegate _next; + private IServiceScopeFactory _servicesScopeFactory; + + public ServerSessionConnectionReaderMiddleware(HandshakeDelegate next, IServiceScopeFactory servicesScopeFactory) + { + _next = next; + _servicesScopeFactory = servicesScopeFactory; + } + + public async Task OnConnectedAsync(FramingConnection connection) + { + var be = connection.ServiceDispatcher.Binding.CreateBindingElements(); + var tbe = be.Find(); + ITransportFactorySettings settings = new NetFramingTransportSettings + { + CloseTimeout = connection.ServiceDispatcher.Binding.CloseTimeout, + OpenTimeout = connection.ServiceDispatcher.Binding.OpenTimeout, + ReceiveTimeout = connection.ServiceDispatcher.Binding.ReceiveTimeout, + SendTimeout = connection.ServiceDispatcher.Binding.SendTimeout, + ManualAddressing = tbe.ManualAddressing, + BufferManager = connection.BufferManager, + MaxReceivedMessageSize = tbe.MaxReceivedMessageSize, + MessageEncoderFactory = connection.MessageEncoderFactory + }; + var scope = _servicesScopeFactory.CreateScope(); + var channel = new ServerFramingDuplexSessionChannel(connection, settings, false, _servicesScopeFactory.CreateScope().ServiceProvider); + await channel.OpenAsync(); + var decoder = connection.ServerSessionDecoder; + var serviceDispatcher = connection.ServiceDispatcher; + while (true) + { + Message message = await ReceiveMessageAsync(connection); + if (message == null) + { + return; // No more messages + } + // TODO: Create a correctly timing out ct + await serviceDispatcher.DispatchAsync(new DuplexRequestContext(channel, message, connection.ServiceDispatcher.Binding), channel, CancellationToken.None); + } + } + + private async Task ReceiveMessageAsync(FramingConnection connection) + { + Message message; + ReadOnlySequence buffer = ReadOnlySequence.Empty; + for (; ; ) + { + var readResult = await connection.Input.ReadAsync(); + if (readResult.IsCompleted || readResult.Buffer.Length == 0) + { + if (!readResult.IsCompleted) + connection.Input.AdvanceTo(readResult.Buffer.Start); + EnsureDecoderAtEof(connection); + connection.EOF = true; + } + + if (connection.EOF) + { + return null; + } + + buffer = readResult.Buffer; + message = DecodeMessage(connection, ref buffer); + connection.Input.AdvanceTo(buffer.Start); + + if (message != null) + { + PrepareMessage(connection, message); + return message; + } + else if (connection.EOF) // could have read the END record under DecodeMessage + { + return null; + } + + if (buffer.Length != 0) + { + throw Fx.AssertAndThrow("Receive: DecodeMessage() should consume the outstanding buffer or return a message."); + } + } + } + + private void PrepareMessage(FramingConnection connection, Message message) + { + if (connection.SecurityMessageProperty != null) + { + message.Properties.Security = (SecurityMessageProperty)connection.SecurityMessageProperty.CreateCopy(); + } + } + + private void EnsureDecoderAtEof(FramingConnection connection) + { + var decoder = connection.ServerSessionDecoder; + if (!(decoder.CurrentState == ServerSessionDecoder.State.End || decoder.CurrentState == ServerSessionDecoder.State.EnvelopeEnd)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(decoder.CreatePrematureEOFException()); + } + } + + private Message DecodeMessage(FramingConnection connection, ref ReadOnlySequence buffer) + { + // TODO: plumb through from binding + int maxBufferSize = TransportDefaults.MaxBufferSize; + var decoder = connection.ServerSessionDecoder; + while (!connection.EOF && buffer.Length > 0) + { + int bytesRead = decoder.Decode(buffer); + if (bytesRead > 0) + { + if (!connection.EnvelopeBuffer.IsEmpty) + { + var remainingEnvelopeBuffer = connection.EnvelopeBuffer.Slice(connection.EnvelopeOffset, connection.EnvelopeSize - connection.EnvelopeOffset); + CopyBuffer(buffer, remainingEnvelopeBuffer, bytesRead); + connection.EnvelopeOffset += bytesRead; + } + + buffer = buffer.Slice(bytesRead); + } + + switch (decoder.CurrentState) + { + case ServerSessionDecoder.State.EnvelopeStart: + int envelopeSize = decoder.EnvelopeSize; + if (envelopeSize > maxBufferSize) + { + // TODO: Sending faults + //base.SendFault(FramingEncodingString.MaxMessageSizeExceededFault, timeout); + + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + MaxMessageSizeStream.CreateMaxReceivedMessageSizeExceededException(maxBufferSize)); + } + connection.EnvelopeBuffer = connection.BufferManager.TakeBuffer(envelopeSize); + connection.EnvelopeSize = envelopeSize; + connection.EnvelopeOffset = 0; + break; + + case ServerSessionDecoder.State.EnvelopeEnd: + if (!connection.EnvelopeBuffer.IsEmpty) + { + Message message = null; + + try + { + message = connection.MessageEncoder.ReadMessage( + new ArraySegment(connection.EnvelopeBuffer.ToArray(), 0, connection.EnvelopeSize), + connection.BufferManager, + connection.ServerSessionDecoder.ContentType); + } + catch (XmlException xmlException) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new ProtocolException(SR.MessageXmlProtocolError, xmlException)); + } + + connection.EnvelopeBuffer = null; + return message; + } + break; + + case ServerSessionDecoder.State.End: + connection.EOF = true; + break; + } + } + + return null; + } + + private void CopyBuffer(ReadOnlySequence src, Memory dest, int bytesToCopy) + { + Fx.Assert(src.Length >= bytesToCopy, "Trying to copy more bytes than exist in src"); + // Grab only the number of bytes that we want to copy from the source sequence + src = src.Slice(0, bytesToCopy); + if(dest.Length < bytesToCopy) + { + throw new ArgumentOutOfRangeException(nameof(bytesToCopy)); + } + + var destSpan = dest.Span; + if (src.IsSingleSegment) + { + var srcSpan = src.First.Span; + srcSpan.CopyTo(destSpan); + } + else + { + foreach (var segment in src) + { + var srcSpan = segment.Span; + if (srcSpan.Length > bytesToCopy) + { + srcSpan = srcSpan.Slice(0, bytesToCopy); + } + srcSpan.CopyTo(destSpan); + bytesToCopy -= srcSpan.Length; + if (bytesToCopy == 0) + return; + destSpan = destSpan.Slice(srcSpan.Length); + } + } + } + } +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Channels/Framing/SingletonFramingFramingMiddleware.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/Framing/SingletonFramingFramingMiddleware.cs new file mode 100644 index 000000000..8ae53a9f3 --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/Framing/SingletonFramingFramingMiddleware.cs @@ -0,0 +1,20 @@ +using CoreWCF.Configuration; +using System.Threading.Tasks; + +namespace CoreWCF.Channels.Framing +{ + internal class SingletonFramingFramingMiddleware + { + private HandshakeDelegate _next; + + public SingletonFramingFramingMiddleware(HandshakeDelegate next) + { + _next = next; + } + + public Task OnConnectedAsync(FramingConnection connection) + { + return Task.CompletedTask; + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Channels/Framing/StreamDuplexPipe.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/Framing/StreamDuplexPipe.cs new file mode 100644 index 000000000..fbaf9807a --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/Framing/StreamDuplexPipe.cs @@ -0,0 +1,156 @@ +using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Pipelines; +using System.Text; +using System.Threading.Tasks; + +namespace CoreWCF.Channels.Framing +{ + internal class StreamDuplexPipe : IDuplexPipe + { + private static readonly int MinAllocBufferSize = 4096; + + private readonly IDuplexPipe _transport; + + // TODO: Have a mechanism to stop wrapping as Net.Tcp allows you to unwrap a connection and make it raw again + public StreamDuplexPipe(IDuplexPipe transport, Stream stream) + { + _transport = transport; + Input = new Pipe(new PipeOptions + ( + readerScheduler: PipeScheduler.Inline, + writerScheduler: PipeScheduler.Inline, + useSynchronizationContext: false + )); + Output = new Pipe(new PipeOptions + ( + readerScheduler: PipeScheduler.Inline, + writerScheduler: PipeScheduler.Inline, + useSynchronizationContext: false + )); + + BackgroundTask = RunAsync(stream); + } + + public Pipe Input { get; } + + public Pipe Output { get; } + + PipeReader IDuplexPipe.Input => Input.Reader; + + PipeWriter IDuplexPipe.Output => Output.Writer; + + public Task BackgroundTask { get; } + + private async Task RunAsync(Stream stream) + { + await Task.Yield(); + var inputTask = ReadInputAsync(stream); + var outputTask = WriteOutputAsync(stream); + + await inputTask; + await outputTask; + } + + private async Task WriteOutputAsync(Stream stream) + { + try + { + if (stream == null) + { + return; + } + + while (true) + { + var result = await Output.Reader.ReadAsync(); + var buffer = result.Buffer; + + try + { + if (buffer.IsEmpty) + { + if (result.IsCompleted) + { + break; + } + await stream.FlushAsync(); + } + else if (buffer.IsSingleSegment) + { + await stream.WriteAsync(buffer.First); + } + else + { + foreach (var memory in buffer) + { + await stream.WriteAsync(memory); + } + } + } + finally + { + Output.Reader.AdvanceTo(buffer.End); + } + } + } + catch (Exception) + { + // TODO: make sure the exception propagates somehow + } + finally + { + Output.Reader.Complete(); + _transport.Output.Complete(); + } + } + + private async Task ReadInputAsync(Stream stream) + { + Exception error = null; + + try + { + if (stream == null) + { + return; + } + + while (true) + { + + var outputBuffer = Input.Writer.GetMemory(MinAllocBufferSize); + var bytesRead = await stream.ReadAsync(outputBuffer); + Input.Writer.Advance(bytesRead); + + if (bytesRead == 0) + { + // FIN + break; + } + + var result = await Input.Writer.FlushAsync(); + + if (result.IsCompleted) + { + break; + } + } + } + catch (Exception ex) + { + // Don't rethrow the exception. It should be handled by the Pipeline consumer. + error = ex; + } + finally + { + Input.Writer.Complete(error); + // The application could have ended the input pipe so complete + // the transport pipe as well + _transport.Input.Complete(); + } + } + } +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Channels/FramingChannel.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/FramingChannel.cs new file mode 100644 index 000000000..95116d88e --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/FramingChannel.cs @@ -0,0 +1,256 @@ +using CoreWCF.Runtime; +using CoreWCF; +using CoreWCF.Diagnostics; +using CoreWCF.Security; +using CoreWCF.Runtime.Diagnostics; +using System; +using System.Diagnostics; +using System.IO; +using System.Threading; +using System.Security; +using System.Security.Authentication.ExtendedProtection; +using System.Security.Principal; +using System.Threading.Tasks; + +namespace CoreWCF.Channels +{ + abstract class FramingDuplexSessionChannel : TransportDuplexSessionChannel + { + IConnection connection; + bool exposeConnectionProperty; + + FramingDuplexSessionChannel(ChannelManagerBase manager, IConnectionOrientedTransportFactorySettings settings, + EndpointAddress localAddress, Uri localVia, EndpointAddress remoteAddresss, Uri via, bool exposeConnectionProperty) + : base(manager, settings, localAddress, localVia, remoteAddresss, via) + { + this.exposeConnectionProperty = exposeConnectionProperty; + } + + protected FramingDuplexSessionChannel(ChannelManagerBase factory, IConnectionOrientedTransportFactorySettings settings, + EndpointAddress remoteAddresss, Uri via, bool exposeConnectionProperty) + : this(factory, settings, EndpointAddress.AnonymousAddress, settings.MessageVersion.Addressing.AnonymousUri(), + remoteAddresss, via, exposeConnectionProperty) + { + Session = FramingConnectionDuplexSession.CreateSession(this, settings.Upgrade); + } + + protected FramingDuplexSessionChannel(ConnectionOrientedTransportChannelListener channelListener, + EndpointAddress localAddress, Uri localVia, bool exposeConnectionProperty) + : this(channelListener, channelListener, localAddress, localVia, + EndpointAddress.AnonymousAddress, channelListener.MessageVersion.Addressing.AnonymousUri(), exposeConnectionProperty) + { + Session = FramingConnectionDuplexSession.CreateSession(this, channelListener.Upgrade); + } + + protected IConnection Connection + { + get + { + return connection; + } + set + { + connection = value; + } + } + + protected override bool IsStreamedOutput + { + get { return false; } + } + + protected override Task CloseOutputSessionCoreAsync(CancellationToken token) + { + var timeout = TimeoutHelper.GetOriginalTimeout(token); + return Connection.WriteAsync(SessionEncoder.EndBytes, 0, SessionEncoder.EndBytes.Length, true, timeout); + } + + protected override Task CompleteCloseAsync(CancellationToken token) + { + ReturnConnectionIfNecessary(false, token); + return Task.CompletedTask; + } + + protected override void PrepareMessage(Message message) + { + if (exposeConnectionProperty) + { + message.Properties[ConnectionMessageProperty.Name] = connection; + } + base.PrepareMessage(message); + } + + protected override void OnSendCore(Message message, TimeSpan timeout) + { + bool allowOutputBatching; + ArraySegment messageData; + allowOutputBatching = message.Properties.AllowOutputBatching; + messageData = EncodeMessage(message); + Connection.Write(messageData.Array, messageData.Offset, messageData.Count, !allowOutputBatching, + timeout, BufferManager); + } + + protected override Task CloseOutputAsync(CancellationToken token) + { + var timeout = TimeoutHelper.GetOriginalTimeout(token); + return Connection.WriteAsync(SessionEncoder.EndBytes, 0, SessionEncoder.EndBytes.Length, + true, timeout); + } + + //protected override void FinishWritingMessage() + //{ + // this.Connection.EndWrite(); + //} + + protected override Task StartWritingBufferedMessageAsync(Message message, ArraySegment messageData, bool allowOutputBatching, CancellationToken token) + { + var timeout = TimeoutHelper.GetOriginalTimeout(token); + return Connection.WriteAsync(messageData.Array, messageData.Offset, messageData.Count, + !allowOutputBatching, timeout); + } + + protected override Task StartWritingStreamedMessageAsync(Message message, CancellationToken token) + { + Fx.Assert(false, "Streamed output should never be called in this channel."); + return Task.FromException(Fx.Exception.AsError(new InvalidOperationException())); + } + + protected override ArraySegment EncodeMessage(Message message) + { + ArraySegment messageData = MessageEncoder.WriteMessage(message, + int.MaxValue, BufferManager, SessionEncoder.MaxMessageFrameSize); + + messageData = SessionEncoder.EncodeMessageFrame(messageData); + + return messageData; + } + + class FramingConnectionDuplexSession : ConnectionDuplexSession + { + + FramingConnectionDuplexSession(FramingDuplexSessionChannel channel) + : base(channel) + { + } + + public static FramingConnectionDuplexSession CreateSession(FramingDuplexSessionChannel channel, + StreamUpgradeProvider upgrade) + { + StreamSecurityUpgradeProvider security = upgrade as StreamSecurityUpgradeProvider; + if (security == null) + { + return new FramingConnectionDuplexSession(channel); + } + else + { + return new SecureConnectionDuplexSession(channel); + } + } + + class SecureConnectionDuplexSession : FramingConnectionDuplexSession, ISecuritySession + { + EndpointIdentity remoteIdentity; + + public SecureConnectionDuplexSession(FramingDuplexSessionChannel channel) + : base(channel) + { + // empty + } + + EndpointIdentity ISecuritySession.RemoteIdentity + { + get + { + if (remoteIdentity == null) + { + SecurityMessageProperty security = Channel.RemoteSecurity; + if (security != null && security.ServiceSecurityContext != null && + security.ServiceSecurityContext.IdentityClaim != null && + security.ServiceSecurityContext.PrimaryIdentity != null) + { + remoteIdentity = EndpointIdentity.CreateIdentity( + security.ServiceSecurityContext.IdentityClaim); + } + } + + return remoteIdentity; + } + } + } + } + } + + // used by StreamedFramingRequestChannel and ClientFramingDuplexSessionChannel + class ConnectionUpgradeHelper + { + public static async Task DecodeFramingFaultAsync(ClientFramingDecoder decoder, IConnection connection, + Uri via, string contentType, TimeoutHelper timeoutHelper) + { + ValidateReadingFaultString(decoder); + + int offset = 0; + byte[] faultBuffer = Fx.AllocateByteArray(FaultStringDecoder.FaultSizeQuota); + int size = await connection.ReadAsync(0, Math.Min(FaultStringDecoder.FaultSizeQuota, connection.AsyncReadBufferSize), + timeoutHelper.RemainingTime()); + + while (size > 0) + { + int bytesDecoded = decoder.Decode(connection.AsyncReadBuffer, offset, size); + offset += bytesDecoded; + size -= bytesDecoded; + + if (decoder.CurrentState == ClientFramingDecoderState.Fault) + { + ConnectionUtilities.CloseNoThrow(connection, timeoutHelper.RemainingTime()); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + FaultStringDecoder.GetFaultException(decoder.Fault, via.ToString(), contentType)); + } + else + { + if (decoder.CurrentState != ClientFramingDecoderState.ReadingFaultString) + { + throw Fx.AssertAndThrow("invalid framing client state machine"); + } + if (size == 0) + { + offset = 0; + size = await connection.ReadAsync(0, Math.Min(FaultStringDecoder.FaultSizeQuota, connection.AsyncReadBufferSize), + timeoutHelper.RemainingTime()); + } + } + } + + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(decoder.CreatePrematureEOFException()); + } + + static void ValidateReadingFaultString(ClientFramingDecoder decoder) + { + if (decoder.CurrentState != ClientFramingDecoderState.ReadingFaultString) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageSecurityException( + SR.Format(SR.ServerRejectedUpgradeRequest))); + } + } + + static bool ValidateUpgradeResponse(byte[] buffer, int count, ClientFramingDecoder decoder) + { + if (count == 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageSecurityException(SR.Format(SR.ServerRejectedUpgradeRequest), decoder.CreatePrematureEOFException())); + } + + // decode until the framing byte has been processed (it always will be) + while (decoder.Decode(buffer, 0, count) == 0) + { + // do nothing + } + + if (decoder.CurrentState != ClientFramingDecoderState.UpgradeResponse) // we have a problem + { + return false; + } + + return true; + } + } +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Channels/FramingDecoder.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/FramingDecoder.cs new file mode 100644 index 000000000..dbf77ba38 --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/FramingDecoder.cs @@ -0,0 +1,1631 @@ +using System; +using System.Globalization; +using CoreWCF; +using System.IO; +using System.Text; +using CoreWCF.Runtime; + +namespace CoreWCF.Channels +{ + static class DecoderHelper + { + public static void ValidateSize(int size) + { + if (size <= 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("size", size, SR.ValueMustBePositive)); + } + } + } + + struct IntDecoder + { + int value; + short index; + bool isValueDecoded; + const int LastIndex = 4; + + public int Value + { + get + { + if (!isValueDecoded) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.FramingValueNotAvailable)); + return value; + } + } + + public bool IsValueDecoded + { + get { return isValueDecoded; } + } + + public void Reset() + { + index = 0; + value = 0; + isValueDecoded = false; + } + + public int Decode(byte[] buffer, int offset, int size) + { + DecoderHelper.ValidateSize(size); + if (isValueDecoded) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.FramingValueNotAvailable)); + } + int bytesConsumed = 0; + while (bytesConsumed < size) + { + int next = buffer[offset]; + value |= (next & 0x7F) << (index * 7); + bytesConsumed++; + if (index == LastIndex && (next & 0xF8) != 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataException(SR.FramingSizeTooLarge)); + } + index++; + if ((next & 0x80) == 0) + { + isValueDecoded = true; + break; + } + offset++; + } + return bytesConsumed; + } + } + + abstract class StringDecoder + { + int encodedSize; + byte[] encodedBytes; + int bytesNeeded; + string value; + State currentState; + IntDecoder sizeDecoder; + int sizeQuota; + int valueLengthInBytes; + + public StringDecoder(int sizeQuota) + { + this.sizeQuota = sizeQuota; + sizeDecoder = new IntDecoder(); + currentState = State.ReadingSize; + Reset(); + } + + public bool IsValueDecoded + { + get { return currentState == State.Done; } + } + + public string Value + { + get + { + if (currentState != State.Done) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.FramingValueNotAvailable)); + return value; + } + } + + public int Decode(byte[] buffer, int offset, int size) + { + DecoderHelper.ValidateSize(size); + + int bytesConsumed; + switch (currentState) + { + case State.ReadingSize: + bytesConsumed = sizeDecoder.Decode(buffer, offset, size); + if (sizeDecoder.IsValueDecoded) + { + encodedSize = sizeDecoder.Value; + if (encodedSize > sizeQuota) + { + Exception quotaExceeded = OnSizeQuotaExceeded(encodedSize); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(quotaExceeded); + } + if (encodedBytes == null || encodedBytes.Length < encodedSize) + { + encodedBytes = Fx.AllocateByteArray(encodedSize); + value = null; + } + currentState = State.ReadingBytes; + bytesNeeded = encodedSize; + } + break; + case State.ReadingBytes: + if (value != null && valueLengthInBytes == encodedSize && bytesNeeded == encodedSize && + size >= encodedSize && CompareBuffers(encodedBytes, buffer, offset)) + { + bytesConsumed = bytesNeeded; + OnComplete(value); + } + else + { + bytesConsumed = bytesNeeded; + if (size < bytesNeeded) + bytesConsumed = size; + Buffer.BlockCopy(buffer, offset, encodedBytes, encodedSize - bytesNeeded, bytesConsumed); + bytesNeeded -= bytesConsumed; + if (bytesNeeded == 0) + { + value = Encoding.UTF8.GetString(encodedBytes, 0, encodedSize); + valueLengthInBytes = encodedSize; + OnComplete(value); + } + } + break; + default: + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataException(SR.InvalidDecoderStateMachine)); + } + + return bytesConsumed; + } + + protected virtual void OnComplete(string value) + { + currentState = State.Done; + } + + static bool CompareBuffers(byte[] buffer1, byte[] buffer2, int offset) + { + for (int i = 0; i < buffer1.Length; i++) + { + if (buffer1[i] != buffer2[i + offset]) + { + return false; + } + } + return true; + } + + protected abstract Exception OnSizeQuotaExceeded(int size); + + public void Reset() + { + currentState = State.ReadingSize; + sizeDecoder.Reset(); + } + + enum State + { + ReadingSize, + ReadingBytes, + Done, + } + } + + class ViaStringDecoder : StringDecoder + { + Uri via; + + public ViaStringDecoder(int sizeQuota) + : base(sizeQuota) + { + } + + protected override Exception OnSizeQuotaExceeded(int size) + { + Exception result = new InvalidDataException(SR.Format(SR.FramingViaTooLong, size)); + FramingEncodingString.AddFaultString(result, FramingEncodingString.ViaTooLongFault); + return result; + } + + protected override void OnComplete(string value) + { + try + { + via = new Uri(value); + base.OnComplete(value); + } + catch (UriFormatException exception) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataException(SR.Format(SR.FramingViaNotUri, value), exception)); + } + } + + public Uri ValueAsUri + { + get + { + if (!IsValueDecoded) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.FramingValueNotAvailable)); + return via; + } + } + } + + class FaultStringDecoder : StringDecoder + { + internal const int FaultSizeQuota = 256; + + public FaultStringDecoder() + : base(FaultSizeQuota) + { + } + + protected override Exception OnSizeQuotaExceeded(int size) + { + return new InvalidDataException(SR.Format(SR.FramingFaultTooLong, size)); + } + + public static Exception GetFaultException(string faultString, string via, string contentType) + { + if (faultString == FramingEncodingString.EndpointNotFoundFault) + { + return new EndpointNotFoundException(SR.Format(SR.EndpointNotFound, via)); + } + else if (faultString == FramingEncodingString.ContentTypeInvalidFault) + { + return new ProtocolException(SR.Format(SR.FramingContentTypeMismatch, contentType, via)); + } + else if (faultString == FramingEncodingString.ServiceActivationFailedFault) + { + return new ServiceActivationException(SR.Format(SR.Hosting_ServiceActivationFailed, via)); + } + else if (faultString == FramingEncodingString.ConnectionDispatchFailedFault) + { + return new CommunicationException(SR.Format(SR.Sharing_ConnectionDispatchFailed, via)); + } + else if (faultString == FramingEncodingString.EndpointUnavailableFault) + { + return new EndpointNotFoundException(SR.Format(SR.Sharing_EndpointUnavailable, via)); + } + else if (faultString == FramingEncodingString.MaxMessageSizeExceededFault) + { + Exception inner = new QuotaExceededException(SR.FramingMaxMessageSizeExceeded); + return new CommunicationException(inner.Message, inner); + } + else if (faultString == FramingEncodingString.UnsupportedModeFault) + { + return new ProtocolException(SR.Format(SR.FramingModeNotSupportedFault, via)); + } + else if (faultString == FramingEncodingString.UnsupportedVersionFault) + { + return new ProtocolException(SR.Format(SR.FramingVersionNotSupportedFault, via)); + } + else if (faultString == FramingEncodingString.ContentTypeTooLongFault) + { + Exception inner = new QuotaExceededException(SR.Format(SR.FramingContentTypeTooLongFault, contentType)); + return new CommunicationException(inner.Message, inner); + } + else if (faultString == FramingEncodingString.ViaTooLongFault) + { + Exception inner = new QuotaExceededException(SR.Format(SR.FramingViaTooLongFault, via)); + return new CommunicationException(inner.Message, inner); + } + else if (faultString == FramingEncodingString.ServerTooBusyFault) + { + return new ServerTooBusyException(SR.Format(SR.ServerTooBusy, via)); + } + else if (faultString == FramingEncodingString.UpgradeInvalidFault) + { + return new ProtocolException(SR.Format(SR.FramingUpgradeInvalid, via)); + } + else + { + return new ProtocolException(SR.Format(SR.FramingFaultUnrecognized, faultString)); + } + } + } + + class ContentTypeStringDecoder : StringDecoder + { + public ContentTypeStringDecoder(int sizeQuota) + : base(sizeQuota) + { + } + + protected override Exception OnSizeQuotaExceeded(int size) + { + Exception result = new InvalidDataException(SR.Format(SR.FramingContentTypeTooLong, size)); + FramingEncodingString.AddFaultString(result, FramingEncodingString.ContentTypeTooLongFault); + return result; + } + + public static string GetString(FramingEncodingType type) + { + switch (type) + { + case FramingEncodingType.Soap11Utf8: + return FramingEncodingString.Soap11Utf8; + case FramingEncodingType.Soap11Utf16: + return FramingEncodingString.Soap11Utf16; + case FramingEncodingType.Soap11Utf16FFFE: + return FramingEncodingString.Soap11Utf16FFFE; + case FramingEncodingType.Soap12Utf8: + return FramingEncodingString.Soap12Utf8; + case FramingEncodingType.Soap12Utf16: + return FramingEncodingString.Soap12Utf16; + case FramingEncodingType.Soap12Utf16FFFE: + return FramingEncodingString.Soap12Utf16FFFE; + case FramingEncodingType.MTOM: + return FramingEncodingString.MTOM; + case FramingEncodingType.Binary: + return FramingEncodingString.Binary; + case FramingEncodingType.BinarySession: + return FramingEncodingString.BinarySession; + default: + return "unknown" + ((int)type).ToString(CultureInfo.InvariantCulture); + } + } + } + + abstract class FramingDecoder + { + long streamPosition; + + protected FramingDecoder() + { + } + + protected FramingDecoder(long streamPosition) + { + this.streamPosition = streamPosition; + } + + protected abstract string CurrentStateAsString { get; } + + public long StreamPosition + { + get { return streamPosition; } + set { streamPosition = value; } + } + + protected void ValidateFramingMode(FramingMode mode) + { + switch (mode) + { + case FramingMode.Singleton: + case FramingMode.Duplex: + case FramingMode.Simplex: + case FramingMode.SingletonSized: + break; + default: + { + Exception exception = CreateException(new InvalidDataException(SR.Format( + SR.FramingModeNotSupported, mode.ToString())), FramingEncodingString.UnsupportedModeFault); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(exception); + } + } + } + + protected void ValidateRecordType(FramingRecordType expectedType, FramingRecordType foundType) + { + if (foundType != expectedType) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateInvalidRecordTypeException(expectedType, foundType)); + } + } + + // special validation for Preamble Ack for usability purposes (MB#39593) + protected void ValidatePreambleAck(FramingRecordType foundType) + { + if (foundType != FramingRecordType.PreambleAck) + { + Exception inner = CreateInvalidRecordTypeException(FramingRecordType.PreambleAck, foundType); + string exceptionString; + if (((byte)foundType == 'h') || ((byte)foundType == 'H')) + { + exceptionString = SR.PreambleAckIncorrectMaybeHttp; + } + else + { + exceptionString = SR.PreambleAckIncorrect; + } + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ProtocolException(exceptionString, inner)); + } + } + + Exception CreateInvalidRecordTypeException(FramingRecordType expectedType, FramingRecordType foundType) + { + return new InvalidDataException(SR.Format(SR.FramingRecordTypeMismatch, expectedType.ToString(), foundType.ToString())); + } + + protected void ValidateMajorVersion(int majorVersion) + { + if (majorVersion != FramingVersion.Major) + { + Exception exception = CreateException(new InvalidDataException(SR.Format( + SR.FramingVersionNotSupported, majorVersion)), FramingEncodingString.UnsupportedVersionFault); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(exception); + } + } + + public Exception CreatePrematureEOFException() + { + return CreateException(new InvalidDataException(SR.FramingPrematureEOF)); + } + + protected Exception CreateException(InvalidDataException innerException, string framingFault) + { + Exception result = CreateException(innerException); + FramingEncodingString.AddFaultString(result, framingFault); + return result; + } + + protected Exception CreateException(InvalidDataException innerException) + { + return new ProtocolException(SR.Format(SR.FramingError, StreamPosition, CurrentStateAsString), + innerException); + } + } + + // Pattern: + // Done + class ServerModeDecoder : FramingDecoder + { + State currentState; + int majorVersion; + int minorVersion; + FramingMode mode; + + public ServerModeDecoder() + { + currentState = State.ReadingVersionRecord; + } + + public int Decode(byte[] bytes, int offset, int size) + { + DecoderHelper.ValidateSize(size); + + try + { + int bytesConsumed; + switch (currentState) + { + case State.ReadingVersionRecord: + ValidateRecordType(FramingRecordType.Version, (FramingRecordType)bytes[offset]); + currentState = State.ReadingMajorVersion; + bytesConsumed = 1; + break; + case State.ReadingMajorVersion: + majorVersion = bytes[offset]; + ValidateMajorVersion(majorVersion); + currentState = State.ReadingMinorVersion; + bytesConsumed = 1; + break; + case State.ReadingMinorVersion: + minorVersion = bytes[offset]; + currentState = State.ReadingModeRecord; + bytesConsumed = 1; + break; + case State.ReadingModeRecord: + ValidateRecordType(FramingRecordType.Mode, (FramingRecordType)bytes[offset]); + currentState = State.ReadingModeValue; + bytesConsumed = 1; + break; + case State.ReadingModeValue: + mode = (FramingMode)bytes[offset]; + ValidateFramingMode(mode); + currentState = State.Done; + bytesConsumed = 1; + break; + default: + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + CreateException(new InvalidDataException(SR.InvalidDecoderStateMachine))); + } + + StreamPosition += bytesConsumed; + return bytesConsumed; + } + catch (InvalidDataException e) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateException(e)); + } + } + + public void Reset() + { + StreamPosition = 0; + currentState = State.ReadingVersionRecord; + } + + public State CurrentState + { + get { return currentState; } + } + + protected override string CurrentStateAsString + { + get { return currentState.ToString(); } + } + + public FramingMode Mode + { + get + { + if (currentState != State.Done) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.FramingValueNotAvailable)); + return mode; + } + } + + public int MajorVersion + { + get + { + if (currentState != State.Done) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.FramingValueNotAvailable)); + return majorVersion; + } + } + + public int MinorVersion + { + get + { + if (currentState != State.Done) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.FramingValueNotAvailable)); + return minorVersion; + } + } + + public enum State + { + ReadingVersionRecord, + ReadingMajorVersion, + ReadingMinorVersion, + ReadingModeRecord, + ReadingModeValue, + Done, + } + } + + // Used for Duplex/Simplex + // Pattern: + // Start, + // (UpgradeRequest, upgrade-content-type)*, + // (EnvelopeStart, ReadingEnvelopeBytes*, EnvelopeEnd)*, + // End + class ServerSessionDecoder : FramingDecoder + { + ViaStringDecoder viaDecoder; + StringDecoder contentTypeDecoder; + IntDecoder sizeDecoder; + State currentState; + string contentType; + int envelopeBytesNeeded; + int envelopeSize; + string upgrade; + + public ServerSessionDecoder(long streamPosition, int maxViaLength, int maxContentTypeLength) + : base(streamPosition) + { + viaDecoder = new ViaStringDecoder(maxViaLength); + contentTypeDecoder = new ContentTypeStringDecoder(maxContentTypeLength); + sizeDecoder = new IntDecoder(); + currentState = State.ReadingViaRecord; + } + + public State CurrentState + { + get { return currentState; } + } + + protected override string CurrentStateAsString + { + get { return currentState.ToString(); } + } + + public string ContentType + { + get + { + if (currentState < State.PreUpgradeStart) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.FramingValueNotAvailable)); + return contentType; + } + } + + public Uri Via + { + get + { + if (currentState < State.ReadingContentTypeRecord) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.FramingValueNotAvailable)); + return viaDecoder.ValueAsUri; + } + } + + public void Reset(long streamPosition) + { + StreamPosition = streamPosition; + currentState = State.ReadingViaRecord; + } + + public string Upgrade + { + get + { + if (currentState != State.UpgradeRequest) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.FramingValueNotAvailable)); + return upgrade; + } + } + + public int EnvelopeSize + { + get + { + if (currentState < State.EnvelopeStart) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.FramingValueNotAvailable)); + return envelopeSize; + } + } + + public int Decode(byte[] bytes, int offset, int size) + { + DecoderHelper.ValidateSize(size); + + try + { + int bytesConsumed; + FramingRecordType recordType; + switch (currentState) + { + case State.ReadingViaRecord: + recordType = (FramingRecordType)bytes[offset]; + ValidateRecordType(FramingRecordType.Via, recordType); + bytesConsumed = 1; + viaDecoder.Reset(); + currentState = State.ReadingViaString; + break; + case State.ReadingViaString: + bytesConsumed = viaDecoder.Decode(bytes, offset, size); + if (viaDecoder.IsValueDecoded) + { + currentState = State.ReadingContentTypeRecord; + } + break; + case State.ReadingContentTypeRecord: + recordType = (FramingRecordType)bytes[offset]; + if (recordType == FramingRecordType.KnownEncoding) + { + bytesConsumed = 1; + currentState = State.ReadingContentTypeByte; + } + else + { + ValidateRecordType(FramingRecordType.ExtensibleEncoding, recordType); + bytesConsumed = 1; + contentTypeDecoder.Reset(); + currentState = State.ReadingContentTypeString; + } + break; + case State.ReadingContentTypeByte: + contentType = ContentTypeStringDecoder.GetString((FramingEncodingType)bytes[offset]); + bytesConsumed = 1; + currentState = State.PreUpgradeStart; + break; + case State.ReadingContentTypeString: + bytesConsumed = contentTypeDecoder.Decode(bytes, offset, size); + if (contentTypeDecoder.IsValueDecoded) + { + currentState = State.PreUpgradeStart; + contentType = contentTypeDecoder.Value; + } + break; + case State.PreUpgradeStart: + bytesConsumed = 0; + currentState = State.ReadingUpgradeRecord; + break; + case State.ReadingUpgradeRecord: + recordType = (FramingRecordType)bytes[offset]; + if (recordType == FramingRecordType.UpgradeRequest) + { + bytesConsumed = 1; + contentTypeDecoder.Reset(); + currentState = State.ReadingUpgradeString; + } + else + { + bytesConsumed = 0; + currentState = State.ReadingPreambleEndRecord; + } + break; + case State.ReadingUpgradeString: + bytesConsumed = contentTypeDecoder.Decode(bytes, offset, size); + if (contentTypeDecoder.IsValueDecoded) + { + currentState = State.UpgradeRequest; + upgrade = contentTypeDecoder.Value; + } + break; + case State.UpgradeRequest: + bytesConsumed = 0; + currentState = State.ReadingUpgradeRecord; + break; + case State.ReadingPreambleEndRecord: + recordType = (FramingRecordType)bytes[offset]; + ValidateRecordType(FramingRecordType.PreambleEnd, recordType); + bytesConsumed = 1; + currentState = State.Start; + break; + case State.Start: + bytesConsumed = 0; + currentState = State.ReadingEndRecord; + break; + case State.ReadingEndRecord: + recordType = (FramingRecordType)bytes[offset]; + if (recordType == FramingRecordType.End) + { + bytesConsumed = 1; + currentState = State.End; + } + else + { + bytesConsumed = 0; + currentState = State.ReadingEnvelopeRecord; + } + break; + case State.ReadingEnvelopeRecord: + ValidateRecordType(FramingRecordType.SizedEnvelope, (FramingRecordType)bytes[offset]); + bytesConsumed = 1; + currentState = State.ReadingEnvelopeSize; + sizeDecoder.Reset(); + break; + case State.ReadingEnvelopeSize: + bytesConsumed = sizeDecoder.Decode(bytes, offset, size); + if (sizeDecoder.IsValueDecoded) + { + currentState = State.EnvelopeStart; + envelopeSize = sizeDecoder.Value; + envelopeBytesNeeded = envelopeSize; + } + break; + case State.EnvelopeStart: + bytesConsumed = 0; + currentState = State.ReadingEnvelopeBytes; + break; + case State.ReadingEnvelopeBytes: + bytesConsumed = size; + if (bytesConsumed > envelopeBytesNeeded) + bytesConsumed = envelopeBytesNeeded; + envelopeBytesNeeded -= bytesConsumed; + if (envelopeBytesNeeded == 0) + currentState = State.EnvelopeEnd; + break; + case State.EnvelopeEnd: + bytesConsumed = 0; + currentState = State.ReadingEndRecord; + break; + case State.End: + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + CreateException(new InvalidDataException(SR.FramingAtEnd))); + default: + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + CreateException(new InvalidDataException(SR.InvalidDecoderStateMachine))); + } + + StreamPosition += bytesConsumed; + return bytesConsumed; + } + catch (InvalidDataException e) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateException(e)); + } + } + + public enum State + { + ReadingViaRecord, + ReadingViaString, + ReadingContentTypeRecord, + ReadingContentTypeString, + ReadingContentTypeByte, + PreUpgradeStart, + ReadingUpgradeRecord, + ReadingUpgradeString, + UpgradeRequest, + ReadingPreambleEndRecord, + Start, + ReadingEnvelopeRecord, + ReadingEnvelopeSize, + EnvelopeStart, + ReadingEnvelopeBytes, + EnvelopeEnd, + ReadingEndRecord, + End, + } + } + + class SingletonMessageDecoder : FramingDecoder + { + IntDecoder sizeDecoder; + int chunkBytesNeeded; + int chunkSize; + State currentState; + + public SingletonMessageDecoder(long streamPosition) + : base(streamPosition) + { + sizeDecoder = new IntDecoder(); + currentState = State.ChunkStart; + } + + public void Reset() + { + currentState = State.ChunkStart; + } + + public State CurrentState + { + get { return currentState; } + } + + protected override string CurrentStateAsString + { + get { return currentState.ToString(); } + } + + public int ChunkSize + { + get + { + if (currentState < State.ChunkStart) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.FramingValueNotAvailable)); + } + + return chunkSize; + } + } + + public int Decode(byte[] bytes, int offset, int size) + { + DecoderHelper.ValidateSize(size); + + try + { + int bytesConsumed; + switch (currentState) + { + case State.ReadingEnvelopeChunkSize: + bytesConsumed = sizeDecoder.Decode(bytes, offset, size); + if (sizeDecoder.IsValueDecoded) + { + chunkSize = sizeDecoder.Value; + sizeDecoder.Reset(); + + if (chunkSize == 0) + { + currentState = State.EnvelopeEnd; + } + else + { + currentState = State.ChunkStart; + chunkBytesNeeded = chunkSize; + } + } + break; + case State.ChunkStart: + bytesConsumed = 0; + currentState = State.ReadingEnvelopeBytes; + break; + case State.ReadingEnvelopeBytes: + bytesConsumed = size; + if (bytesConsumed > chunkBytesNeeded) + { + bytesConsumed = chunkBytesNeeded; + } + chunkBytesNeeded -= bytesConsumed; + if (chunkBytesNeeded == 0) + { + currentState = State.ChunkEnd; + } + break; + case State.ChunkEnd: + bytesConsumed = 0; + currentState = State.ReadingEnvelopeChunkSize; + break; + case State.EnvelopeEnd: + ValidateRecordType(FramingRecordType.End, (FramingRecordType)bytes[offset]); + bytesConsumed = 1; + currentState = State.End; + break; + case State.End: + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + CreateException(new InvalidDataException(SR.FramingAtEnd))); + + default: + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + CreateException(new InvalidDataException(SR.InvalidDecoderStateMachine))); + } + + StreamPosition += bytesConsumed; + return bytesConsumed; + } + catch (InvalidDataException e) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateException(e)); + } + } + + public enum State + { + ReadingEnvelopeChunkSize, + ChunkStart, + ReadingEnvelopeBytes, + ChunkEnd, + EnvelopeEnd, + End, + } + } + + // Pattern: + // Start, + // (UpgradeRequest, upgrade-bytes)*, + // EnvelopeStart, + class ServerSingletonDecoder : FramingDecoder + { + ViaStringDecoder viaDecoder; + ContentTypeStringDecoder contentTypeDecoder; + State currentState; + string contentType; + string upgrade; + + public ServerSingletonDecoder(long streamPosition, int maxViaLength, int maxContentTypeLength) + : base(streamPosition) + { + viaDecoder = new ViaStringDecoder(maxViaLength); + contentTypeDecoder = new ContentTypeStringDecoder(maxContentTypeLength); + currentState = State.ReadingViaRecord; + } + + public void Reset() + { + currentState = State.ReadingViaRecord; + } + + public State CurrentState + { + get { return currentState; } + } + + protected override string CurrentStateAsString + { + get { return currentState.ToString(); } + } + + public Uri Via + { + get + { + if (currentState < State.ReadingContentTypeRecord) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.FramingValueNotAvailable)); + return viaDecoder.ValueAsUri; + } + } + + public string ContentType + { + get + { + if (currentState < State.PreUpgradeStart) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.FramingValueNotAvailable)); + return contentType; + } + } + + public string Upgrade + { + get + { + if (currentState != State.UpgradeRequest) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.FramingValueNotAvailable)); + return upgrade; + } + } + + public int Decode(byte[] bytes, int offset, int size) + { + DecoderHelper.ValidateSize(size); + + try + { + int bytesConsumed; + FramingRecordType recordType; + switch (currentState) + { + case State.ReadingViaRecord: + recordType = (FramingRecordType)bytes[offset]; + ValidateRecordType(FramingRecordType.Via, recordType); + bytesConsumed = 1; + viaDecoder.Reset(); + currentState = State.ReadingViaString; + break; + case State.ReadingViaString: + bytesConsumed = viaDecoder.Decode(bytes, offset, size); + if (viaDecoder.IsValueDecoded) + { + currentState = State.ReadingContentTypeRecord; + } + break; + case State.ReadingContentTypeRecord: + recordType = (FramingRecordType)bytes[offset]; + if (recordType == FramingRecordType.KnownEncoding) + { + bytesConsumed = 1; + currentState = State.ReadingContentTypeByte; + } + else + { + ValidateRecordType(FramingRecordType.ExtensibleEncoding, recordType); + bytesConsumed = 1; + contentTypeDecoder.Reset(); + currentState = State.ReadingContentTypeString; + } + break; + case State.ReadingContentTypeByte: + contentType = ContentTypeStringDecoder.GetString((FramingEncodingType)bytes[offset]); + bytesConsumed = 1; + currentState = State.PreUpgradeStart; + break; + case State.ReadingContentTypeString: + bytesConsumed = contentTypeDecoder.Decode(bytes, offset, size); + if (contentTypeDecoder.IsValueDecoded) + { + currentState = State.PreUpgradeStart; + contentType = contentTypeDecoder.Value; + } + break; + case State.PreUpgradeStart: + bytesConsumed = 0; + currentState = State.ReadingUpgradeRecord; + break; + case State.ReadingUpgradeRecord: + recordType = (FramingRecordType)bytes[offset]; + if (recordType == FramingRecordType.UpgradeRequest) + { + bytesConsumed = 1; + contentTypeDecoder.Reset(); + currentState = State.ReadingUpgradeString; + } + else + { + bytesConsumed = 0; + currentState = State.ReadingPreambleEndRecord; + } + break; + case State.ReadingUpgradeString: + bytesConsumed = contentTypeDecoder.Decode(bytes, offset, size); + if (contentTypeDecoder.IsValueDecoded) + { + currentState = State.UpgradeRequest; + upgrade = contentTypeDecoder.Value; + } + break; + case State.UpgradeRequest: + bytesConsumed = 0; + currentState = State.ReadingUpgradeRecord; + break; + case State.ReadingPreambleEndRecord: + recordType = (FramingRecordType)bytes[offset]; + ValidateRecordType(FramingRecordType.PreambleEnd, recordType); + bytesConsumed = 1; + currentState = State.Start; + break; + case State.Start: + bytesConsumed = 0; + currentState = State.ReadingEnvelopeRecord; + break; + case State.ReadingEnvelopeRecord: + ValidateRecordType(FramingRecordType.UnsizedEnvelope, (FramingRecordType)bytes[offset]); + bytesConsumed = 1; + currentState = State.EnvelopeStart; + break; + case State.EnvelopeStart: + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + CreateException(new InvalidDataException(SR.FramingAtEnd))); + default: + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + CreateException(new InvalidDataException(SR.InvalidDecoderStateMachine))); + } + + StreamPosition += bytesConsumed; + return bytesConsumed; + } + catch (InvalidDataException e) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateException(e)); + } + } + + public enum State + { + ReadingViaRecord, + ReadingViaString, + ReadingContentTypeRecord, + ReadingContentTypeString, + ReadingContentTypeByte, + PreUpgradeStart, + ReadingUpgradeRecord, + ReadingUpgradeString, + UpgradeRequest, + ReadingPreambleEndRecord, + Start, + ReadingEnvelopeRecord, + EnvelopeStart, + ReadingEnvelopeChunkSize, + ChunkStart, + ReadingEnvelopeChunk, + ChunkEnd, + End, + } + } + + // Pattern: + // Start, + // EnvelopeStart, + class ServerSingletonSizedDecoder : FramingDecoder + { + ViaStringDecoder viaDecoder; + ContentTypeStringDecoder contentTypeDecoder; + State currentState; + string contentType; + + public ServerSingletonSizedDecoder(long streamPosition, int maxViaLength, int maxContentTypeLength) + : base(streamPosition) + { + viaDecoder = new ViaStringDecoder(maxViaLength); + contentTypeDecoder = new ContentTypeStringDecoder(maxContentTypeLength); + currentState = State.ReadingViaRecord; + } + + public int Decode(byte[] bytes, int offset, int size) + { + DecoderHelper.ValidateSize(size); + + try + { + int bytesConsumed; + FramingRecordType recordType; + switch (currentState) + { + case State.ReadingViaRecord: + recordType = (FramingRecordType)bytes[offset]; + ValidateRecordType(FramingRecordType.Via, recordType); + bytesConsumed = 1; + viaDecoder.Reset(); + currentState = State.ReadingViaString; + break; + case State.ReadingViaString: + bytesConsumed = viaDecoder.Decode(bytes, offset, size); + if (viaDecoder.IsValueDecoded) + currentState = State.ReadingContentTypeRecord; + break; + case State.ReadingContentTypeRecord: + recordType = (FramingRecordType)bytes[offset]; + if (recordType == FramingRecordType.KnownEncoding) + { + bytesConsumed = 1; + currentState = State.ReadingContentTypeByte; + } + else + { + ValidateRecordType(FramingRecordType.ExtensibleEncoding, recordType); + bytesConsumed = 1; + contentTypeDecoder.Reset(); + currentState = State.ReadingContentTypeString; + } + break; + case State.ReadingContentTypeByte: + contentType = ContentTypeStringDecoder.GetString((FramingEncodingType)bytes[offset]); + bytesConsumed = 1; + currentState = State.Start; + break; + case State.ReadingContentTypeString: + bytesConsumed = contentTypeDecoder.Decode(bytes, offset, size); + if (contentTypeDecoder.IsValueDecoded) + { + currentState = State.Start; + contentType = contentTypeDecoder.Value; + } + break; + case State.Start: + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + CreateException(new InvalidDataException(SR.FramingAtEnd))); + default: + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + CreateException(new InvalidDataException(SR.InvalidDecoderStateMachine))); + } + + StreamPosition += bytesConsumed; + return bytesConsumed; + } + catch (InvalidDataException e) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateException(e)); + } + } + + public void Reset(long streamPosition) + { + StreamPosition = streamPosition; + currentState = State.ReadingViaRecord; + } + + public State CurrentState + { + get { return currentState; } + } + + protected override string CurrentStateAsString + { + get { return currentState.ToString(); } + } + + public Uri Via + { + get + { + if (currentState < State.ReadingContentTypeRecord) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.FramingValueNotAvailable)); + return viaDecoder.ValueAsUri; + } + } + + public string ContentType + { + get + { + if (currentState < State.Start) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.FramingValueNotAvailable)); + return contentType; + } + } + + public enum State + { + ReadingViaRecord, + ReadingViaString, + ReadingContentTypeRecord, + ReadingContentTypeString, + ReadingContentTypeByte, + Start, + } + } + + // common set of states used on the client-side. + enum ClientFramingDecoderState + { + ReadingUpgradeRecord, + ReadingUpgradeMode, + UpgradeResponse, + ReadingAckRecord, + Start, + ReadingFault, + ReadingFaultString, + Fault, + ReadingEnvelopeRecord, + ReadingEnvelopeSize, + EnvelopeStart, + ReadingEnvelopeBytes, + EnvelopeEnd, + ReadingEndRecord, + End, + } + + abstract class ClientFramingDecoder : FramingDecoder + { + ClientFramingDecoderState currentState; + + protected ClientFramingDecoder(long streamPosition) + : base(streamPosition) + { + currentState = ClientFramingDecoderState.ReadingUpgradeRecord; + } + + public ClientFramingDecoderState CurrentState + { + get + { + return currentState; + } + + protected set + { + currentState = value; + } + } + + protected override string CurrentStateAsString + { + get { return currentState.ToString(); } + } + + public abstract string Fault + { + get; + } + + public abstract int Decode(byte[] bytes, int offset, int size); + } + + // Pattern: + // (UpgradeResponse, upgrade-bytes)*, (Ack | Fault), + // ((EnvelopeStart, ReadingEnvelopeBytes*, EnvelopeEnd) | Fault)*, + // End + class ClientDuplexDecoder : ClientFramingDecoder + { + IntDecoder sizeDecoder; + FaultStringDecoder faultDecoder; + int envelopeBytesNeeded; + int envelopeSize; + + public ClientDuplexDecoder(long streamPosition) + : base(streamPosition) + { + sizeDecoder = new IntDecoder(); + } + + public int EnvelopeSize + { + get + { + if (CurrentState < ClientFramingDecoderState.EnvelopeStart) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.FramingValueNotAvailable)); + return envelopeSize; + } + } + + public override string Fault + { + get + { + if (CurrentState < ClientFramingDecoderState.Fault) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.FramingValueNotAvailable)); + return faultDecoder.Value; + } + } + + public override int Decode(byte[] bytes, int offset, int size) + { + DecoderHelper.ValidateSize(size); + + try + { + int bytesConsumed; + FramingRecordType recordType; + switch (CurrentState) + { + case ClientFramingDecoderState.ReadingUpgradeRecord: + recordType = (FramingRecordType)bytes[offset]; + if (recordType == FramingRecordType.UpgradeResponse) + { + bytesConsumed = 1; + base.CurrentState = ClientFramingDecoderState.UpgradeResponse; + } + else + { + bytesConsumed = 0; + base.CurrentState = ClientFramingDecoderState.ReadingAckRecord; + } + break; + case ClientFramingDecoderState.UpgradeResponse: + bytesConsumed = 0; + base.CurrentState = ClientFramingDecoderState.ReadingUpgradeRecord; + break; + case ClientFramingDecoderState.ReadingAckRecord: + recordType = (FramingRecordType)bytes[offset]; + if (recordType == FramingRecordType.Fault) + { + bytesConsumed = 1; + faultDecoder = new FaultStringDecoder(); + base.CurrentState = ClientFramingDecoderState.ReadingFaultString; + break; + } + ValidatePreambleAck(recordType); + bytesConsumed = 1; + base.CurrentState = ClientFramingDecoderState.Start; + break; + case ClientFramingDecoderState.Start: + bytesConsumed = 0; + base.CurrentState = ClientFramingDecoderState.ReadingEnvelopeRecord; + break; + case ClientFramingDecoderState.ReadingEnvelopeRecord: + recordType = (FramingRecordType)bytes[offset]; + if (recordType == FramingRecordType.End) + { + bytesConsumed = 1; + base.CurrentState = ClientFramingDecoderState.End; + break; + } + else if (recordType == FramingRecordType.Fault) + { + bytesConsumed = 1; + faultDecoder = new FaultStringDecoder(); + base.CurrentState = ClientFramingDecoderState.ReadingFaultString; + break; + } + ValidateRecordType(FramingRecordType.SizedEnvelope, recordType); + bytesConsumed = 1; + base.CurrentState = ClientFramingDecoderState.ReadingEnvelopeSize; + sizeDecoder.Reset(); + break; + case ClientFramingDecoderState.ReadingEnvelopeSize: + bytesConsumed = sizeDecoder.Decode(bytes, offset, size); + if (sizeDecoder.IsValueDecoded) + { + base.CurrentState = ClientFramingDecoderState.EnvelopeStart; + envelopeSize = sizeDecoder.Value; + envelopeBytesNeeded = envelopeSize; + } + break; + case ClientFramingDecoderState.EnvelopeStart: + bytesConsumed = 0; + base.CurrentState = ClientFramingDecoderState.ReadingEnvelopeBytes; + break; + case ClientFramingDecoderState.ReadingEnvelopeBytes: + bytesConsumed = size; + if (bytesConsumed > envelopeBytesNeeded) + bytesConsumed = envelopeBytesNeeded; + envelopeBytesNeeded -= bytesConsumed; + if (envelopeBytesNeeded == 0) + base.CurrentState = ClientFramingDecoderState.EnvelopeEnd; + break; + case ClientFramingDecoderState.EnvelopeEnd: + bytesConsumed = 0; + base.CurrentState = ClientFramingDecoderState.ReadingEnvelopeRecord; + break; + case ClientFramingDecoderState.ReadingFaultString: + bytesConsumed = faultDecoder.Decode(bytes, offset, size); + if (faultDecoder.IsValueDecoded) + { + base.CurrentState = ClientFramingDecoderState.Fault; + } + break; + case ClientFramingDecoderState.Fault: + bytesConsumed = 0; + base.CurrentState = ClientFramingDecoderState.ReadingEndRecord; + break; + case ClientFramingDecoderState.ReadingEndRecord: + ValidateRecordType(FramingRecordType.End, (FramingRecordType)bytes[offset]); + bytesConsumed = 1; + base.CurrentState = ClientFramingDecoderState.End; + break; + case ClientFramingDecoderState.End: + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + CreateException(new InvalidDataException(SR.FramingAtEnd))); + default: + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + CreateException(new InvalidDataException(SR.InvalidDecoderStateMachine))); + } + + StreamPosition += bytesConsumed; + return bytesConsumed; + } + catch (InvalidDataException e) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateException(e)); + } + } + } + + // Pattern: + // (UpgradeResponse, upgrade-bytes)*, (Ack | Fault), + // End + class ClientSingletonDecoder : ClientFramingDecoder + { + FaultStringDecoder faultDecoder; + + public ClientSingletonDecoder(long streamPosition) + : base(streamPosition) + { + } + + public override string Fault + { + get + { + if (CurrentState < ClientFramingDecoderState.Fault) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.FramingValueNotAvailable)); + return faultDecoder.Value; + } + } + + public override int Decode(byte[] bytes, int offset, int size) + { + DecoderHelper.ValidateSize(size); + + try + { + int bytesConsumed; + FramingRecordType recordType; + switch (CurrentState) + { + case ClientFramingDecoderState.ReadingUpgradeRecord: + recordType = (FramingRecordType)bytes[offset]; + if (recordType == FramingRecordType.UpgradeResponse) + { + bytesConsumed = 1; + base.CurrentState = ClientFramingDecoderState.UpgradeResponse; + } + else + { + bytesConsumed = 0; + base.CurrentState = ClientFramingDecoderState.ReadingAckRecord; + } + break; + case ClientFramingDecoderState.UpgradeResponse: + bytesConsumed = 0; + base.CurrentState = ClientFramingDecoderState.ReadingUpgradeRecord; + break; + case ClientFramingDecoderState.ReadingAckRecord: + recordType = (FramingRecordType)bytes[offset]; + if (recordType == FramingRecordType.Fault) + { + bytesConsumed = 1; + faultDecoder = new FaultStringDecoder(); + base.CurrentState = ClientFramingDecoderState.ReadingFaultString; + break; + } + ValidatePreambleAck(recordType); + bytesConsumed = 1; + base.CurrentState = ClientFramingDecoderState.Start; + break; + + case ClientFramingDecoderState.Start: + bytesConsumed = 0; + base.CurrentState = ClientFramingDecoderState.ReadingEnvelopeRecord; + break; + + case ClientFramingDecoderState.ReadingEnvelopeRecord: + recordType = (FramingRecordType)bytes[offset]; + if (recordType == FramingRecordType.End) + { + bytesConsumed = 1; + base.CurrentState = ClientFramingDecoderState.End; + break; + } + else if (recordType == FramingRecordType.Fault) + { + bytesConsumed = 0; + base.CurrentState = ClientFramingDecoderState.ReadingFault; + break; + } + ValidateRecordType(FramingRecordType.UnsizedEnvelope, recordType); + bytesConsumed = 1; + base.CurrentState = ClientFramingDecoderState.EnvelopeStart; + break; + + case ClientFramingDecoderState.EnvelopeStart: + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + CreateException(new InvalidDataException(SR.FramingAtEnd))); + + case ClientFramingDecoderState.ReadingFault: + recordType = (FramingRecordType)bytes[offset]; + ValidateRecordType(FramingRecordType.Fault, recordType); + bytesConsumed = 1; + faultDecoder = new FaultStringDecoder(); + base.CurrentState = ClientFramingDecoderState.ReadingFaultString; + break; + case ClientFramingDecoderState.ReadingFaultString: + bytesConsumed = faultDecoder.Decode(bytes, offset, size); + if (faultDecoder.IsValueDecoded) + { + base.CurrentState = ClientFramingDecoderState.Fault; + } + break; + case ClientFramingDecoderState.Fault: + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + CreateException(new InvalidDataException(SR.FramingAtEnd))); + default: + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + CreateException(new InvalidDataException(SR.InvalidDecoderStateMachine))); + } + + StreamPosition += bytesConsumed; + return bytesConsumed; + } + catch (InvalidDataException e) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateException(e)); + } + } + } +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Channels/FramingEncoder.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/FramingEncoder.cs new file mode 100644 index 000000000..f7c742e4c --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/FramingEncoder.cs @@ -0,0 +1,366 @@ +using CoreWCF.Runtime; +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoreWCF.Channels +{ + internal static class IntEncoder + { + public const int MaxEncodedSize = 5; + + public static int Encode(int value, byte[] bytes, int offset) + { + int count = 1; + while ((value & 0xFFFFFF80) != 0) + { + bytes[offset++] = (byte)((value & 0x7F) | 0x80); + count++; + value >>= 7; + } + bytes[offset] = (byte)value; + return count; + } + + public static int GetEncodedSize(int value) + { + if (value < 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value, + SR.ValueMustBeNonNegative)); + } + + int count = 1; + while ((value & 0xFFFFFF80) != 0) + { + count++; + value >>= 7; + } + return count; + } + } + + internal abstract class EncodedFramingRecord + { + private byte[] _encodedBytes; + + protected EncodedFramingRecord(byte[] encodedBytes) + { + _encodedBytes = encodedBytes; + } + + internal EncodedFramingRecord(FramingRecordType recordType, string value) + { + int valueByteCount = Encoding.UTF8.GetByteCount(value); + int sizeByteCount = IntEncoder.GetEncodedSize(valueByteCount); + _encodedBytes = Fx.AllocateByteArray(checked(1 + sizeByteCount + valueByteCount)); + _encodedBytes[0] = (byte)recordType; + int offset = 1; + offset += IntEncoder.Encode(valueByteCount, _encodedBytes, offset); + Encoding.UTF8.GetBytes(value, 0, value.Length, _encodedBytes, offset); + SetEncodedBytes(_encodedBytes); + } + + + public byte[] EncodedBytes + { + get { return _encodedBytes; } + } + + protected void SetEncodedBytes(byte[] encodedBytes) + { + _encodedBytes = encodedBytes; + } + + public override int GetHashCode() + { + return (_encodedBytes[0] << 16) | + (_encodedBytes[_encodedBytes.Length / 2] << 8) | + _encodedBytes[_encodedBytes.Length - 1]; + } + + public override bool Equals(object o) + { + if (o is EncodedFramingRecord) + return Equals((EncodedFramingRecord)o); + return false; + } + + public bool Equals(EncodedFramingRecord other) + { + if (other == null) + return false; + if (other == this) + return true; + byte[] otherBytes = other._encodedBytes; + if (_encodedBytes.Length != otherBytes.Length) + return false; + + for (int i = 0; i < _encodedBytes.Length; i++) + { + if (_encodedBytes[i] != otherBytes[i]) + return false; + } + + return true; + } + } + + internal class EncodedContentType : EncodedFramingRecord + { + private EncodedContentType(FramingEncodingType encodingType) : + base(new byte[] { (byte)FramingRecordType.KnownEncoding, (byte)encodingType }) + { + } + + private EncodedContentType(string contentType) + : base(FramingRecordType.ExtensibleEncoding, contentType) + { + } + + public static EncodedContentType Create(string contentType) + { + if (contentType == FramingEncodingString.BinarySession) + { + return new EncodedContentType(FramingEncodingType.BinarySession); + } + else if (contentType == FramingEncodingString.Binary) + { + return new EncodedContentType(FramingEncodingType.Binary); + } + else if (contentType == FramingEncodingString.Soap12Utf8) + { + return new EncodedContentType(FramingEncodingType.Soap12Utf8); + } + else if (contentType == FramingEncodingString.Soap11Utf8) + { + return new EncodedContentType(FramingEncodingType.Soap11Utf8); + } + else if (contentType == FramingEncodingString.Soap12Utf16) + { + return new EncodedContentType(FramingEncodingType.Soap12Utf16); + } + else if (contentType == FramingEncodingString.Soap11Utf16) + { + return new EncodedContentType(FramingEncodingType.Soap11Utf16); + } + else if (contentType == FramingEncodingString.Soap12Utf16FFFE) + { + return new EncodedContentType(FramingEncodingType.Soap12Utf16FFFE); + } + else if (contentType == FramingEncodingString.Soap11Utf16FFFE) + { + return new EncodedContentType(FramingEncodingType.Soap11Utf16FFFE); + } + else if (contentType == FramingEncodingString.MTOM) + { + return new EncodedContentType(FramingEncodingType.MTOM); + } + else + { + return new EncodedContentType(contentType); + } + } + } + + internal class EncodedVia : EncodedFramingRecord + { + public EncodedVia(string via) + : base(FramingRecordType.Via, via) + { + } + } + + internal class EncodedUpgrade : EncodedFramingRecord + { + public EncodedUpgrade(string contentType) + : base(FramingRecordType.UpgradeRequest, contentType) + { + } + } + + internal class EncodedFault : EncodedFramingRecord + { + public EncodedFault(string fault) + : base(FramingRecordType.Fault, fault) + { + } + } + + // used by SimplexEncoder/DuplexEncoder + internal abstract class SessionEncoder + { + public const int MaxMessageFrameSize = 1 + IntEncoder.MaxEncodedSize; + + protected SessionEncoder() { } + + public static byte[] PreambleEndBytes = new byte[] { + (byte)FramingRecordType.PreambleEnd + }; + + public static byte[] EndBytes = new byte[] { + (byte)FramingRecordType.End + }; + + public static int CalcStartSize(EncodedVia via, EncodedContentType contentType) + { + return via.EncodedBytes.Length + contentType.EncodedBytes.Length; + } + + public static void EncodeStart(byte[] buffer, int offset, EncodedVia via, EncodedContentType contentType) + { + Buffer.BlockCopy(via.EncodedBytes, 0, buffer, offset, via.EncodedBytes.Length); + Buffer.BlockCopy(contentType.EncodedBytes, 0, buffer, offset + via.EncodedBytes.Length, contentType.EncodedBytes.Length); + } + + public static ArraySegment EncodeMessageFrame(ArraySegment messageFrame) + { + int spaceNeeded = 1 + IntEncoder.GetEncodedSize(messageFrame.Count); + int offset = messageFrame.Offset - spaceNeeded; + if (offset < 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("messageFrame.Offset", + messageFrame.Offset, SR.Format(SR.SpaceNeededExceedsMessageFrameOffset, spaceNeeded))); + } + + byte[] buffer = messageFrame.Array; + buffer[offset++] = (byte)FramingRecordType.SizedEnvelope; + IntEncoder.Encode(messageFrame.Count, buffer, offset); + return new ArraySegment(buffer, messageFrame.Offset - spaceNeeded, messageFrame.Count + spaceNeeded); + } + } + + // used by ServerDuplexEncoder/ServerSimplexEncoder + internal abstract class ServerSessionEncoder : SessionEncoder + { + protected ServerSessionEncoder() { } + + public static byte[] AckResponseBytes = new byte[] { + (byte)FramingRecordType.PreambleAck + }; + public static byte[] UpgradeResponseBytes = new byte[] { + (byte)FramingRecordType.UpgradeResponse + }; + } + + // Pattern: + // ModeBytes, + // EncodeStart, + // EncodeUpgrade*, + // EncodeMessageFrame*, + // EndBytes + internal class ClientDuplexEncoder : SessionEncoder + { + private ClientDuplexEncoder() { } + + public static byte[] ModeBytes = new byte[] { + (byte)FramingRecordType.Version, + (byte)FramingVersion.Major, + (byte)FramingVersion.Minor, + (byte)FramingRecordType.Mode, + (byte)FramingMode.Duplex }; + } + + // Pattern: + // ModeBytes, + // EncodeStart, + // EncodeUpgrade*, + // EncodeMessageFrame*, + // EndBytes + internal class ClientSimplexEncoder : SessionEncoder + { + private ClientSimplexEncoder() { } + + public static byte[] ModeBytes = new byte[] { + (byte)FramingRecordType.Version, + (byte)FramingVersion.Major, + (byte)FramingVersion.Minor, + (byte)FramingRecordType.Mode, + (byte)FramingMode.Simplex }; + } + + // shared code for client and server + internal abstract class SingletonEncoder + { + protected SingletonEncoder() + { + } + + public static byte[] EnvelopeStartBytes = new byte[] { + (byte)FramingRecordType.UnsizedEnvelope }; + + public static byte[] EnvelopeEndBytes = new byte[] { + (byte)0 }; + + public static byte[] EnvelopeEndFramingEndBytes = new byte[] { + (byte)0, (byte)FramingRecordType.End }; + + public static byte[] EndBytes = new byte[] { + (byte)FramingRecordType.End }; + + public static ArraySegment EncodeMessageFrame(ArraySegment messageFrame) + { + int spaceNeeded = IntEncoder.GetEncodedSize(messageFrame.Count); + int offset = messageFrame.Offset - spaceNeeded; + if (offset < 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("messageFrame.Offset", + messageFrame.Offset, SR.Format(SR.SpaceNeededExceedsMessageFrameOffset, spaceNeeded))); + } + + byte[] buffer = messageFrame.Array; + IntEncoder.Encode(messageFrame.Count, buffer, offset); + return new ArraySegment(buffer, offset, messageFrame.Count + spaceNeeded); + } + } + + // Pattern: + // ModeBytes, + // EncodeStart, + // EncodeUpgrade*, + // EnvelopeStartBytes, + // streamed-message-bytes* + internal class ClientSingletonEncoder : SingletonEncoder + { + private ClientSingletonEncoder() { } + + + public static byte[] PreambleEndBytes = new byte[] { + (byte)FramingRecordType.PreambleEnd + }; + + public static byte[] ModeBytes = new byte[] { + (byte)FramingRecordType.Version, + (byte)FramingVersion.Major, + (byte)FramingVersion.Minor, + (byte)FramingRecordType.Mode, + (byte)FramingMode.Singleton }; + + public static int CalcStartSize(EncodedVia via, EncodedContentType contentType) + { + return via.EncodedBytes.Length + contentType.EncodedBytes.Length; + } + + public static void EncodeStart(byte[] buffer, int offset, EncodedVia via, EncodedContentType contentType) + { + Buffer.BlockCopy(via.EncodedBytes, 0, buffer, offset, via.EncodedBytes.Length); + Buffer.BlockCopy(contentType.EncodedBytes, 0, buffer, offset + via.EncodedBytes.Length, contentType.EncodedBytes.Length); + } + } + + // Pattern: + // (UpgradeResponseBytes, upgrade-bytes)?, + internal class ServerSingletonEncoder : SingletonEncoder + { + private ServerSingletonEncoder() { } + + public static byte[] AckResponseBytes = new byte[] { + (byte)FramingRecordType.PreambleAck + }; + + public static byte[] UpgradeResponseBytes = new byte[] { + (byte)FramingRecordType.UpgradeResponse + }; + } + +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Channels/FramingFormat.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/FramingFormat.cs new file mode 100644 index 000000000..8bbe01667 --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/FramingFormat.cs @@ -0,0 +1,179 @@ +using System; + +namespace CoreWCF.Channels +{ + /* + Message Framing BNF: + + protocol-stream-a = (singleton-unsized-stream-a | duplex-stream-a | simplex-stream-a | singleton-sized-stream-a)+ + protocol-stream-b = (singleton-unsized-stream-b | duplex-stream-b)+ + + singleton-unsized-stream-a = version-record mode-record-type singleton-unsized-mode via-record encoding-record upgrade-request* preamble-end-record-type singleton-message end-record-type + duplex-stream-a = version-record mode-record-type duplex-mode via-record encoding-record upgrade-request* preamble-end-record-type duplex-message* end-record-type + simplex-stream-a = version-record mode-record-type simplex-mode via-record encoding-record simplex-message* end-record-type + singleton-sized-stream-a = version-record mode-record-type singleton-sized-mode via-record encoding-record octets + + singleton-unsized-stream-b = upgrade-response* preamble-response singleton-message? end-record-type + duplex-stream-b = upgrade-response* preamble-response duplex-message* (fault-message | end-record-type) + + singleton-message = unsized-message + duplex-message = sized-message + simplex-message = sized-message + fault-message = fault-record-type mbint utf8-octets + sized-message = sized-envelope-record-type mbint octets + unsized-message = unsized-envelope-record-type (mbint octets)* octet(0x0) + + preamble-response = preamble-ack-record-type | fault-message + + upgrade-request = upgrade-request-record-type mbint utf8-octets octets + upgrade-response = upgrade-response-record-type octets + + version-record = version-record-type major-version-number minor-version-number + major-version-number = octet(0x1) + minor-version-number = octet(0x0) + + encoding-record = known-encoding-record | extensible-encoding-record + known-encoding-record = known-encoding-record-type known-encoding-type + extensible-encoding-record = extensible-encoding-record-type mbint utf8-octets + + via-record = via-record-type mbint utf8-octets + + singleton-unsized-mode = octet(0x1) + duplex-mode = octet(0x2) + simplex-mode = octet(0x3) + singleton-sized-mode = octet(0x4) + + known-encoding-type = text-encoding | binary-encoding | mtom-encoding + binary-encoding = binary-sessionless-encoding | binary-session-encoding + text-encoding = soap11-text-encoding | soap12-text-encoding + soap11-text-encoding = soap11-utf8-encoding | soap11-utf16-encoding | soap11-unicodeFFFE-encoding + soap12-text-encoding = soap12-utf8-encoding | soap12-utf16-encoding | soap12-unicodeFFFE-encoding + + soap11-utf8-encoding = octet(0x0) + soap11-utf16-encoding = octet(0x1) + soap11-unicodeFFFE-encoding = octet(0x2) + soap12-utf8-encoding = octet(0x3) + soap12-utf16-encoding = octet(0x4) + soap12-unicodeFFFE-encoding = octet(0x5) + mtom-encoding = octet(0x6) + binary-sessionless-encoding = octet(0x7) + binary-session-encoding = octet(0x8) + + version-record-type = octet(0x0) + mode-record-type = octet(0x1) + via-record-type = octet(0x2) + known-encoding-record-type = octet(0x3) + extensible-encoding-record-type = octet(0x4) + unsized-envelope-record-type = octet(0x5) + sized-envelope-record-type = octet(0x6) + end-record-type = octet(0x7) + fault-record-type = octet(0x8) + upgrade-request-record-type = octet(0x9) + upgrade-response-record-type = octet(0xA) + preamble-ack-record-type = octet (0xB) + preamble-end-record-type = octet (0xC) + */ + + enum FramingRecordType + { + Version = 0x0, + Mode = 0x1, + Via = 0x2, + KnownEncoding = 0x3, + ExtensibleEncoding = 0x4, + UnsizedEnvelope = 0x5, + SizedEnvelope = 0x6, + End = 0x7, + Fault = 0x8, + UpgradeRequest = 0x9, + UpgradeResponse = 0xA, + PreambleAck = 0xB, + PreambleEnd = 0xC, + } + + enum FramingMode + { + Singleton = 0x1, + Duplex = 0x2, + Simplex = 0x3, + SingletonSized = 0x4, + } + + static class FramingUpgradeString + { + public const string SslOrTls = "application/ssl-tls"; + public const string Negotiate = "application/negotiate"; + } + + enum FramingEncodingType + { + Soap11Utf8 = 0x0, + Soap11Utf16 = 0x1, + Soap11Utf16FFFE = 0x2, + Soap12Utf8 = 0x3, + Soap12Utf16 = 0x4, + Soap12Utf16FFFE = 0x5, + MTOM = 0x6, + Binary = 0x7, + BinarySession = 0x8, + } + + static class FramingEncodingString + { + public const string Soap11Utf8 = "text/xml; charset=utf-8"; + public const string Soap11Utf16 = "text/xml; charset=utf16"; + public const string Soap11Utf16FFFE = "text/xml; charset=unicodeFFFE"; + public const string Soap12Utf8 = "application/soap+xml; charset=utf-8"; + public const string Soap12Utf16 = "application/soap+xml; charset=utf16"; + public const string Soap12Utf16FFFE = "application/soap+xml; charset=unicodeFFFE"; + public const string MTOM = "multipart/related"; + public const string Binary = "application/soap+msbin1"; + public const string BinarySession = "application/soap+msbinsession1"; + public const string ExtendedBinaryGZip = Binary + "+gzip"; + public const string ExtendedBinarySessionGZip = BinarySession + "+gzip"; + public const string ExtendedBinaryDeflate = Binary + "+deflate"; + public const string ExtendedBinarySessionDeflate = BinarySession + "+deflate"; + public const string NamespaceUri = "http://schemas.microsoft.com/ws/2006/05/framing"; + const string FaultBaseUri = NamespaceUri + "/faults/"; + public const string ContentTypeInvalidFault = FaultBaseUri + "ContentTypeInvalid"; + public const string ContentTypeTooLongFault = FaultBaseUri + "ContentTypeTooLong"; + public const string ConnectionDispatchFailedFault = FaultBaseUri + "ConnectionDispatchFailed"; + public const string EndpointNotFoundFault = FaultBaseUri + "EndpointNotFound"; + public const string EndpointUnavailableFault = FaultBaseUri + "EndpointUnavailable"; + public const string MaxMessageSizeExceededFault = FaultBaseUri + "MaxMessageSizeExceededFault"; + public const string ServerTooBusyFault = FaultBaseUri + "ServerTooBusy"; + public const string ServiceActivationFailedFault = FaultBaseUri + "ServiceActivationFailed"; + public const string UnsupportedModeFault = FaultBaseUri + "UnsupportedMode"; + public const string UnsupportedVersionFault = FaultBaseUri + "UnsupportedVersion"; + public const string UpgradeInvalidFault = FaultBaseUri + "UpgradeInvalid"; + public const string ViaTooLongFault = FaultBaseUri + "ViaTooLong"; + + const string ExceptionKey = "FramingEncodingString"; + public static bool TryGetFaultString(Exception exception, out string framingFault) + { + framingFault = null; + if (exception.Data.Contains(FramingEncodingString.ExceptionKey)) + { + framingFault = exception.Data[FramingEncodingString.ExceptionKey] as string; + if (framingFault != null) + { + return true; + } + } + + return false; + } + + public static void AddFaultString(Exception exception, string framingFault) + { + exception.Data[FramingEncodingString.ExceptionKey] = framingFault; + } + } + + static class FramingVersion + { + public const int Major = 0x1; + public const int Minor = 0x0; + } + +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Channels/IConnectionOrientedTransportFactorySettings.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/IConnectionOrientedTransportFactorySettings.cs new file mode 100644 index 000000000..7a207b3b6 --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/IConnectionOrientedTransportFactorySettings.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoreWCF.Channels +{ + interface IConnectionOrientedTransportFactorySettings : ITransportFactorySettings, IConnectionOrientedConnectionSettings + { + int MaxBufferSize { get; } + StreamUpgradeProvider Upgrade { get; } + TransferMode TransferMode { get; } + // Audit + //ServiceSecurityAuditBehavior AuditBehavior { get; } + } + + interface IConnectionOrientedConnectionSettings + { + int ConnectionBufferSize { get; } + TimeSpan MaxOutputDelay { get; } + TimeSpan IdleTimeout { get; } + } + + interface IConnectionOrientedListenerSettings : IConnectionOrientedConnectionSettings + { + TimeSpan ChannelInitializationTimeout { get; } + int MaxPendingConnections { get; } + int MaxPendingAccepts { get; } + int MaxPooledConnections { get; } + } +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Channels/IMessageSource.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/IMessageSource.cs new file mode 100644 index 000000000..1c5a91861 --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/IMessageSource.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace CoreWCF.Channels +{ + enum AsyncReceiveResult + { + Completed, + Pending, + } + + interface IMessageSource + { + Task ReceiveAsync(CancellationToken token); + Task WaitForMessageAsync(CancellationToken token); + } +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Channels/IStreamUpgradeChannelBindingProvider.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/IStreamUpgradeChannelBindingProvider.cs new file mode 100644 index 000000000..f779c0622 --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/IStreamUpgradeChannelBindingProvider.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Security.Authentication.ExtendedProtection; +using System.Text; + +namespace CoreWCF.Channels +{ + interface IStreamUpgradeChannelBindingProvider : IChannelBindingProvider + { + ChannelBinding GetChannelBinding(StreamUpgradeAcceptor upgradeAcceptor, ChannelBindingKind kind); + } +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Channels/ImmutableCommunicationTimeouts.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/ImmutableCommunicationTimeouts.cs new file mode 100644 index 000000000..ce0ff213a --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/ImmutableCommunicationTimeouts.cs @@ -0,0 +1,43 @@ +using System; + +namespace CoreWCF.Channels +{ + class ImmutableCommunicationTimeouts : IDefaultCommunicationTimeouts + { + TimeSpan _close; + TimeSpan _open; + TimeSpan _receive; + TimeSpan _send; + + internal ImmutableCommunicationTimeouts() + : this(null) + { + } + + internal ImmutableCommunicationTimeouts(IDefaultCommunicationTimeouts timeouts) + { + if (timeouts == null) + { + _close = ServiceDefaults.CloseTimeout; + _open = ServiceDefaults.OpenTimeout; + _receive = ServiceDefaults.ReceiveTimeout; + _send = ServiceDefaults.SendTimeout; + } + else + { + _close = timeouts.CloseTimeout; + _open = timeouts.OpenTimeout; + _receive = timeouts.ReceiveTimeout; + _send = timeouts.SendTimeout; + } + } + + TimeSpan IDefaultCommunicationTimeouts.CloseTimeout => _close; + + TimeSpan IDefaultCommunicationTimeouts.OpenTimeout => _open; + + TimeSpan IDefaultCommunicationTimeouts.ReceiveTimeout => _receive; + + TimeSpan IDefaultCommunicationTimeouts.SendTimeout => _send; + } +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Channels/InitialServerConnectionReader.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/InitialServerConnectionReader.cs new file mode 100644 index 000000000..d32f8a073 --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/InitialServerConnectionReader.cs @@ -0,0 +1,282 @@ +using System; +using System.Diagnostics; +using System.IO; +using CoreWCF.Runtime; +using CoreWCF.Runtime.Diagnostics; +using CoreWCF; +using CoreWCF.Diagnostics; +using System.Threading.Tasks; + +namespace CoreWCF.Channels +{ + delegate IConnectionOrientedTransportFactorySettings TransportSettingsCallback(Uri via); + delegate void ConnectionClosedCallback(InitialServerConnectionReader connectionReader); + + // Host for a connection that deals with structured close/abort and notifying the owner appropriately + // used for cases where no one else (channel, etc) actually owns the reader + abstract class InitialServerConnectionReader : IDisposable + { + int maxViaSize; + int maxContentTypeSize; + IConnection connection; + Action connectionDequeuedCallback; + ConnectionClosedCallback closedCallback; + bool isClosed; + + protected InitialServerConnectionReader(IConnection connection, ConnectionClosedCallback closedCallback) + : this(connection, closedCallback, + ConnectionOrientedTransportDefaults.MaxViaSize, ConnectionOrientedTransportDefaults.MaxContentTypeSize) + { + } + + protected InitialServerConnectionReader(IConnection connection, ConnectionClosedCallback closedCallback, int maxViaSize, int maxContentTypeSize) + { + if (connection == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("connection"); + } + + if (closedCallback == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("closedCallback"); + } + + this.connection = connection; + this.closedCallback = closedCallback; + this.maxContentTypeSize = maxContentTypeSize; + this.maxViaSize = maxViaSize; + } + + public IConnection Connection + { + get { return connection; } + } + + public Action ConnectionDequeuedCallback + { + get + { + return connectionDequeuedCallback; + } + + set + { + connectionDequeuedCallback = value; + } + } + + public Action GetConnectionDequeuedCallback() + { + Action dequeuedCallback = connectionDequeuedCallback; + connectionDequeuedCallback = null; + return dequeuedCallback; + } + + protected bool IsClosed + { + get { return isClosed; } + } + + protected int MaxContentTypeSize + { + get + { + return maxContentTypeSize; + } + } + + protected int MaxViaSize + { + get + { + return maxViaSize; + } + } + + object ThisLock + { + get { return this; } + } + + // used by the listener to release the connection object so it can be closed at a later time + public void ReleaseConnection() + { + isClosed = true; + connection = null; + } + + // for cached connections -- try to shut down gracefully if possible + public void CloseFromPool(TimeSpan timeout) + { + try + { + Close(timeout); + } + catch (CommunicationException communicationException) + { + DiagnosticUtility.TraceHandledException(communicationException, TraceEventType.Information); + } + catch (TimeoutException timeoutException) + { + DiagnosticUtility.TraceHandledException(timeoutException, TraceEventType.Information); + } + } + + public void Dispose() + { + lock (ThisLock) + { + if (isClosed) + { + return; + } + + isClosed = true; + } + + IConnection connection = this.connection; + if (connection != null) + { + connection.Abort(); + } + + if (connectionDequeuedCallback != null) + { + connectionDequeuedCallback(); + } + } + + protected void Abort() + { + Abort(null); + } + + internal void Abort(Exception e) + { + lock (ThisLock) + { + if (isClosed) + return; + + isClosed = true; + } + + try + { + connection.Abort(); + } + finally + { + if (closedCallback != null) + { + closedCallback(this); + } + + if (connectionDequeuedCallback != null) + { + connectionDequeuedCallback(); + } + } + } + + protected void Close(TimeSpan timeout) + { + lock (ThisLock) + { + if (isClosed) + return; + + isClosed = true; + } + + bool success = false; + try + { + connection.Close(timeout, true); + success = true; + } + finally + { + if (!success) + { + connection.Abort(); + } + + if (closedCallback != null) + { + closedCallback(this); + } + + if (connectionDequeuedCallback != null) + { + connectionDequeuedCallback(); + } + } + } + + internal static void SendFault(IConnection connection, string faultString, byte[] drainBuffer, TimeSpan sendTimeout, int maxRead) + { + EncodedFault encodedFault = new EncodedFault(faultString); + TimeoutHelper timeoutHelper = new TimeoutHelper(sendTimeout); + try + { + connection.Write(encodedFault.EncodedBytes, 0, encodedFault.EncodedBytes.Length, true, timeoutHelper.RemainingTime()); + connection.Shutdown(timeoutHelper.RemainingTime()); + } + catch (CommunicationException e) + { + DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); + connection.Abort(); + return; + } + catch (TimeoutException e) + { + DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); + connection.Abort(); + return; + } + + // make sure we read until EOF or a quota is hit + int read = 0; + int readTotal = 0; + for (;;) + { + try + { + read = connection.Read(drainBuffer, 0, drainBuffer.Length, timeoutHelper.RemainingTime()); + } + catch (CommunicationException e) + { + DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); + connection.Abort(); + return; + } + catch (TimeoutException e) + { + DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); + connection.Abort(); + return; + } + + if (read == 0) + break; + + readTotal += read; + if (readTotal > maxRead || timeoutHelper.RemainingTime() <= TimeSpan.Zero) + { + connection.Abort(); + return; + } + } + + ConnectionUtilities.CloseNoThrow(connection, timeoutHelper.RemainingTime()); + } + + public static async Task UpgradeConnectionAsync(IConnection connection, StreamUpgradeAcceptor upgradeAcceptor, IDefaultCommunicationTimeouts defaultTimeouts) + { + ConnectionStream connectionStream = new ConnectionStream(connection, defaultTimeouts); + Stream stream = await upgradeAcceptor.AcceptUpgradeAsync(connectionStream); + return new StreamConnection(stream, connectionStream); + } + } +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Channels/NetFramingTransportSettings.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/NetFramingTransportSettings.cs new file mode 100644 index 000000000..18307e869 --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/NetFramingTransportSettings.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoreWCF.Channels +{ + internal class NetFramingTransportSettings : ITransportFactorySettings + { + public TimeSpan CloseTimeout { get; set; } + public TimeSpan OpenTimeout { get; set; } + public TimeSpan ReceiveTimeout { get; set; } + public TimeSpan SendTimeout { get; set; } + public bool ManualAddressing { get; set; } + public BufferManager BufferManager { get; set; } + public long MaxReceivedMessageSize { get; set; } + public MessageEncoderFactory MessageEncoderFactory { get; set; } + public MessageVersion MessageVersion => MessageEncoderFactory.MessageVersion; + public IAnonymousUriPrefixMatcher AnonymousUriPrefixMatcher { get; set; } + } +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Channels/OutputChannel.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/OutputChannel.cs new file mode 100644 index 000000000..0e27c80aa --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/OutputChannel.cs @@ -0,0 +1,67 @@ +using System; +using System.Diagnostics; +using CoreWCF.Runtime.Diagnostics; +using CoreWCF; +using CoreWCF.Diagnostics; +using System.Threading.Tasks; +using CoreWCF.Runtime; +using System.Threading; + +namespace CoreWCF.Channels +{ + abstract class OutputChannel : ChannelBase, IOutputChannel + { + protected OutputChannel(ChannelManagerBase manager) + : base(manager) + { + } + + public abstract EndpointAddress RemoteAddress { get; } + public abstract Uri Via { get; } + + public Task SendAsync(Message message) + { + var helper = new TimeoutHelper(DefaultSendTimeout); + return SendAsync(message, helper.GetCancellationToken()); + } + + public Task SendAsync(Message message, CancellationToken token) + { + if (message == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("message"); + + if(token.IsCancellationRequested) + { + return Task.FromCanceled(token); + } + + ThrowIfDisposedOrNotOpen(); + + AddHeadersTo(message); + return OnSendAsync(message, token); + } + + public override T GetProperty() + { + if (typeof(T) == typeof(IOutputChannel)) + { + return (T)(object)this; + } + + T baseProperty = base.GetProperty(); + if (baseProperty != null) + { + return baseProperty; + } + + return default(T); + } + + protected abstract Task OnSendAsync(Message message, CancellationToken token); + + protected virtual void AddHeadersTo(Message message) + { + } + } + +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Channels/QueuedObjectPool.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/QueuedObjectPool.cs new file mode 100644 index 000000000..a11513e94 --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/QueuedObjectPool.cs @@ -0,0 +1,115 @@ +using CoreWCF.Runtime; +using System; +using System.Collections.Generic; + +namespace CoreWCF.Channels +{ + // This is the base object pool class which manages objects in a FIFO queue. The objects are + // created through the provided Func createObjectFunc. The main purpose for this class is + // to get better memory usage for Garbage Collection (GC) when part or all of an object is + // regularly pinned. Constantly creating such objects can cause large Gen0 Heap fragmentation + // and thus high memory usage pressure. The pooled objects are first created in Gen0 heaps and + // would be eventually moved to a more stable segment which would prevent the fragmentation + // to happen. + // + // The objects are created in batches for better localization of the objects. Here are the + // parameters that control the behavior of creation/removal: + // + // batchAllocCount: number of objects to be created at the same time when new objects are needed + // + // createObjectFunc: func delegate that is used to create objects by sub-classes. + // + // maxFreeCount: max number of free objects the queue can store. This is to make sure the memory + // usage is bounded. + // + internal abstract class QueuedObjectPool + { + Queue objectQueue; + bool isClosed; + int batchAllocCount; + int maxFreeCount; + + protected void Initialize(int batchAllocCount, int maxFreeCount) + { + if (batchAllocCount <= 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("batchAllocCount")); + } + + Fx.Assert(batchAllocCount <= maxFreeCount, "batchAllocCount cannot be greater than maxFreeCount"); + this.batchAllocCount = batchAllocCount; + this.maxFreeCount = maxFreeCount; + objectQueue = new Queue(batchAllocCount); + } + + object ThisLock + { + get + { + return objectQueue; + } + } + + public virtual bool Return(T value) + { + lock (ThisLock) + { + if (objectQueue.Count < maxFreeCount && !isClosed) + { + objectQueue.Enqueue(value); + return true; + } + + return false; + } + } + + public T Take() + { + lock (ThisLock) + { + Fx.Assert(!isClosed, "Cannot take an item from closed QueuedObjectPool"); + + if (objectQueue.Count == 0) + { + AllocObjects(); + } + + return objectQueue.Dequeue(); + } + } + + public void Close() + { + lock (ThisLock) + { + foreach (T item in objectQueue) + { + if (item != null) + { + CleanupItem(item); + } + } + + objectQueue.Clear(); + isClosed = true; + } + } + + protected virtual void CleanupItem(T item) + { + } + + protected abstract T Create(); + + void AllocObjects() + { + Fx.Assert(objectQueue.Count == 0, "The object queue must be empty for new allocations"); + for (int i = 0; i < batchAllocCount; i++) + { + objectQueue.Enqueue(Create()); + } + } + } + +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Channels/ReplyChannel.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/ReplyChannel.cs new file mode 100644 index 000000000..f69d672d2 --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/ReplyChannel.cs @@ -0,0 +1,115 @@ +using CoreWCF.Runtime; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace CoreWCF.Channels +{ + class ReplyChannel : InputQueueChannel, IReplyChannel + { + EndpointAddress localAddress; + + public ReplyChannel(ChannelManagerBase channelManager, EndpointAddress localAddress) + : base(channelManager) + { + this.localAddress = localAddress; + } + + public EndpointAddress LocalAddress + { + get { return localAddress; } + } + + public override T GetProperty() + { + if (typeof(T) == typeof(IReplyChannel)) + { + return (T)(object)this; + } + + T baseProperty = base.GetProperty(); + if (baseProperty != null) + { + return baseProperty; + } + + return default(T); + } + + protected override Task OnOpenAsync(CancellationToken token) + { + return TaskHelpers.CompletedOrCanceled(token); + } + + #region static Helpers to convert TryReceiveRequest to ReceiveRequest + internal static async Task HelpReceiveRequestAsync(IReplyChannel channel, CancellationToken token) + { + var result = await channel.TryReceiveRequestAsync(token); + if (result.Success) + { + return result.Result; + } + else + { + // TODO: Fix timeout value + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + ReplyChannel.CreateReceiveRequestTimedOutException(channel, TimeSpan.Zero)); + } + } + + static Exception CreateReceiveRequestTimedOutException(IReplyChannel channel, TimeSpan timeout) + { + if (channel.LocalAddress != null) + { + return new TimeoutException(SR.Format(SR.ReceiveRequestTimedOut, channel.LocalAddress.Uri.AbsoluteUri, timeout)); + } + else + { + return new TimeoutException(SR.Format(SR.ReceiveRequestTimedOutNoLocalAddress, timeout)); + } + } + #endregion + + + public Task ReceiveRequestAsync() + { + return ReceiveRequestAsync(new TimeoutHelper(DefaultReceiveTimeout).GetCancellationToken()); + } + + public Task ReceiveRequestAsync(CancellationToken token) + { + if (token.IsCancellationRequested) + { + return Task.FromCanceled(token); + } + + ThrowPending(); + return ReplyChannel.HelpReceiveRequestAsync(this, token); + } + + public Task> TryReceiveRequestAsync(CancellationToken token) + { + if (token.IsCancellationRequested) + { + return Task.FromCanceled>(token); + } + + ThrowPending(); + return base.DequeueAsync(token); + } + + public Task WaitForRequestAsync(CancellationToken token) + { + if (token.IsCancellationRequested) + { + return Task.FromCanceled(token); + } + + ThrowPending(); + return base.WaitForItemAsync(token); + } + } + +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Channels/ReplyChannelAcceptor.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/ReplyChannelAcceptor.cs new file mode 100644 index 000000000..c4a80e492 --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/ReplyChannelAcceptor.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoreWCF.Channels +{ + class ReplyChannelAcceptor : SingletonChannelAcceptor + { + public ReplyChannelAcceptor(ChannelManagerBase channelManager) + : base(channelManager) + { + } + + protected override ReplyChannel OnCreateChannel() + { + return new ReplyChannel(ChannelManager, null); + } + + protected override void OnTraceMessageReceived(RequestContext requestContext) + { + } + } +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Channels/SessionConnectionReader.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/SessionConnectionReader.cs new file mode 100644 index 000000000..ff7a814a9 --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/SessionConnectionReader.cs @@ -0,0 +1,818 @@ +using System; +using System.Diagnostics; +using System.Net; +using CoreWCF.Runtime; +using System.Runtime.CompilerServices; +using System.Security.Authentication.ExtendedProtection; +using CoreWCF; +using CoreWCF.Description; +using CoreWCF.Diagnostics; +using CoreWCF.Dispatcher; +using CoreWCF.Security; +using System.Threading; +using System.Xml; +using System.Threading.Tasks; + +namespace CoreWCF.Channels +{ + delegate void ServerSessionPreambleCallback(ServerSessionPreambleConnectionReader serverSessionPreambleReader); + delegate void ServerSessionPreambleDemuxCallback(ServerSessionPreambleConnectionReader serverSessionPreambleReader, ConnectionDemuxer connectionDemuxer); + interface ISessionPreambleHandler + { + void HandleServerSessionPreamble(ServerSessionPreambleConnectionReader serverSessionPreambleReader, + ConnectionDemuxer connectionDemuxer); + } + + // reads everything we need in order to match a channel (i.e. up to the via) + class ServerSessionPreambleConnectionReader : InitialServerConnectionReader + { + ServerSessionDecoder decoder; + byte[] connectionBuffer; + int offset; + int size; + TransportSettingsCallback transportSettingsCallback; + ServerSessionPreambleCallback callback; + IConnectionOrientedTransportFactorySettings settings; + Uri via; + Action viaDelegate; + TimeoutHelper receiveTimeoutHelper; + IConnection rawConnection; + + public ServerSessionPreambleConnectionReader(IConnection connection, Action connectionDequeuedCallback, + long streamPosition, int offset, int size, TransportSettingsCallback transportSettingsCallback, + ConnectionClosedCallback closedCallback, ServerSessionPreambleCallback callback) + : base(connection, closedCallback) + { + rawConnection = connection; + decoder = new ServerSessionDecoder(streamPosition, MaxViaSize, MaxContentTypeSize); + this.offset = offset; + this.size = size; + this.transportSettingsCallback = transportSettingsCallback; + this.callback = callback; + ConnectionDequeuedCallback = connectionDequeuedCallback; + } + + public int BufferOffset + { + get { return offset; } + } + + public int BufferSize + { + get { return size; } + } + + public ServerSessionDecoder Decoder + { + get { return decoder; } + } + + public IConnection RawConnection + { + get { return rawConnection; } + } + + public Uri Via + { + get { return via; } + } + + TimeSpan GetRemainingTimeout() + { + return receiveTimeoutHelper.RemainingTime(); + } + + async void ContinueReadingAsync() + { + bool success = false; + try + { + for (;;) + { + if (size == 0) + { + offset = 0; + size = await Connection.ReadAsync(0, connectionBuffer.Length, GetRemainingTimeout()); + if (size == 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(decoder.CreatePrematureEOFException()); + } + } + + int bytesDecoded = decoder.Decode(connectionBuffer, offset, size); + if (bytesDecoded > 0) + { + offset += bytesDecoded; + size -= bytesDecoded; + } + + if (decoder.CurrentState == ServerSessionDecoder.State.PreUpgradeStart) + { + via = decoder.Via; + if(await Connection.ValidateAsync(via)) + { + settings = transportSettingsCallback(via); + + if (settings == null) + { + EndpointNotFoundException e = new EndpointNotFoundException(SR.Format(SR.EndpointNotFound, decoder.Via)); + DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); + + SendFault(FramingEncodingString.EndpointNotFoundFault); + // This goes through the failure path (Abort) even though it doesn't throw. + return; + } + + // we have enough information to hand off to a channel. Our job is done + callback(this); + } + + break; //exit loop, set success=true; + } + } + success = true; + } + catch (CommunicationException exception) + { + DiagnosticUtility.TraceHandledException(exception, TraceEventType.Information); + } + catch (TimeoutException exception) + { + DiagnosticUtility.TraceHandledException(exception, TraceEventType.Information); + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + if (!ExceptionHandlerHelper.HandleTransportExceptionHelper(e)) + { + throw; + } + // containment -- all exceptions abort the reader, no additional containment action necessary + } + finally + { + if (!success) + { + Abort(); + } + } + } + + public void SendFault(string faultString) + { + InitialServerConnectionReader.SendFault( + Connection, faultString, connectionBuffer, GetRemainingTimeout(), + TransportDefaults.MaxDrainSize); + base.Close(GetRemainingTimeout()); + } + + public void StartReading(Action viaDelegate, TimeSpan receiveTimeout) + { + // TODO: It looks like viaDelegate is only used for port sharing, remove + this.viaDelegate = viaDelegate; + receiveTimeoutHelper = new TimeoutHelper(receiveTimeout); + connectionBuffer = Connection.AsyncReadBuffer; + ContinueReadingAsync(); + } + + public IDuplexSessionChannel CreateDuplexSessionChannel(ConnectionOrientedTransportChannelListener channelListener, EndpointAddress localAddress, bool exposeConnectionProperty, ConnectionDemuxer connectionDemuxer) + { + return new ServerFramingDuplexSessionChannel(channelListener, this, localAddress, exposeConnectionProperty, connectionDemuxer); + } + + class ServerFramingDuplexSessionChannel : FramingDuplexSessionChannel + { + ConnectionOrientedTransportChannelListener channelListener; + ConnectionDemuxer connectionDemuxer; + ServerSessionConnectionReader sessionReader; + ServerSessionDecoder decoder; + IConnection rawConnection; + byte[] connectionBuffer; + int offset; + int size; + StreamUpgradeAcceptor upgradeAcceptor; + IStreamUpgradeChannelBindingProvider channelBindingProvider; + + public ServerFramingDuplexSessionChannel(ConnectionOrientedTransportChannelListener channelListener, ServerSessionPreambleConnectionReader preambleReader, + EndpointAddress localAddress, bool exposeConnectionProperty, ConnectionDemuxer connectionDemuxer) + : base(channelListener, localAddress, preambleReader.Via, exposeConnectionProperty) + { + this.channelListener = channelListener; + this.connectionDemuxer = connectionDemuxer; + Connection = preambleReader.Connection; + decoder = preambleReader.Decoder; + connectionBuffer = preambleReader.connectionBuffer; + offset = preambleReader.BufferOffset; + size = preambleReader.BufferSize; + rawConnection = preambleReader.RawConnection; + StreamUpgradeProvider upgrade = channelListener.Upgrade; + if (upgrade != null) + { + channelBindingProvider = upgrade.GetProperty(); + upgradeAcceptor = upgrade.CreateUpgradeAcceptor(); + } + } + + protected override void ReturnConnectionIfNecessary(bool abort, CancellationToken token) + { + var timeout = TimeoutHelper.GetOriginalTimeout(token); + IConnection localConnection = null; + if (sessionReader != null) + { + lock (ThisLock) + { + localConnection = sessionReader.GetRawConnection(); + } + } + + if (localConnection != null) + { + if (abort) + { + localConnection.Abort(); + } + else + { + connectionDemuxer.ReuseConnection(localConnection, timeout); + } + connectionDemuxer = null; + } + } + + public override T GetProperty() + { + if (typeof(T) == typeof(IChannelBindingProvider)) + { + return (T)(object)channelBindingProvider; + } + + return base.GetProperty(); + } + + protected override void PrepareMessage(Message message) + { + channelListener.RaiseMessageReceived(); + base.PrepareMessage(message); + } + + // perform security handshake and ACK connection + protected override async Task OnOpenAsync(CancellationToken token) + { + bool success = false; + try + { + // TODO: Sort out the timeout here + var timeoutHelper = new TimeoutHelper(TimeSpan.FromSeconds(30)); + // first validate our content type + ValidateContentType(ref timeoutHelper); + + // next read any potential upgrades and finish consuming the preamble + for (;;) + { + if (size == 0) + { + offset = 0; + size = await Connection.ReadAsync(0, connectionBuffer.Length, timeoutHelper.RemainingTime()); + if (size == 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(decoder.CreatePrematureEOFException()); + } + } + + for (;;) + { + DecodeBytes(); + switch (decoder.CurrentState) + { + case ServerSessionDecoder.State.UpgradeRequest: + ProcessUpgradeRequest(ref timeoutHelper); + + // accept upgrade + await Connection.WriteAsync(ServerSessionEncoder.UpgradeResponseBytes, 0, ServerSessionEncoder.UpgradeResponseBytes.Length, true, timeoutHelper.RemainingTime()); + + IConnection connectionToUpgrade = Connection; + if (size > 0) + { + // TODO: Switch to using PreReadConnection constructor which doesn't take a buffer. This is currently causing an extra buffer allocation. + connectionToUpgrade = new PreReadConnection(connectionToUpgrade, connectionBuffer, offset, size); + } + + try + { + Connection = await InitialServerConnectionReader.UpgradeConnectionAsync(connectionToUpgrade, upgradeAcceptor, this); + + if (channelBindingProvider != null && channelBindingProvider.IsChannelBindingSupportEnabled) + { + SetChannelBinding(channelBindingProvider.GetChannelBinding(upgradeAcceptor, ChannelBindingKind.Endpoint)); + } + + connectionBuffer = Connection.AsyncReadBuffer; + } + catch (Exception exception) + { + if (Fx.IsFatal(exception)) + throw; + + // Audit Authentication Failure + WriteAuditFailure(upgradeAcceptor as StreamSecurityUpgradeAcceptor, exception); + throw; + } + break; + + case ServerSessionDecoder.State.Start: + SetupSecurityIfNecessary(); + + // we've finished the preamble. Ack and return. + await Connection.WriteAsync(ServerSessionEncoder.AckResponseBytes, 0, + ServerSessionEncoder.AckResponseBytes.Length, true, timeoutHelper.RemainingTime()); + SetupSessionReader(); + success = true; + return; + } + + if (size == 0) + break; + } + } + } + finally + { + if (!success) + { + Connection.Abort(); + } + } + } + + void AcceptUpgradedConnection(IConnection upgradedConnection) + { + Connection = upgradedConnection; + + if (channelBindingProvider != null && channelBindingProvider.IsChannelBindingSupportEnabled) + { + SetChannelBinding(channelBindingProvider.GetChannelBinding(upgradeAcceptor, ChannelBindingKind.Endpoint)); + } + + connectionBuffer = Connection.AsyncReadBuffer; + } + + void ValidateContentType(ref TimeoutHelper timeoutHelper) + { + MessageEncoder = channelListener.MessageEncoderFactory.CreateSessionEncoder(); + + if (!MessageEncoder.IsContentTypeSupported(decoder.ContentType)) + { + SendFault(FramingEncodingString.ContentTypeInvalidFault, ref timeoutHelper); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ProtocolException(SR.Format( + SR.ContentTypeMismatch, decoder.ContentType, MessageEncoder.ContentType))); + } + + // TODO: Support ICompressedMessageEncoder + //ICompressedMessageEncoder compressedMessageEncoder = this.MessageEncoder as ICompressedMessageEncoder; + //if (compressedMessageEncoder != null && compressedMessageEncoder.CompressionEnabled) + //{ + // compressedMessageEncoder.SetSessionContentType(this.decoder.ContentType); + //} + } + + void DecodeBytes() + { + int bytesDecoded = decoder.Decode(connectionBuffer, offset, size); + if (bytesDecoded > 0) + { + offset += bytesDecoded; + size -= bytesDecoded; + } + } + + void ProcessUpgradeRequest(ref TimeoutHelper timeoutHelper) + { + if (upgradeAcceptor == null) + { + SendFault(FramingEncodingString.UpgradeInvalidFault, ref timeoutHelper); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new ProtocolException(SR.Format(SR.UpgradeRequestToNonupgradableService, decoder.Upgrade))); + } + + if (!upgradeAcceptor.CanUpgrade(decoder.Upgrade)) + { + SendFault(FramingEncodingString.UpgradeInvalidFault, ref timeoutHelper); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new ProtocolException(SR.Format(SR.UpgradeProtocolNotSupported, decoder.Upgrade))); + } + } + + void SendFault(string faultString, ref TimeoutHelper timeoutHelper) + { + InitialServerConnectionReader.SendFault(Connection, faultString, + connectionBuffer, timeoutHelper.RemainingTime(), TransportDefaults.MaxDrainSize); + } + + void SetupSecurityIfNecessary() + { + StreamSecurityUpgradeAcceptor securityUpgradeAcceptor = upgradeAcceptor as StreamSecurityUpgradeAcceptor; + if (securityUpgradeAcceptor != null) + { + RemoteSecurity = securityUpgradeAcceptor.GetRemoteSecurity(); + + if (RemoteSecurity == null) + { + Exception securityFailedException = new ProtocolException( + SR.Format(SR.RemoteSecurityNotNegotiatedOnStreamUpgrade, Via)); + WriteAuditFailure(securityUpgradeAcceptor, securityFailedException); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(securityFailedException); + } + else + { + // Audit Authentication Success + // WriteAuditEvent(securityUpgradeAcceptor, AuditLevel.Success, null); + } + } + } + + void SetupSessionReader() + { + sessionReader = new ServerSessionConnectionReader(this); + base.SetMessageSource(sessionReader); + } + + #region Transport Security Auditing + void WriteAuditFailure(StreamSecurityUpgradeAcceptor securityUpgradeAcceptor, Exception exception) + { + //try + //{ + // WriteAuditEvent(securityUpgradeAcceptor, AuditLevel.Failure, exception); + //} + //catch (Exception auditException) + //{ + // if (Fx.IsFatal(auditException)) + // { + // throw; + // } + + // DiagnosticUtility.TraceHandledException(auditException, TraceEventType.Error); + //} + } + + //void WriteAuditEvent(StreamSecurityUpgradeAcceptor securityUpgradeAcceptor, AuditLevel auditLevel, Exception exception) + //{ + // if ((this.channelListener.AuditBehavior.MessageAuthenticationAuditLevel & auditLevel) != auditLevel) + // { + // return; + // } + + // if (securityUpgradeAcceptor == null) + // { + // return; + // } + + // String primaryIdentity = String.Empty; + // SecurityMessageProperty clientSecurity = securityUpgradeAcceptor.GetRemoteSecurity(); + // if (clientSecurity != null) + // { + // primaryIdentity = GetIdentityNameFromContext(clientSecurity); + // } + + // ServiceSecurityAuditBehavior auditBehavior = this.channelListener.AuditBehavior; + + // if (auditLevel == AuditLevel.Success) + // { + // SecurityAuditHelper.WriteTransportAuthenticationSuccessEvent(auditBehavior.AuditLogLocation, + // auditBehavior.SuppressAuditFailure, null, this.LocalVia, primaryIdentity); + // } + // else + // { + // SecurityAuditHelper.WriteTransportAuthenticationFailureEvent(auditBehavior.AuditLogLocation, + // auditBehavior.SuppressAuditFailure, null, this.LocalVia, primaryIdentity, exception); + // } + //} + + //[MethodImpl(MethodImplOptions.NoInlining)] + //static string GetIdentityNameFromContext(SecurityMessageProperty clientSecurity) + //{ + // return SecurityUtils.GetIdentityNamesFromContext( + // clientSecurity.ServiceSecurityContext.AuthorizationContext); + //} + #endregion + + class ServerSessionConnectionReader : SessionConnectionReader + { + ServerSessionDecoder decoder; + int maxBufferSize; + BufferManager bufferManager; + MessageEncoder messageEncoder; + string contentType; + IConnection rawConnection; + + public ServerSessionConnectionReader(ServerFramingDuplexSessionChannel channel) + : base(channel.Connection, channel.rawConnection, channel.offset, channel.size, channel.RemoteSecurity) + { + decoder = channel.decoder; + contentType = decoder.ContentType; + maxBufferSize = channel.channelListener.MaxBufferSize; + bufferManager = channel.channelListener.BufferManager; + messageEncoder = channel.MessageEncoder; + rawConnection = channel.rawConnection; + } + + protected override void EnsureDecoderAtEof() + { + if (!(decoder.CurrentState == ServerSessionDecoder.State.End || decoder.CurrentState == ServerSessionDecoder.State.EnvelopeEnd)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(decoder.CreatePrematureEOFException()); + } + } + + protected override Message DecodeMessage(byte[] buffer, ref int offset, ref int size, ref bool isAtEof, TimeSpan timeout) + { + while (!isAtEof && size > 0) + { + int bytesRead = decoder.Decode(buffer, offset, size); + if (bytesRead > 0) + { + if (EnvelopeBuffer != null) + { + if (!object.ReferenceEquals(buffer, EnvelopeBuffer)) + { + System.Buffer.BlockCopy(buffer, offset, EnvelopeBuffer, EnvelopeOffset, bytesRead); + } + EnvelopeOffset += bytesRead; + } + + offset += bytesRead; + size -= bytesRead; + } + + switch (decoder.CurrentState) + { + case ServerSessionDecoder.State.EnvelopeStart: + int envelopeSize = decoder.EnvelopeSize; + if (envelopeSize > maxBufferSize) + { + base.SendFault(FramingEncodingString.MaxMessageSizeExceededFault, timeout); + + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + MaxMessageSizeStream.CreateMaxReceivedMessageSizeExceededException(maxBufferSize)); + } + EnvelopeBuffer = bufferManager.TakeBuffer(envelopeSize); + EnvelopeOffset = 0; + EnvelopeSize = envelopeSize; + break; + + case ServerSessionDecoder.State.EnvelopeEnd: + if (EnvelopeBuffer != null) + { + Message message = null; + + try + { + message = messageEncoder.ReadMessage(new ArraySegment(EnvelopeBuffer, 0, EnvelopeSize), bufferManager, contentType); + } + catch (XmlException xmlException) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new ProtocolException(SR.Format(SR.MessageXmlProtocolError), xmlException)); + } + + EnvelopeBuffer = null; + return message; + } + break; + + case ServerSessionDecoder.State.End: + isAtEof = true; + break; + } + } + + return null; + } + + protected override void PrepareMessage(Message message) + { + base.PrepareMessage(message); + + IPEndPoint remoteEndPoint = rawConnection.RemoteIPEndPoint; + // pipes will return null + if (remoteEndPoint != null) + { + RemoteEndpointMessageProperty remoteEndpointProperty = new RemoteEndpointMessageProperty(remoteEndPoint); + message.Properties.Add(RemoteEndpointMessageProperty.Name, remoteEndpointProperty); + } + } + } + } + } + + abstract class SessionConnectionReader : IMessageSource + { + bool isAtEOF; + bool usingAsyncReadBuffer; + IConnection connection; + byte[] buffer; + int offset; + int size; + byte[] envelopeBuffer; + int envelopeOffset; + int envelopeSize; + bool readIntoEnvelopeBuffer; + Message pendingMessage; + Exception pendingException; + SecurityMessageProperty security; + // Raw connection that we will revert to after end handshake + IConnection rawConnection; + + protected SessionConnectionReader(IConnection connection, IConnection rawConnection, + int offset, int size, SecurityMessageProperty security) + { + this.offset = offset; + this.size = size; + if (size > 0) + { + buffer = connection.AsyncReadBuffer; + } + this.connection = connection; + this.rawConnection = rawConnection; + this.security = security; + } + + Message DecodeMessage(TimeSpan timeout) + { + if (!readIntoEnvelopeBuffer) + { + return DecodeMessage(buffer, ref offset, ref size, ref isAtEOF, timeout); + } + else + { + // decode from the envelope buffer + int dummyOffset = envelopeOffset; + return DecodeMessage(envelopeBuffer, ref dummyOffset, ref size, ref isAtEOF, timeout); + } + } + + protected abstract Message DecodeMessage(byte[] buffer, ref int offset, ref int size, ref bool isAtEof, TimeSpan timeout); + + protected byte[] EnvelopeBuffer + { + get { return envelopeBuffer; } + set { envelopeBuffer = value; } + } + + protected int EnvelopeOffset + { + get { return envelopeOffset; } + set { envelopeOffset = value; } + } + + protected int EnvelopeSize + { + get { return envelopeSize; } + set { envelopeSize = value; } + } + + public IConnection GetRawConnection() + { + IConnection result = null; + if (rawConnection != null) + { + result = rawConnection; + rawConnection = null; + if (size > 0) + { + PreReadConnection preReadConnection = result as PreReadConnection; + if (preReadConnection != null) // make sure we don't keep wrapping + { + preReadConnection.AddPreReadData(buffer, offset, size); + } + else + { + result = new PreReadConnection(result, buffer, offset, size); + } + } + } + + return result; + } + + public async Task ReceiveAsync(CancellationToken token) + { + Message message = GetPendingMessage(); + + if (message != null) + { + return message; + } + + TimeoutHelper timeoutHelper = new TimeoutHelper(TimeoutHelper.GetOriginalTimeout(token)); + while (true) + { + if (isAtEOF) + { + return null; + } + + if (size > 0) + { + message = DecodeMessage(timeoutHelper.RemainingTime()); + + if (message != null) + { + PrepareMessage(message); + return message; + } + else if (isAtEOF) // could have read the END record under DecodeMessage + { + return null; + } + } + + if (size != 0) + { + throw Fx.AssertAndThrow("Receive: DecodeMessage() should consume the outstanding buffer or return a message."); + } + + if (!usingAsyncReadBuffer) + { + buffer = connection.AsyncReadBuffer; + usingAsyncReadBuffer = true; + } + + int bytesRead = await connection.ReadAsync(0, buffer.Length, timeoutHelper.RemainingTime()); + + HandleReadComplete(bytesRead, false); + } + } + + Message GetPendingMessage() + { + if (pendingException != null) + { + Exception exception = pendingException; + pendingException = null; + throw TraceUtility.ThrowHelperError(exception, pendingMessage); + } + + if (pendingMessage != null) + { + Message message = pendingMessage; + pendingMessage = null; + return message; + } + + return null; + } + + public async Task WaitForMessageAsync(CancellationToken token) + { + try + { + Message message = await ReceiveAsync(token); + pendingMessage = message; + return true; + } + catch (TimeoutException e) + { + pendingException = e; + DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); + return false; + } + } + + protected abstract void EnsureDecoderAtEof(); + + void HandleReadComplete(int bytesRead, bool readIntoEnvelopeBuffer) + { + this.readIntoEnvelopeBuffer = readIntoEnvelopeBuffer; + + if (bytesRead == 0) + { + EnsureDecoderAtEof(); + isAtEOF = true; + } + else + { + offset = 0; + size = bytesRead; + } + } + + protected virtual void PrepareMessage(Message message) + { + if (security != null) + { + message.Properties.Security = (SecurityMessageProperty)security.CreateCopy(); + } + } + + protected void SendFault(string faultString, TimeSpan timeout) + { + byte[] drainBuffer = new byte[128]; + InitialServerConnectionReader.SendFault( + connection, faultString, drainBuffer, timeout, + TransportDefaults.MaxDrainSize); + } + } +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Channels/SingletonChannelAcceptor.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/SingletonChannelAcceptor.cs new file mode 100644 index 000000000..c2d4300c0 --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/SingletonChannelAcceptor.cs @@ -0,0 +1,224 @@ +using CoreWCF.Runtime; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace CoreWCF.Channels +{ + abstract class SingletonChannelAcceptor + : InputQueueChannelAcceptor + where ChannelInterfaceType : class, IChannel + where TChannel : /*ChannelInterfaceType,*/ InputQueueChannel + where QueueItemType : class, IDisposable + { + TChannel currentChannel; + object currentChannelLock = new object(); + static Action onInvokeDequeuedCallback; + + public SingletonChannelAcceptor(ChannelManagerBase channelManager) + : base(channelManager) + { + } + + public override Task AcceptChannelAsync(CancellationToken token) + { + EnsureChannelAvailable(); + return base.AcceptChannelAsync(token); + } + + protected TChannel GetCurrentChannel() + { + return currentChannel; + } + + TChannel EnsureChannelAvailable() + { + bool channelCreated = false; + TChannel newChannel; + + if ((newChannel = currentChannel) == null) + { + lock (currentChannelLock) + { + if (IsDisposed) + { + return null; + } + + if ((newChannel = currentChannel) == null) + { + newChannel = OnCreateChannel(); + newChannel.Closed += OnChannelClosed; + currentChannel = newChannel; + channelCreated = true; + } + } + } + + if (channelCreated) + { + EnqueueAndDispatch((ChannelInterfaceType)(object)newChannel); + } + + return newChannel; + } + + protected abstract TChannel OnCreateChannel(); + protected abstract void OnTraceMessageReceived(QueueItemType item); + + public void DispatchItems() + { + TChannel channel = EnsureChannelAvailable(); + if (channel != null) + { + channel.Dispatch(); + } + } + + public void Enqueue(QueueItemType item) + { + Enqueue(item, null); + } + + public void Enqueue(QueueItemType item, Action dequeuedCallback) + { + Enqueue(item, dequeuedCallback, true); + } + + public void Enqueue(QueueItemType item, Action dequeuedCallback, bool canDispatchOnThisThread) + { + TChannel channel = EnsureChannelAvailable(); + + if (channel != null) + { + channel.EnqueueAndDispatch(item, dequeuedCallback, canDispatchOnThisThread); + } + else + { + InvokeDequeuedCallback(dequeuedCallback, canDispatchOnThisThread); + item.Dispose(); + } + } + + public void Enqueue(Exception exception, Action dequeuedCallback) + { + Enqueue(exception, dequeuedCallback, true); + } + + public void Enqueue(Exception exception, Action dequeuedCallback, bool canDispatchOnThisThread) + { + TChannel channel = EnsureChannelAvailable(); + + if (channel != null) + { + channel.EnqueueAndDispatch(exception, dequeuedCallback, canDispatchOnThisThread); + } + else + { + InvokeDequeuedCallback(dequeuedCallback, canDispatchOnThisThread); + } + } + + public bool EnqueueWithoutDispatch(QueueItemType item, Action dequeuedCallback) + { + TChannel channel = EnsureChannelAvailable(); + + if (channel != null) + { + return channel.EnqueueWithoutDispatch(item, dequeuedCallback); + } + else + { + InvokeDequeuedCallback(dequeuedCallback, false); + item.Dispose(); + return false; + } + } + + public override bool EnqueueWithoutDispatch(Exception exception, Action dequeuedCallback) + { + TChannel channel = EnsureChannelAvailable(); + + if (channel != null) + { + return channel.EnqueueWithoutDispatch(exception, dequeuedCallback); + } + else + { + InvokeDequeuedCallback(dequeuedCallback, false); + return false; + } + } + + public void EnqueueAndDispatch(QueueItemType item, Action dequeuedCallback, bool canDispatchOnThisThread) + { + TChannel channel = EnsureChannelAvailable(); + + if (channel != null) + { + channel.EnqueueAndDispatch(item, dequeuedCallback, canDispatchOnThisThread); + } + else + { + InvokeDequeuedCallback(dequeuedCallback, canDispatchOnThisThread); + item.Dispose(); + } + } + + public override void EnqueueAndDispatch(Exception exception, Action dequeuedCallback, bool canDispatchOnThisThread) + { + TChannel channel = EnsureChannelAvailable(); + + if (channel != null) + { + channel.EnqueueAndDispatch(exception, dequeuedCallback, canDispatchOnThisThread); + } + else + { + InvokeDequeuedCallback(dequeuedCallback, canDispatchOnThisThread); + } + } + + protected void OnChannelClosed(object sender, EventArgs args) + { + IChannel channel = (IChannel)sender; + lock (currentChannelLock) + { + if (channel == currentChannel) + { + currentChannel = null; + } + } + } + + static void InvokeDequeuedCallback(Action dequeuedCallback, bool canDispatchOnThisThread) + { + if (dequeuedCallback != null) + { + if (canDispatchOnThisThread) + { + dequeuedCallback(); + return; + } + + if (onInvokeDequeuedCallback == null) + { + onInvokeDequeuedCallback = OnInvokeDequeuedCallback; + } + + ActionItem.Schedule(onInvokeDequeuedCallback, dequeuedCallback); + } + } + + static void OnInvokeDequeuedCallback(object state) + { + Fx.Assert(state != null, "SingletonChannelAcceptor.OnInvokeDequeuedCallback: (state != null)"); + + Action dequeuedCallback = (Action)state; + dequeuedCallback(); + } + } + +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Channels/SingletonConnectionReader.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/SingletonConnectionReader.cs new file mode 100644 index 000000000..062ae0932 --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/SingletonConnectionReader.cs @@ -0,0 +1,1216 @@ +using CoreWCF.Runtime; +using CoreWCF; +using CoreWCF.Description; +using CoreWCF.Diagnostics; +using CoreWCF.Dispatcher; +using CoreWCF.Security; +using System; +using System.IO; +using System.Net; +using System.Runtime.CompilerServices; +using System.Security.Authentication.ExtendedProtection; +using System.Threading; +using System.Xml; +using System.Threading.Tasks; +using System.Diagnostics; + +namespace CoreWCF.Channels +{ + delegate void ServerSingletonPreambleCallback(ServerSingletonPreambleConnectionReader serverSingletonPreambleReader); + delegate ISingletonChannelListener SingletonPreambleDemuxCallback(ServerSingletonPreambleConnectionReader serverSingletonPreambleReader); + interface ISingletonChannelListener + { + TimeSpan ReceiveTimeout { get; } + // TODO: This is a synchronous method to dispatch a request. This might cause a method to synchronously block + void ReceiveRequest(RequestContext requestContext, Action callback, bool canDispatchOnThisThread); + } + + class ServerSingletonPreambleConnectionReader : InitialServerConnectionReader + { + ServerSingletonDecoder decoder; + ServerSingletonPreambleCallback callback; + IConnectionOrientedTransportFactorySettings transportSettings; + TransportSettingsCallback transportSettingsCallback; + SecurityMessageProperty security; + Uri via; + IConnection rawConnection; + byte[] connectionBuffer; + bool isReadPending; + int offset; + int size; + TimeoutHelper receiveTimeoutHelper; + Action viaDelegate; + ChannelBinding channelBindingToken; + + public ServerSingletonPreambleConnectionReader(IConnection connection, Action connectionDequeuedCallback, + long streamPosition, int offset, int size, TransportSettingsCallback transportSettingsCallback, + ConnectionClosedCallback closedCallback, ServerSingletonPreambleCallback callback) + : base(connection, closedCallback) + { + decoder = new ServerSingletonDecoder(streamPosition, MaxViaSize, MaxContentTypeSize); + this.offset = offset; + this.size = size; + this.callback = callback; + this.transportSettingsCallback = transportSettingsCallback; + rawConnection = connection; + ConnectionDequeuedCallback = connectionDequeuedCallback; + + } + + public ChannelBinding ChannelBinding + { + get + { + return channelBindingToken; + } + } + + public int BufferOffset + { + get { return offset; } + } + + public int BufferSize + { + get { return size; } + } + + public ServerSingletonDecoder Decoder + { + get { return decoder; } + } + + public IConnection RawConnection + { + get { return rawConnection; } + } + + public Uri Via + { + get { return via; } + } + + public IConnectionOrientedTransportFactorySettings TransportSettings + { + get { return transportSettings; } + } + + public SecurityMessageProperty Security + { + get { return security; } + } + + TimeSpan GetRemainingTimeout() + { + return receiveTimeoutHelper.RemainingTime(); + } + + async Task ReadAndDispatchAsync() + { + bool success = false; + try + { + while ((size > 0 || !isReadPending) && !IsClosed) + { + if (size == 0) + { + isReadPending = true; + size = await Connection.ReadAsync(0, connectionBuffer.Length, GetRemainingTimeout()); + offset = 0; + isReadPending = false; + if (size == 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(decoder.CreatePrematureEOFException()); + } + } + + int bytesRead = decoder.Decode(connectionBuffer, offset, size); + if (bytesRead > 0) + { + offset += bytesRead; + size -= bytesRead; + } + + if (decoder.CurrentState == ServerSingletonDecoder.State.PreUpgradeStart) + { + via = decoder.Via; + var validated = await Connection.ValidateAsync(via); + + if (!ContinuePostValidationProcessing()) + { + // This goes through the failure path (Abort) even though it doesn't throw. + return; + } + break; //exit loop, set success=true; + } + } + success = true; + } + catch (CommunicationException exception) + { + DiagnosticUtility.TraceHandledException(exception, TraceEventType.Information); + } + catch (TimeoutException exception) + { + DiagnosticUtility.TraceHandledException(exception, TraceEventType.Information); + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + if (!ExceptionHandlerHelper.HandleTransportExceptionHelper(e)) + { + throw; + } + + // containment -- we abort ourselves for any error, no extra containment needed + } + finally + { + if (!success) + { + Abort(); + } + } + } + + //returns false if the connection should be aborted + bool ContinuePostValidationProcessing() + { + if (viaDelegate != null) + { + try + { + viaDelegate(via); + } + catch (ServiceActivationException e) + { + DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); + // return fault and close connection + SendFault(FramingEncodingString.ServiceActivationFailedFault); + return true; + } + } + + + transportSettings = transportSettingsCallback(via); + + if (transportSettings == null) + { + EndpointNotFoundException e = new EndpointNotFoundException(SR.Format(SR.EndpointNotFound, decoder.Via)); + DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); + // return fault and close connection + SendFault(FramingEncodingString.EndpointNotFoundFault); + return false; + } + + // we have enough information to hand off to a channel. Our job is done + callback(this); + return true; + } + + public void SendFault(string faultString) + { + SendFault(faultString, ref receiveTimeoutHelper); + } + + void SendFault(string faultString, ref TimeoutHelper timeoutHelper) + { + InitialServerConnectionReader.SendFault(Connection, faultString, + connectionBuffer, timeoutHelper.RemainingTime(), TransportDefaults.MaxDrainSize); + } + + public async Task CompletePreambleAsync(TimeSpan timeout) + { + var timeoutHelper = new TimeoutHelper(timeout); + var parent = this; + if (!transportSettings.MessageEncoderFactory.Encoder.IsContentTypeSupported(Decoder.ContentType)) + { + SendFault(FramingEncodingString.ContentTypeInvalidFault, ref timeoutHelper); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ProtocolException(SR.Format( + SR.ContentTypeMismatch, Decoder.ContentType, parent.transportSettings.MessageEncoderFactory.Encoder.ContentType))); + } + + IStreamUpgradeChannelBindingProvider channelBindingProvider = null; + StreamUpgradeAcceptor upgradeAcceptor = null; + if (transportSettings.Upgrade != null) + { + channelBindingProvider = transportSettings.Upgrade.GetProperty(); + upgradeAcceptor = transportSettings.Upgrade.CreateUpgradeAcceptor(); + } + + var currentConnection = Connection; + UpgradeState upgradeState = UpgradeState.None; + + while (true) + { + if (size == 0 && CanReadAndDecode(upgradeState)) + { + size = await currentConnection.ReadAsync(0, connectionBuffer.Length, timeoutHelper.RemainingTime()); + if (size == 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Decoder.CreatePrematureEOFException()); + } + } + + while(true) + { + if (CanReadAndDecode(upgradeState)) + { + int bytesRead = Decoder.Decode(connectionBuffer, offset, size); + if (bytesRead > 0) + { + offset += bytesRead; + size -= bytesRead; + } + } + + switch (Decoder.CurrentState) + { + case ServerSingletonDecoder.State.UpgradeRequest: + switch (upgradeState) + { + case UpgradeState.None: + //change the state so that we don't read/decode until it is safe + ChangeUpgradeState(ref upgradeState, UpgradeState.VerifyingUpgradeRequest); + break; + case UpgradeState.VerifyingUpgradeRequest: + if (upgradeAcceptor == null) + { + SendFault(FramingEncodingString.UpgradeInvalidFault, ref timeoutHelper); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new ProtocolException(SR.Format(SR.UpgradeRequestToNonupgradableService, Decoder.Upgrade))); + } + + if (!upgradeAcceptor.CanUpgrade(Decoder.Upgrade)) + { + SendFault(FramingEncodingString.UpgradeInvalidFault, ref timeoutHelper); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ProtocolException(SR.Format(SR.UpgradeProtocolNotSupported, Decoder.Upgrade))); + } + + ChangeUpgradeState(ref upgradeState, UpgradeState.WritingUpgradeAck); + // accept upgrade + await currentConnection.WriteAsync(ServerSingletonEncoder.UpgradeResponseBytes, 0, ServerSingletonEncoder.UpgradeResponseBytes.Length, + true, timeoutHelper.RemainingTime()); + ChangeUpgradeState(ref upgradeState, UpgradeState.UpgradeAckSent); + break; + case UpgradeState.UpgradeAckSent: + IConnection connectionToUpgrade = currentConnection; + if (size > 0) + { + connectionToUpgrade = new PreReadConnection(connectionToUpgrade, connectionBuffer, offset, size); + } + ChangeUpgradeState(ref upgradeState, UpgradeState.BeginUpgrade); + break; + case UpgradeState.BeginUpgrade: + try + { + currentConnection = await InitialServerConnectionReader.UpgradeConnectionAsync(currentConnection, upgradeAcceptor, transportSettings); + connectionBuffer = currentConnection.AsyncReadBuffer; + if (channelBindingProvider != null && + channelBindingProvider.IsChannelBindingSupportEnabled && + channelBindingToken == null)//first one wins in the case of multiple upgrades. + { + channelBindingToken = channelBindingProvider.GetChannelBinding(upgradeAcceptor, ChannelBindingKind.Endpoint); + } + ChangeUpgradeState(ref upgradeState, UpgradeState.EndUpgrade); + ChangeUpgradeState(ref upgradeState, UpgradeState.UpgradeComplete); + } + catch (Exception exception) + { + if (Fx.IsFatal(exception)) + throw; + + WriteAuditFailure(upgradeAcceptor as StreamSecurityUpgradeAcceptor, exception); + throw; + } + break; + case UpgradeState.UpgradeComplete: + //Client is doing more than one upgrade, reset the state + ChangeUpgradeState(ref upgradeState, UpgradeState.VerifyingUpgradeRequest); + break; + } + break; + case ServerSingletonDecoder.State.Start: + SetupSecurityIfNecessary(upgradeAcceptor); + + if (upgradeState == UpgradeState.UpgradeComplete //We have done at least one upgrade, but we are now done. + || upgradeState == UpgradeState.None)//no upgrade, just send the preample end bytes + { + ChangeUpgradeState(ref upgradeState, UpgradeState.WritingPreambleEnd); + // we've finished the preamble. Ack and return. + await currentConnection.WriteAsync(ServerSessionEncoder.AckResponseBytes, 0, ServerSessionEncoder.AckResponseBytes.Length, + true, timeoutHelper.RemainingTime()); + //terminal state + ChangeUpgradeState(ref upgradeState, UpgradeState.PreambleEndSent); + } + + //we are done, this.currentConnection is the upgraded connection + return currentConnection; + } + + if (size == 0) + { + break; + } + } + } + } + + void ChangeUpgradeState(ref UpgradeState upgradeState, UpgradeState newState) + { + switch (newState) + { + case UpgradeState.None: + throw Fx.AssertAndThrow("Invalid State Transition: currentState=" + upgradeState + ", newState=" + newState); + case UpgradeState.VerifyingUpgradeRequest: + if (upgradeState != UpgradeState.None //starting first upgrade + && upgradeState != UpgradeState.UpgradeComplete)//completing one upgrade and starting another + { + throw Fx.AssertAndThrow("Invalid State Transition: currentState=" + upgradeState + ", newState=" + newState); + } + break; + case UpgradeState.WritingUpgradeAck: + if (upgradeState != UpgradeState.VerifyingUpgradeRequest) + { + throw Fx.AssertAndThrow("Invalid State Transition: currentState=" + upgradeState + ", newState=" + newState); + } + break; + case UpgradeState.UpgradeAckSent: + if (upgradeState != UpgradeState.WritingUpgradeAck) + { + throw Fx.AssertAndThrow("Invalid State Transition: currentState=" + upgradeState + ", newState=" + newState); + } + break; + case UpgradeState.BeginUpgrade: + if (upgradeState != UpgradeState.UpgradeAckSent) + { + throw Fx.AssertAndThrow("Invalid State Transition: currentState=" + upgradeState + ", newState=" + newState); + } + break; + case UpgradeState.EndUpgrade: + if (upgradeState != UpgradeState.BeginUpgrade) + { + throw Fx.AssertAndThrow("Invalid State Transition: currentState=" + upgradeState + ", newState=" + newState); + } + break; + case UpgradeState.UpgradeComplete: + if (upgradeState != UpgradeState.EndUpgrade) + { + throw Fx.AssertAndThrow("Invalid State Transition: currentState=" + upgradeState + ", newState=" + newState); + } + break; + case UpgradeState.WritingPreambleEnd: + if (upgradeState != UpgradeState.None //no upgrade being used + && upgradeState != UpgradeState.UpgradeComplete)//upgrades are now complete, end the preamble handshake. + { + throw Fx.AssertAndThrow("Invalid State Transition: currentState=" + upgradeState + ", newState=" + newState); + } + break; + case UpgradeState.PreambleEndSent: + if (upgradeState != UpgradeState.WritingPreambleEnd) + { + throw Fx.AssertAndThrow("Invalid State Transition: currentState=" + upgradeState + ", newState=" + newState); + } + break; + default: + throw Fx.AssertAndThrow("Unexpected Upgrade State: " + newState); + } + + upgradeState = newState; + } + + private static bool CanReadAndDecode(UpgradeState upgradeState) + { + //ok to read/decode before we start the upgrade + //and between UpgradeComplete/WritingPreambleAck + return upgradeState == UpgradeState.None + || upgradeState == UpgradeState.UpgradeComplete; + } + + enum UpgradeState + { + None, + VerifyingUpgradeRequest, + WritingUpgradeAck, + UpgradeAckSent, + BeginUpgrade, + EndUpgrade, + UpgradeComplete, + WritingPreambleEnd, + PreambleEndSent, + } + + void SetupSecurityIfNecessary(StreamUpgradeAcceptor upgradeAcceptor) + { + StreamSecurityUpgradeAcceptor securityUpgradeAcceptor = upgradeAcceptor as StreamSecurityUpgradeAcceptor; + if (securityUpgradeAcceptor != null) + { + security = securityUpgradeAcceptor.GetRemoteSecurity(); + if (security == null) + { + Exception securityFailedException = new ProtocolException( + SR.Format(SR.RemoteSecurityNotNegotiatedOnStreamUpgrade, Via)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(securityFailedException); + } + } + } + + #region Transport Security Auditing + void WriteAuditFailure(StreamSecurityUpgradeAcceptor securityUpgradeAcceptor, Exception exception) + { + } + #endregion + + public Task StartReadingAsync(Action viaDelegate, TimeSpan timeout) + { + this.viaDelegate = viaDelegate; + receiveTimeoutHelper = new TimeoutHelper(timeout); + connectionBuffer = Connection.AsyncReadBuffer; + return ReadAndDispatchAsync(); + } + } + + class ServerSingletonConnectionReader : SingletonConnectionReader + { + ConnectionDemuxer connectionDemuxer; + ServerSingletonDecoder decoder; + IConnection rawConnection; + string contentType; + ChannelBinding channelBindingToken; + + public ServerSingletonConnectionReader(ServerSingletonPreambleConnectionReader preambleReader, + IConnection upgradedConnection, ConnectionDemuxer connectionDemuxer) + : base(upgradedConnection, preambleReader.BufferOffset, preambleReader.BufferSize, + preambleReader.Security, preambleReader.TransportSettings, preambleReader.Via) + { + decoder = preambleReader.Decoder; + contentType = decoder.ContentType; + this.connectionDemuxer = connectionDemuxer; + rawConnection = preambleReader.RawConnection; + channelBindingToken = preambleReader.ChannelBinding; + } + + protected override string ContentType + { + get { return contentType; } + } + + protected override long StreamPosition + { + get { return decoder.StreamPosition; } + } + + protected override bool DecodeBytes(byte[] buffer, ref int offset, ref int size, ref bool isAtEof) + { + while (size > 0) + { + int bytesRead = decoder.Decode(buffer, offset, size); + if (bytesRead > 0) + { + offset += bytesRead; + size -= bytesRead; + } + + switch (decoder.CurrentState) + { + case ServerSingletonDecoder.State.EnvelopeStart: + // we're at the envelope + return true; + + case ServerSingletonDecoder.State.End: + isAtEof = true; + return false; + } + } + + return false; + } + + protected override async Task OnCloseAsync(CancellationToken token) + { + TimeoutHelper timeoutHelper = new TimeoutHelper(TimeoutHelper.GetOriginalTimeout(token)); + // send back EOF and then recycle the connection + await Connection.WriteAsync(SingletonEncoder.EndBytes, 0, SingletonEncoder.EndBytes.Length, true, timeoutHelper.RemainingTime()); + connectionDemuxer.ReuseConnection(rawConnection, timeoutHelper.RemainingTime()); + + ChannelBindingUtility.Dispose(ref channelBindingToken); + } + + protected override void PrepareMessage(Message message) + { + base.PrepareMessage(message); + IPEndPoint remoteEndPoint = rawConnection.RemoteIPEndPoint; + + // pipes will return null + if (remoteEndPoint != null) + { + RemoteEndpointMessageProperty remoteEndpointProperty = new RemoteEndpointMessageProperty(remoteEndPoint); + message.Properties.Add(RemoteEndpointMessageProperty.Name, remoteEndpointProperty); + } + + if (channelBindingToken != null) + { + ChannelBindingMessageProperty property = new ChannelBindingMessageProperty(channelBindingToken, false); + property.AddTo(message); + ((IDisposable)property).Dispose(); //message.Properties.Add() creates a copy... + } + } + } + + abstract class SingletonConnectionReader + { + IConnection connection; + bool doneReceiving; + bool doneSending; + bool isAtEof; + bool isClosed; + SecurityMessageProperty security; + object thisLock = new object(); + int offset; + int size; + IConnectionOrientedTransportFactorySettings transportSettings; + Uri via; + Stream inputStream; + + protected SingletonConnectionReader(IConnection connection, int offset, int size, SecurityMessageProperty security, + IConnectionOrientedTransportFactorySettings transportSettings, Uri via) + { + this.connection = connection; + this.offset = offset; + this.size = size; + this.security = security; + this.transportSettings = transportSettings; + this.via = via; + } + + protected IConnection Connection + { + get + { + return connection; + } + } + + protected object ThisLock + { + get + { + return thisLock; + } + } + + protected virtual string ContentType + { + get { return null; } + } + + protected abstract long StreamPosition { get; } + + public void Abort() + { + connection.Abort(); + } + + public Task DoneReceivingAsync(bool atEof) + { + return DoneReceivingAsync(atEof, new TimeoutHelper(transportSettings.CloseTimeout).GetCancellationToken()); + } + + Task DoneReceivingAsync(bool atEof, CancellationToken token) + { + if (!doneReceiving) + { + isAtEof = atEof; + doneReceiving = true; + + if (doneSending) + { + return CloseAsync(token); + } + } + + return Task.CompletedTask; + } + + public async Task CloseAsync(CancellationToken token) + { + lock (ThisLock) + { + if (isClosed) + { + return; + } + + isClosed = true; + } + + bool success = false; + try + { + // first drain our stream if necessary + if (inputStream != null) + { + byte[] dummy = Fx.AllocateByteArray(transportSettings.ConnectionBufferSize); + while (!isAtEof) + { + int bytesRead = await inputStream.ReadAsync(dummy, 0, dummy.Length, token); + if (bytesRead == 0) + { + isAtEof = true; + } + } + } + await OnCloseAsync(token); + success = true; + } + finally + { + if (!success) + { + Abort(); + } + } + } + + protected abstract Task OnCloseAsync(CancellationToken token); + + public Task DoneSendingAsync(CancellationToken token) + { + doneSending = true; + if (doneReceiving) + { + return CloseAsync(token); + } + + return Task.CompletedTask; + } + + protected abstract bool DecodeBytes(byte[] buffer, ref int offset, ref int size, ref bool isAtEof); + + protected virtual void PrepareMessage(Message message) + { + message.Properties.Via = via; + message.Properties.Security = (security != null) ? (SecurityMessageProperty)security.CreateCopy() : null; + } + + public async Task ReceiveRequestAsync(CancellationToken token) + { + Message requestMessage = await ReceiveAsync(token); + return new StreamedFramingRequestContext(this, requestMessage); + } + + public async Task ReceiveAsync(CancellationToken token) + { + //byte[] buffer = Fx.AllocateByteArray(connection.AsyncReadBufferSize); + + //if (size > 0) + //{ + // Buffer.BlockCopy(connection.AsyncReadBuffer, offset, buffer, offset, size); + //} + var timeoutHelper = new TimeoutHelper(TimeoutHelper.GetOriginalTimeout(token)); + for (;;) + { + if (DecodeBytes(connection.AsyncReadBuffer, ref offset, ref size, ref isAtEof)) + { + break; + } + + if (isAtEof) + { + await DoneReceivingAsync(true, token); + return null; + } + + if (size == 0) + { + offset = 0; + size = await connection.ReadAsync(0, connection.AsyncReadBufferSize, timeoutHelper.RemainingTime()); + if (size == 0) + { + await DoneReceivingAsync(true, token); + return null; + } + } + } + + // we're ready to read a message + IConnection singletonConnection = connection; + if (size > 0) + { + singletonConnection = new PreReadConnection(singletonConnection, offset, size); + } + + Stream connectionStream = new SingletonInputConnectionStream(this, singletonConnection, transportSettings); + inputStream = new MaxMessageSizeStream(connectionStream, transportSettings.MaxReceivedMessageSize); + Message message = null; + try + { + message = transportSettings.MessageEncoderFactory.Encoder.ReadMessage( + inputStream, transportSettings.MaxBufferSize, ContentType); + } + catch (XmlException xmlException) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new ProtocolException(SR.Format(SR.MessageXmlProtocolError), xmlException)); + } + + PrepareMessage(message); + + return message; + } + + class StreamedFramingRequestContext : RequestContextBase + { + IConnection connection; + SingletonConnectionReader parent; + IConnectionOrientedTransportFactorySettings settings; + TimeoutHelper timeoutHelper; + + public StreamedFramingRequestContext(SingletonConnectionReader parent, Message requestMessage) + : base(requestMessage, parent.transportSettings.CloseTimeout, parent.transportSettings.SendTimeout) + { + this.parent = parent; + connection = parent.connection; + settings = parent.transportSettings; + } + + protected override void OnAbort() + { + parent.Abort(); + } + + protected override Task OnCloseAsync(CancellationToken token) + { + return parent.CloseAsync(token); + } + + protected override async Task OnReplyAsync(Message message, CancellationToken token) + { + // TODO: Support ICompressedMessageEncoder + //ICompressedMessageEncoder compressedMessageEncoder = this.settings.MessageEncoderFactory.Encoder as ICompressedMessageEncoder; + //if (compressedMessageEncoder != null && compressedMessageEncoder.CompressionEnabled) + //{ + // compressedMessageEncoder.AddCompressedMessageProperties(message, this.parent.ContentType); + //} + + timeoutHelper = new TimeoutHelper(TimeoutHelper.GetOriginalTimeout(token)); + await StreamingConnectionHelper.WriteMessageAsync(message, connection, false, settings, token); + await parent.DoneSendingAsync(token); + } + } + + // ensures that the reader is notified at end-of-stream, and takes care of the framing chunk headers + class SingletonInputConnectionStream : ConnectionStream + { + SingletonMessageDecoder decoder; + SingletonConnectionReader reader; + bool atEof; + byte[] chunkBuffer; // used for when we have overflow + int chunkBufferOffset; + int chunkBufferSize; + int chunkBytesRemaining; + + public SingletonInputConnectionStream(SingletonConnectionReader reader, IConnection connection, + IDefaultCommunicationTimeouts defaultTimeouts) + : base(connection, defaultTimeouts) + { + this.reader = reader; + decoder = new SingletonMessageDecoder(reader.StreamPosition); + chunkBytesRemaining = 0; + chunkBuffer = new byte[IntEncoder.MaxEncodedSize]; + } + + void AbortReader() + { + reader.Abort(); + } + + public override void Close() + { + reader.DoneReceivingAsync(atEof).GetAwaiter().GetResult(); + } + + // run chunk data through the decoder + void DecodeData(byte[] buffer, int offset, int size) + { + while (size > 0) + { + int bytesRead = decoder.Decode(buffer, offset, size); + offset += bytesRead; + size -= bytesRead; + Fx.Assert(decoder.CurrentState == SingletonMessageDecoder.State.ReadingEnvelopeBytes || decoder.CurrentState == SingletonMessageDecoder.State.ChunkEnd, ""); + } + } + + // run the current data through the decoder to get valid message bytes + void DecodeSize(byte[] buffer, ref int offset, ref int size) + { + while (size > 0) + { + int bytesRead = decoder.Decode(buffer, offset, size); + + if (bytesRead > 0) + { + offset += bytesRead; + size -= bytesRead; + } + + switch (decoder.CurrentState) + { + case SingletonMessageDecoder.State.ChunkStart: + chunkBytesRemaining = decoder.ChunkSize; + + // if we have overflow and we're not decoding out of our buffer, copy over + if (size > 0 && !object.ReferenceEquals(buffer, chunkBuffer)) + { + Fx.Assert(size <= chunkBuffer.Length, ""); + Buffer.BlockCopy(buffer, offset, chunkBuffer, 0, size); + chunkBufferOffset = 0; + chunkBufferSize = size; + } + return; + + case SingletonMessageDecoder.State.End: + ProcessEof(); + return; + } + } + } + + async Task ReadCoreAsync(byte[] buffer, int offset, int count, CancellationToken token) + { + int bytesRead = -1; + try + { + bytesRead = await base.ReadAsync(buffer, offset, count, token); + if (bytesRead == 0) + { + ProcessEof(); + } + } + finally + { + if (bytesRead == -1) // there was an exception + { + AbortReader(); + } + } + + return bytesRead; + } + + int ReadCore(byte[] buffer, int offset, int count) + { + int bytesRead = -1; + try + { + bytesRead = base.Read(buffer, offset, count); + if (bytesRead == 0) + { + ProcessEof(); + } + } + finally + { + if (bytesRead == -1) // there was an exception + { + AbortReader(); + } + } + + return bytesRead; + } + + public override async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken token) + { + int result = 0; + while (true) + { + if (count == 0) + { + return result; + } + + if (atEof) + { + return result; + } + + // first deal with any residual carryover + if (chunkBufferSize > 0) + { + int bytesToCopy = Math.Min(chunkBytesRemaining, + Math.Min(chunkBufferSize, count)); + + Buffer.BlockCopy(chunkBuffer, chunkBufferOffset, buffer, offset, bytesToCopy); + // keep decoder up to date + DecodeData(chunkBuffer, chunkBufferOffset, bytesToCopy); + + chunkBufferOffset += bytesToCopy; + chunkBufferSize -= bytesToCopy; + chunkBytesRemaining -= bytesToCopy; + if (chunkBytesRemaining == 0 && chunkBufferSize > 0) + { + DecodeSize(chunkBuffer, ref chunkBufferOffset, ref chunkBufferSize); + } + + result += bytesToCopy; + offset += bytesToCopy; + count -= bytesToCopy; + } + else if (chunkBytesRemaining > 0) + { + // We're in the middle of a chunk. Try and include the next chunk size as well + + int bytesToRead = count; + if (int.MaxValue - chunkBytesRemaining >= IntEncoder.MaxEncodedSize) + { + bytesToRead = Math.Min(count, chunkBytesRemaining + IntEncoder.MaxEncodedSize); + } + + int bytesRead = ReadCore(buffer, offset, bytesToRead); + + // keep decoder up to date + DecodeData(buffer, offset, Math.Min(bytesRead, chunkBytesRemaining)); + + if (bytesRead > chunkBytesRemaining) + { + result += chunkBytesRemaining; + int overflowCount = bytesRead - chunkBytesRemaining; + int overflowOffset = offset + chunkBytesRemaining; + chunkBytesRemaining = 0; + // read at least part of the next chunk, and put any overflow in this.chunkBuffer + DecodeSize(buffer, ref overflowOffset, ref overflowCount); + } + else + { + result += bytesRead; + chunkBytesRemaining -= bytesRead; + } + + return result; + } + else + { + // Final case: we have a new chunk. Read the size, and loop around again + if (count < IntEncoder.MaxEncodedSize) + { + // we don't have space for MaxEncodedSize, so it's worth the copy cost to read into a temp buffer + chunkBufferOffset = 0; + chunkBufferSize = await ReadCoreAsync(chunkBuffer, 0, chunkBuffer.Length, token); + DecodeSize(chunkBuffer, ref chunkBufferOffset, ref chunkBufferSize); + } + else + { + int bytesRead = ReadCore(buffer, offset, IntEncoder.MaxEncodedSize); + int sizeOffset = offset; + DecodeSize(buffer, ref sizeOffset, ref bytesRead); + } + } + } + } + + public override int Read(byte[] buffer, int offset, int count) + { + int result = 0; + while (true) + { + if (count == 0) + { + return result; + } + + if (atEof) + { + return result; + } + + // first deal with any residual carryover + if (chunkBufferSize > 0) + { + int bytesToCopy = Math.Min(chunkBytesRemaining, + Math.Min(chunkBufferSize, count)); + + Buffer.BlockCopy(chunkBuffer, chunkBufferOffset, buffer, offset, bytesToCopy); + // keep decoder up to date + DecodeData(chunkBuffer, chunkBufferOffset, bytesToCopy); + + chunkBufferOffset += bytesToCopy; + chunkBufferSize -= bytesToCopy; + chunkBytesRemaining -= bytesToCopy; + if (chunkBytesRemaining == 0 && chunkBufferSize > 0) + { + DecodeSize(chunkBuffer, ref chunkBufferOffset, ref chunkBufferSize); + } + + result += bytesToCopy; + offset += bytesToCopy; + count -= bytesToCopy; + } + else if (chunkBytesRemaining > 0) + { + // We're in the middle of a chunk. Try and include the next chunk size as well + + int bytesToRead = count; + if (int.MaxValue - chunkBytesRemaining >= IntEncoder.MaxEncodedSize) + { + bytesToRead = Math.Min(count, chunkBytesRemaining + IntEncoder.MaxEncodedSize); + } + + int bytesRead = ReadCore(buffer, offset, bytesToRead); + + // keep decoder up to date + DecodeData(buffer, offset, Math.Min(bytesRead, chunkBytesRemaining)); + + if (bytesRead > chunkBytesRemaining) + { + result += chunkBytesRemaining; + int overflowCount = bytesRead - chunkBytesRemaining; + int overflowOffset = offset + chunkBytesRemaining; + chunkBytesRemaining = 0; + // read at least part of the next chunk, and put any overflow in this.chunkBuffer + DecodeSize(buffer, ref overflowOffset, ref overflowCount); + } + else + { + result += bytesRead; + chunkBytesRemaining -= bytesRead; + } + + return result; + } + else + { + // Final case: we have a new chunk. Read the size, and loop around again + if (count < IntEncoder.MaxEncodedSize) + { + // we don't have space for MaxEncodedSize, so it's worth the copy cost to read into a temp buffer + chunkBufferOffset = 0; + chunkBufferSize = ReadCore(chunkBuffer, 0, chunkBuffer.Length); + DecodeSize(chunkBuffer, ref chunkBufferOffset, ref chunkBufferSize); + } + else + { + int bytesRead = ReadCore(buffer, offset, IntEncoder.MaxEncodedSize); + int sizeOffset = offset; + DecodeSize(buffer, ref sizeOffset, ref bytesRead); + } + } + } + } + + void ProcessEof() + { + if (!atEof) + { + atEof = true; + if (chunkBufferSize > 0 || chunkBytesRemaining > 0 + || decoder.CurrentState != SingletonMessageDecoder.State.End) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(decoder.CreatePrematureEOFException()); + } + + reader.DoneReceivingAsync(true).GetAwaiter().GetResult(); + } + } + } + } + + static class StreamingConnectionHelper + { + public static async Task WriteMessageAsync(Message message, IConnection connection, bool isRequest, + IConnectionOrientedTransportFactorySettings settings, CancellationToken token) + { + // TODO: Switch to using the token as this is supposed to use remaining time + var timeoutHelper = new TimeoutHelper(TimeoutHelper.GetOriginalTimeout(token)); + + byte[] endBytes = null; + if (message != null) + { + MessageEncoder messageEncoder = settings.MessageEncoderFactory.Encoder; + byte[] envelopeStartBytes = SingletonEncoder.EnvelopeStartBytes; + + bool writeStreamed; + if (isRequest) + { + endBytes = SingletonEncoder.EnvelopeEndFramingEndBytes; + writeStreamed = TransferModeHelper.IsRequestStreamed(settings.TransferMode); + } + else + { + endBytes = SingletonEncoder.EnvelopeEndBytes; + writeStreamed = TransferModeHelper.IsResponseStreamed(settings.TransferMode); + } + + if (writeStreamed) + { + await connection.WriteAsync(envelopeStartBytes, 0, envelopeStartBytes.Length, false, timeoutHelper.RemainingTime()); + Stream connectionStream = new StreamingOutputConnectionStream(connection, settings); + Stream writeTimeoutStream = new TimeoutStream(connectionStream, timeoutHelper.RemainingTime()); + messageEncoder.WriteMessage(message, writeTimeoutStream); + } + else + { + ArraySegment messageData = messageEncoder.WriteMessage(message, + int.MaxValue, settings.BufferManager, envelopeStartBytes.Length + IntEncoder.MaxEncodedSize); + messageData = SingletonEncoder.EncodeMessageFrame(messageData); + Buffer.BlockCopy(envelopeStartBytes, 0, messageData.Array, messageData.Offset - envelopeStartBytes.Length, + envelopeStartBytes.Length); + await connection.WriteAsync(messageData.Array, messageData.Offset - envelopeStartBytes.Length, + messageData.Count + envelopeStartBytes.Length, true, timeoutHelper.RemainingTime(), settings.BufferManager); + } + } + else if (isRequest) // context handles response end bytes + { + endBytes = SingletonEncoder.EndBytes; + } + + if (endBytes != null) + { + await connection.WriteAsync(endBytes, 0, endBytes.Length, + true, timeoutHelper.RemainingTime()); + } + } + + // overrides ConnectionStream to add a Framing int at the beginning of each record + class StreamingOutputConnectionStream : ConnectionStream + { + byte[] encodedSize; + + public StreamingOutputConnectionStream(IConnection connection, IDefaultCommunicationTimeouts timeouts) + : base(connection, timeouts) + { + encodedSize = new byte[IntEncoder.MaxEncodedSize]; + } + void WriteChunkSize(int size) + { + if (size > 0) + { + int bytesEncoded = IntEncoder.Encode(size, encodedSize, 0); + base.Connection.Write(encodedSize, 0, bytesEncoded, false, TimeSpan.FromMilliseconds(WriteTimeout)); + } + } + + public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) + { + WriteChunkSize(count); + return base.BeginWrite(buffer, offset, count, callback, state); + } + + public override void WriteByte(byte value) + { + WriteChunkSize(1); + base.WriteByte(value); + } + + public override void Write(byte[] buffer, int offset, int count) + { + WriteChunkSize(count); + base.Write(buffer, offset, count); + } + } + } +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Channels/SocketAsyncEventArgsPool.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/SocketAsyncEventArgsPool.cs new file mode 100644 index 000000000..8b118ebe6 --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/SocketAsyncEventArgsPool.cs @@ -0,0 +1,84 @@ +using System; +using System.Diagnostics; +using System.Net.Sockets; +using CoreWCF.Runtime; + + +namespace CoreWCF.Channels +{ + class SocketAsyncEventArgsPool : QueuedObjectPool + { + const int SingleBatchSize = 128 * 1024; + const int MaxBatchCount = 16; + const int MaxFreeCountFactor = 4; + int acceptBufferSize; + + public SocketAsyncEventArgsPool(int acceptBufferSize) + { + if (acceptBufferSize <= 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("acceptBufferSize")); + } + + this.acceptBufferSize = acceptBufferSize; + int batchCount = (SingleBatchSize + acceptBufferSize - 1) / acceptBufferSize; + if (batchCount > MaxBatchCount) + { + batchCount = MaxBatchCount; + } + + Initialize(batchCount, batchCount * MaxFreeCountFactor); + } + + public override bool Return(SocketAsyncEventArgs socketAsyncEventArgs) + { + CleanupAcceptSocket(socketAsyncEventArgs); + + if (!base.Return(socketAsyncEventArgs)) + { + CleanupItem(socketAsyncEventArgs); + return false; + } + + return true; + } + + // TODO: Make async + internal static void CleanupAcceptSocket(SocketAsyncEventArgs socketAsyncEventArgs) + { + Fx.Assert(socketAsyncEventArgs != null, "socketAsyncEventArgs should not be null."); + + Socket socket = socketAsyncEventArgs.AcceptSocket; + if (socket != null) + { + socketAsyncEventArgs.AcceptSocket = null; + + try + { + socket.Close(0); + } + catch (SocketException ex) + { + Fx.Exception.TraceHandledException(ex, TraceEventType.Information); + } + catch (ObjectDisposedException ex) + { + Fx.Exception.TraceHandledException(ex, TraceEventType.Information); + } + } + } + + protected override void CleanupItem(SocketAsyncEventArgs item) + { + item.Dispose(); + } + + protected override SocketAsyncEventArgs Create() + { + SocketAsyncEventArgs eventArgs = new SocketAsyncEventArgs(); + byte[] acceptBuffer = Fx.AllocateByteArray(acceptBufferSize); + eventArgs.SetBuffer(acceptBuffer, 0, acceptBufferSize); + return eventArgs; + } + } +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Channels/SocketConnection.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/SocketConnection.cs new file mode 100644 index 000000000..4806be0ee --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/SocketConnection.cs @@ -0,0 +1,1655 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Net; +using System.Net.Sockets; +using CoreWCF.Runtime; +using CoreWCF.Runtime.Diagnostics; +using System.Runtime.InteropServices; +using System.Security; +using System.Security.Permissions; +using CoreWCF; +using CoreWCF.Diagnostics; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Diagnostics.Contracts; + +namespace CoreWCF.Channels +{ + class SocketConnection : IConnection + { + static EventHandler onReceiveAsyncCompleted; + static EventHandler onSocketSendCompleted; + + // common state + Socket socket; + TimeSpan sendTimeout; + TimeSpan readFinTimeout; + TimeSpan receiveTimeout; + CloseState _closeState; + bool isShutdown; + bool noDelay = false; + bool aborted; + TraceEventType exceptionEventType; + + // close state + TimeoutHelper closeTimeoutHelper; + static Action onWaitForFinComplete = new Action(OnWaitForFinComplete); + + // read state + int asyncReadSize; + SocketAsyncEventArgs asyncReadEventArgs; + byte[] readBuffer; + int asyncReadBufferSize; + object asyncReadState; + Action asyncReadCallback; + Exception asyncReadException; + bool asyncReadPending; + + // write state + SocketAsyncEventArgs asyncWriteEventArgs; + object asyncWriteState; + Action asyncWriteCallback; + Exception asyncWriteException; + bool asyncWritePending; + + IOThreadTimer receiveTimer; + static Action onReceiveTimeout; + IOThreadTimer sendTimer; + static Action onSendTimeout; + string timeoutErrorString; + TransferOperation timeoutErrorTransferOperation; + IPEndPoint remoteEndpoint; + ConnectionBufferPool connectionBufferPool; + string remoteEndpointAddress; + + public SocketConnection(Socket socket, ConnectionBufferPool connectionBufferPool, bool autoBindToCompletionPort) + { + Fx.Assert(autoBindToCompletionPort, "Not binding to completion port isn't supported in by WCF Core"); + + if (socket == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("socket"); + } + + Fx.Assert(connectionBufferPool != null, "Argument connectionBufferPool cannot be null"); + + CloseStateVal = CloseState.Open; + exceptionEventType = TraceEventType.Error; + this.socket = socket; + this.connectionBufferPool = connectionBufferPool; + readBuffer = this.connectionBufferPool.Take(); + asyncReadBufferSize = readBuffer.Length; + this.socket.SendBufferSize = this.socket.ReceiveBufferSize = asyncReadBufferSize; + sendTimeout = receiveTimeout = TimeSpan.MaxValue; + + remoteEndpoint = null; + + if (autoBindToCompletionPort) + { + this.socket.UseOnlyOverlappedIO = false; + } + } + + private CloseState CloseStateVal + { + get + { + return _closeState; + } + set + { + _closeState = value; + } + } + public int AsyncReadBufferSize + { + get { return asyncReadBufferSize; } + } + + public byte[] AsyncReadBuffer + { + get + { + return readBuffer; + } + } + + object ThisLock + { + get { return this; } + } + + public TraceEventType ExceptionEventType + { + get { return exceptionEventType; } + set { exceptionEventType = value; } + } + + public IPEndPoint RemoteIPEndPoint + { + get + { + // this property should only be called on the receive path + if (remoteEndpoint == null && CloseStateVal == CloseState.Open) + { + try + { + remoteEndpoint = (IPEndPoint)socket.RemoteEndPoint; + } + catch (SocketException socketException) + { + // will never be a timeout error, so TimeSpan.Zero is ok + throw DiagnosticUtility.ExceptionUtility.ThrowHelper( + ConvertReceiveException(socketException, TimeSpan.Zero), ExceptionEventType); + } + catch (ObjectDisposedException objectDisposedException) + { + Exception exceptionToThrow = ConvertObjectDisposedException(objectDisposedException, TransferOperation.Undefined); + if (object.ReferenceEquals(exceptionToThrow, objectDisposedException)) + { + throw; + } + else + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelper(exceptionToThrow, ExceptionEventType); + } + } + } + + return remoteEndpoint; + } + } + + IOThreadTimer SendTimer + { + get + { + if (sendTimer == null) + { + if (onSendTimeout == null) + { + onSendTimeout = new Action(OnSendTimeout); + } + + sendTimer = new IOThreadTimer(onSendTimeout, this, false); + } + + return sendTimer; + } + } + + IOThreadTimer ReceiveTimer + { + get + { + if (receiveTimer == null) + { + if (onReceiveTimeout == null) + { + onReceiveTimeout = new Action(OnReceiveTimeout); + } + + receiveTimer = new IOThreadTimer(onReceiveTimeout, this, false); + } + + return receiveTimer; + } + } + + + string RemoteEndpointAddress + { + get + { + if (remoteEndpointAddress == null) + { + try + { + IPEndPoint local, remote; + if (TryGetEndpoints(out local, out remote)) + { + remoteEndpointAddress = GetRemoteEndpointAddressPort(remote); + } + else + { + //null indicates not initialized. + remoteEndpointAddress = string.Empty; + } + } + catch (Exception exception) + { + if (Fx.IsFatal(exception)) + { + throw; + } + + } + } + return remoteEndpointAddress; + } + } + + internal static string GetRemoteEndpointAddressPort(IPEndPoint iPEndPoint) + { + //We really don't want any exceptions + if (iPEndPoint != null) + { + try + { + return iPEndPoint.Address.ToString() + ":" + iPEndPoint.Port; + } + catch (Exception exception) + { + if (Fx.IsFatal(exception)) + { + throw; + } + //ignore and continue with all non-fatal exceptions. + } + } + + return string.Empty; + } + + static void OnReceiveTimeout(object state) + { + SocketConnection thisPtr = (SocketConnection)state; + thisPtr.Abort(SR.Format(SR.SocketAbortedReceiveTimedOut, thisPtr.receiveTimeout), TransferOperation.Read); + } + + static void OnSendTimeout(object state) + { + SocketConnection thisPtr = (SocketConnection)state; + thisPtr.Abort(TraceEventType.Warning, + SR.Format(SR.SocketAbortedSendTimedOut, thisPtr.sendTimeout), TransferOperation.Write); + } + + static void OnReceiveAsyncCompleted(object sender, SocketAsyncEventArgs e) + { + ((SocketConnection)e.UserToken).OnReceiveAsync(sender, e); + } + + static void OnSendAsyncCompleted(object sender, SocketAsyncEventArgs e) + { + ((SocketConnection)e.UserToken).OnSendAsync(sender, e); + } + + public void Abort() + { + Abort(null, TransferOperation.Undefined); + } + + void Abort(string timeoutErrorString, TransferOperation transferOperation) + { + TraceEventType traceEventType = TraceEventType.Warning; + + // we could be timing out a cached connection + if (ExceptionEventType == TraceEventType.Information) + { + traceEventType = ExceptionEventType; + } + + Abort(traceEventType, timeoutErrorString, transferOperation); + } + + void Abort(TraceEventType traceEventType) + { + Abort(traceEventType, null, TransferOperation.Undefined); + } + + void Abort(TraceEventType traceEventType, string timeoutErrorString, TransferOperation transferOperation) + { + lock (ThisLock) + { + if (CloseStateVal == CloseState.Closed) + { + return; + } + + this.timeoutErrorString = timeoutErrorString; + timeoutErrorTransferOperation = transferOperation; + aborted = true; + CloseStateVal = CloseState.Closed; + + if (asyncReadPending) + { + CancelReceiveTimer(); + } + else + { + DisposeReadEventArgs(); + } + + if (asyncWritePending) + { + CancelSendTimer(); + } + else + { + DisposeWriteEventArgs(); + } + } + + socket.Close(0); + } + + void AbortRead() + { + lock (ThisLock) + { + if (asyncReadPending) + { + if (CloseStateVal != CloseState.Closed) + { + SetUserToken(asyncReadEventArgs, null); + asyncReadPending = false; + CancelReceiveTimer(); + } + else + { + DisposeReadEventArgs(); + } + } + } + } + + void CancelReceiveTimer() + { + // CSDMain 34539: Snapshot the timer so that we don't null ref if there is a race + // between calls to CancelReceiveTimer (e.g., Abort, AsyncReadCallback) + + IOThreadTimer receiveTimerSnapshot = receiveTimer; + receiveTimer = null; + + if (receiveTimerSnapshot != null) + { + receiveTimerSnapshot.Cancel(); + } + } + + void CancelSendTimer() + { + IOThreadTimer sendTimerSnapshot = sendTimer; + sendTimer = null; + + if (sendTimerSnapshot != null) + { + sendTimerSnapshot.Cancel(); + } + } + + void CloseAsyncAndLinger() + { + readFinTimeout = closeTimeoutHelper.RemainingTime(); + + try + { + if (BeginReadCore(0, 1, readFinTimeout, onWaitForFinComplete, this) == AsyncCompletionResult.Queued) + { + return; + } + + int bytesRead = EndRead(); + + if (bytesRead > 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelper( + new CommunicationException(SR.Format(SR.SocketCloseReadReceivedData, socket.RemoteEndPoint)), + ExceptionEventType); + } + } + catch (TimeoutException timeoutException) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelper(new TimeoutException( + SR.Format(SR.SocketCloseReadTimeout, socket.RemoteEndPoint, readFinTimeout), timeoutException), + ExceptionEventType); + } + + ContinueClose(closeTimeoutHelper.RemainingTime()); + } + + static void OnWaitForFinComplete(object state) + { + SocketConnection thisPtr = (SocketConnection)state; + + try + { + int bytesRead; + + try + { + bytesRead = thisPtr.EndRead(); + + if (bytesRead > 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelper( + new CommunicationException(SR.Format(SR.SocketCloseReadReceivedData, thisPtr.socket.RemoteEndPoint)), + thisPtr.ExceptionEventType); + } + } + catch (TimeoutException timeoutException) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelper(new TimeoutException( + SR.Format(SR.SocketCloseReadTimeout, thisPtr.socket.RemoteEndPoint, thisPtr.readFinTimeout), + timeoutException), thisPtr.ExceptionEventType); + } + + thisPtr.ContinueClose(thisPtr.closeTimeoutHelper.RemainingTime()); + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + + DiagnosticUtility.TraceHandledException(e, TraceEventType.Warning); + + // The user has no opportunity to clean up the connection in the async and linger + // code path, ensure cleanup finishes. + thisPtr.Abort(); + } + } + + public void Close(TimeSpan timeout, bool asyncAndLinger) + { + lock (ThisLock) + { + if (CloseStateVal == CloseState.Closing || CloseStateVal == CloseState.Closed) + { + // already closing or closed, so just return + return; + } + CloseStateVal = CloseState.Closing; + } + + // first we shutdown our send-side + closeTimeoutHelper = new TimeoutHelper(timeout); + Shutdown(closeTimeoutHelper.RemainingTime()); + + if (asyncAndLinger) + { + CloseAsyncAndLinger(); + } + else + { + CloseSync(); + } + } + + void CloseSync() + { + byte[] dummy = new byte[1]; + + // then we check for a FIN from the other side (i.e. read zero) + int bytesRead; + readFinTimeout = closeTimeoutHelper.RemainingTime(); + + try + { + bytesRead = ReadCore(dummy, 0, 1, readFinTimeout, true); + + if (bytesRead > 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelper( + new CommunicationException(SR.Format(SR.SocketCloseReadReceivedData, socket.RemoteEndPoint)), ExceptionEventType); + } + } + catch (TimeoutException timeoutException) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelper(new TimeoutException( + SR.Format(SR.SocketCloseReadTimeout, socket.RemoteEndPoint, readFinTimeout), timeoutException), ExceptionEventType); + } + + // finally we call Close with whatever time is remaining + ContinueClose(closeTimeoutHelper.RemainingTime()); + } + + public void ContinueClose(TimeSpan timeout) + { + socket.Close(TimeoutHelper.ToMilliseconds(timeout)); + + lock (ThisLock) + { + // Abort could have been called on a separate thread and cleaned up + // our buffers/completion here + if (CloseStateVal != CloseState.Closed) + { + if (!asyncReadPending) + { + DisposeReadEventArgs(); + } + + if (!asyncWritePending) + { + DisposeWriteEventArgs(); + } + } + + CloseStateVal = CloseState.Closed; + } + } + + public void Shutdown(TimeSpan timeout) + { + lock (ThisLock) + { + if (isShutdown) + { + return; + } + + isShutdown = true; + } + + try + { + socket.Shutdown(SocketShutdown.Send); + } + catch (SocketException socketException) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelper( + ConvertSendException(socketException, TimeSpan.MaxValue), ExceptionEventType); + } + catch (ObjectDisposedException objectDisposedException) + { + Exception exceptionToThrow = ConvertObjectDisposedException(objectDisposedException, TransferOperation.Undefined); + if (object.ReferenceEquals(exceptionToThrow, objectDisposedException)) + { + throw; + } + else + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelper(exceptionToThrow, ExceptionEventType); + } + } + } + + void ThrowIfNotOpen() + { + if (CloseStateVal == CloseState.Closing || CloseStateVal == CloseState.Closed) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelper( + ConvertObjectDisposedException(new ObjectDisposedException( + GetType().ToString(), SR.Format(SR.SocketConnectionDisposed)), TransferOperation.Undefined), ExceptionEventType); + } + } + + void ThrowIfClosed() + { + if (CloseStateVal == CloseState.Closed) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelper( + ConvertObjectDisposedException(new ObjectDisposedException( + GetType().ToString(), SR.Format(SR.SocketConnectionDisposed)), TransferOperation.Undefined), ExceptionEventType); + } + } + + bool TryGetEndpoints(out IPEndPoint localIPEndpoint, out IPEndPoint remoteIPEndpoint) + { + localIPEndpoint = null; + remoteIPEndpoint = null; + + if (CloseStateVal == CloseState.Open) + { + try + { + remoteIPEndpoint = remoteEndpoint ?? (IPEndPoint)socket.RemoteEndPoint; + localIPEndpoint = (IPEndPoint)socket.LocalEndPoint; + } + catch (Exception exception) + { + if (Fx.IsFatal(exception)) + { + throw; + } + + DiagnosticUtility.TraceHandledException(exception, TraceEventType.Warning); + } + } + + return localIPEndpoint != null && remoteIPEndpoint != null; + } + + public object DuplicateAndClose(int targetProcessId) + { + object result = socket.DuplicateAndClose(targetProcessId); + Abort(TraceEventType.Information); + return result; + } + + public object GetCoreTransport() + { + return socket; + } + + public Task ValidateAsync(Uri uri) + { + return Task.FromResult(true); + } + + Exception ConvertSendException(SocketException socketException, TimeSpan remainingTime) + { + return ConvertTransferException(socketException, sendTimeout, socketException, + TransferOperation.Write, aborted, timeoutErrorString, timeoutErrorTransferOperation, this, remainingTime); + } + + Exception ConvertReceiveException(SocketException socketException, TimeSpan remainingTime) + { + return ConvertTransferException(socketException, receiveTimeout, socketException, + TransferOperation.Read, aborted, timeoutErrorString, timeoutErrorTransferOperation, this, remainingTime); + } + + internal static Exception ConvertTransferException(SocketException socketException, TimeSpan timeout, Exception originalException) + { + return ConvertTransferException(socketException, timeout, originalException, + TransferOperation.Undefined, false, null, TransferOperation.Undefined, null, TimeSpan.MaxValue); + } + + Exception ConvertObjectDisposedException(ObjectDisposedException originalException, TransferOperation transferOperation) + { + if (timeoutErrorString != null) + { + return ConvertTimeoutErrorException(originalException, transferOperation, timeoutErrorString, timeoutErrorTransferOperation); + } + else if (aborted) + { + return new CommunicationObjectAbortedException(SR.Format(SR.SocketConnectionDisposed), originalException); + } + else + { + return originalException; + } + } + + static Exception ConvertTransferException(SocketException socketException, TimeSpan timeout, Exception originalException, + TransferOperation transferOperation, bool aborted, string timeoutErrorString, TransferOperation timeoutErrorTransferOperation, + SocketConnection socketConnection, TimeSpan remainingTime) + { + if (socketException.ErrorCode == NativeSocketErrors.ERROR_INVALID_HANDLE) + { + return new CommunicationObjectAbortedException(socketException.Message, socketException); + } + + if (timeoutErrorString != null) + { + return ConvertTimeoutErrorException(originalException, transferOperation, timeoutErrorString, timeoutErrorTransferOperation); + } + + TraceEventType exceptionEventType = socketConnection == null ? TraceEventType.Error : socketConnection.ExceptionEventType; + + // 10053 can occur due to our timeout sockopt firing, so map to TimeoutException in that case + if (socketException.ErrorCode == NativeSocketErrors.WSAECONNABORTED && + remainingTime <= TimeSpan.Zero) + { + TimeoutException timeoutException = new TimeoutException(SR.Format(SR.TcpConnectionTimedOut, timeout), originalException); + return timeoutException; + } + + if (socketException.ErrorCode == NativeSocketErrors.WSAENETRESET || + socketException.ErrorCode == NativeSocketErrors.WSAECONNABORTED || + socketException.ErrorCode == NativeSocketErrors.WSAECONNRESET) + { + if (aborted) + { + return new CommunicationObjectAbortedException(SR.Format(SR.TcpLocalConnectionAborted), originalException); + } + else + { + CommunicationException communicationException = new CommunicationException(SR.Format(SR.TcpConnectionResetError, timeout), originalException); + return communicationException; + } + } + else if (socketException.ErrorCode == NativeSocketErrors.WSAETIMEDOUT) + { + TimeoutException timeoutException = new TimeoutException(SR.Format(SR.TcpConnectionTimedOut, timeout), originalException); + return timeoutException; + } + else + { + if (aborted) + { + return new CommunicationObjectAbortedException(SR.Format(SR.TcpTransferError, socketException.ErrorCode, socketException.Message), originalException); + } + else + { + CommunicationException communicationException = new CommunicationException(SR.Format(SR.TcpTransferError, socketException.ErrorCode, socketException.Message), originalException); + return communicationException; + } + } + } + + static Exception ConvertTimeoutErrorException(Exception originalException, + TransferOperation transferOperation, string timeoutErrorString, TransferOperation timeoutErrorTransferOperation) + { + if (timeoutErrorString == null) + { + Fx.Assert("Argument timeoutErrorString must not be null."); + } + + if (transferOperation == timeoutErrorTransferOperation) + { + return new TimeoutException(timeoutErrorString, originalException); + } + else + { + return new CommunicationException(timeoutErrorString, originalException); + } + } + + static string GetEndpointString(string sr, TimeSpan timeout, SocketException socketException, SocketConnection socketConnection) + { + IPEndPoint remoteEndpoint = null; + IPEndPoint localEndpoint = null; + bool haveEndpoints = socketConnection != null && socketConnection.TryGetEndpoints(out localEndpoint, out remoteEndpoint); + + if (string.Compare(sr, SR.TcpConnectionTimedOut, StringComparison.OrdinalIgnoreCase) == 0) + { + return haveEndpoints + ? SR.Format(SR.TcpConnectionTimedOutWithIP, timeout, localEndpoint, remoteEndpoint) + : SR.Format(SR.TcpConnectionTimedOut, timeout); + } + else if (string.Compare(sr, SR.TcpConnectionResetError, StringComparison.OrdinalIgnoreCase) == 0) + { + return haveEndpoints + ? SR.Format(SR.TcpConnectionResetErrorWithIP, timeout, localEndpoint, remoteEndpoint) + : SR.Format(SR.TcpConnectionResetError, timeout); + } + else + { + // sr == SR.TcpTransferError + return haveEndpoints + ? SR.Format(SR.TcpTransferErrorWithIP, socketException.ErrorCode, socketException.Message, localEndpoint, remoteEndpoint) + : SR.Format(SR.TcpTransferError, socketException.ErrorCode, socketException.Message); + } + } + + public AsyncCompletionResult BeginWrite(byte[] buffer, int offset, int size, bool immediate, TimeSpan timeout, + Action callback, object state) + { + ConnectionUtilities.ValidateBufferBounds(buffer, offset, size); + bool abortWrite = true; + + try + { + lock (ThisLock) + { + Fx.Assert(!asyncWritePending, "Called BeginWrite twice."); + ThrowIfClosed(); + EnsureWriteEventArgs(); + SetImmediate(immediate); + SetWriteTimeout(timeout, false); + SetUserToken(asyncWriteEventArgs, this); + asyncWritePending = true; + asyncWriteCallback = callback; + asyncWriteState = state; + } + + asyncWriteEventArgs.SetBuffer(buffer, offset, size); + + if (socket.SendAsync(asyncWriteEventArgs)) + { + abortWrite = false; + return AsyncCompletionResult.Queued; + } + + HandleSendAsyncCompleted(); + abortWrite = false; + return AsyncCompletionResult.Completed; + } + catch (SocketException socketException) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelper( + ConvertSendException(socketException, TimeSpan.MaxValue), ExceptionEventType); + } + catch (ObjectDisposedException objectDisposedException) + { + Exception exceptionToThrow = ConvertObjectDisposedException(objectDisposedException, TransferOperation.Write); + if (object.ReferenceEquals(exceptionToThrow, objectDisposedException)) + { + throw; + } + else + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelper(exceptionToThrow, ExceptionEventType); + } + } + finally + { + if (abortWrite) + { + AbortWrite(); + } + } + } + + public void EndWrite() + { + if (asyncWriteException != null) + { + AbortWrite(); + throw DiagnosticUtility.ExceptionUtility.ThrowHelper(asyncWriteException, ExceptionEventType); + } + + lock (ThisLock) + { + if (!asyncWritePending) + { + throw Fx.AssertAndThrow("SocketConnection.EndWrite called with no write pending."); + } + + SetUserToken(asyncWriteEventArgs, null); + asyncWritePending = false; + + if (CloseStateVal == CloseState.Closed) + { + DisposeWriteEventArgs(); + } + } + } + + void OnSendAsync(object sender, SocketAsyncEventArgs eventArgs) + { + Fx.Assert(eventArgs != null, "Argument 'eventArgs' cannot be NULL."); + CancelSendTimer(); + + try + { + HandleSendAsyncCompleted(); + Fx.Assert(eventArgs.BytesTransferred == asyncWriteEventArgs.Count, "The socket SendAsync did not send all the bytes."); + } + catch (SocketException socketException) + { + asyncWriteException = ConvertSendException(socketException, TimeSpan.MaxValue); + } + catch (Exception exception) + { + if (Fx.IsFatal(exception)) + { + throw; + } + + asyncWriteException = exception; + } + + FinishWrite(); + } + + void HandleSendAsyncCompleted() + { + if (asyncWriteEventArgs.SocketError == SocketError.Success) + { + return; + } + + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SocketException((int)asyncWriteEventArgs.SocketError)); + } + + // This method should be called inside ThisLock + void DisposeWriteEventArgs() + { + if (asyncWriteEventArgs != null) + { + asyncWriteEventArgs.Completed -= onSocketSendCompleted; + asyncWriteEventArgs.Dispose(); + } + } + + void AbortWrite() + { + lock (ThisLock) + { + if (asyncWritePending) + { + if (CloseStateVal != CloseState.Closed) + { + SetUserToken(asyncWriteEventArgs, null); + asyncWritePending = false; + CancelSendTimer(); + } + else + { + DisposeWriteEventArgs(); + } + } + } + } + + void FinishWrite() + { + Action asyncWriteCallback = this.asyncWriteCallback; + object asyncWriteState = this.asyncWriteState; + + this.asyncWriteState = null; + this.asyncWriteCallback = null; + + asyncWriteCallback(asyncWriteState); + } + + public void Write(byte[] buffer, int offset, int size, bool immediate, TimeSpan timeout) + { + // as per http://support.microsoft.com/default.aspx?scid=kb%3ben-us%3b201213 + // we shouldn't write more than 64K synchronously to a socket + const int maxSocketWrite = 64 * 1024; + + ConnectionUtilities.ValidateBufferBounds(buffer, offset, size); + + TimeoutHelper timeoutHelper = new TimeoutHelper(timeout); + try + { + SetImmediate(immediate); + int bytesToWrite = size; + + while (bytesToWrite > 0) + { + SetWriteTimeout(timeoutHelper.RemainingTime(), true); + size = Math.Min(bytesToWrite, maxSocketWrite); + socket.Send(buffer, offset, size, SocketFlags.None); + bytesToWrite -= size; + offset += size; + timeout = timeoutHelper.RemainingTime(); + } + } + catch (SocketException socketException) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelper( + ConvertSendException(socketException, timeoutHelper.RemainingTime()), ExceptionEventType); + } + catch (ObjectDisposedException objectDisposedException) + { + Exception exceptionToThrow = ConvertObjectDisposedException(objectDisposedException, TransferOperation.Write); + if (object.ReferenceEquals(exceptionToThrow, objectDisposedException)) + { + throw; + } + else + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelper(exceptionToThrow, ExceptionEventType); + } + } + } + + public void Write(byte[] buffer, int offset, int size, bool immediate, TimeSpan timeout, BufferManager bufferManager) + { + try + { + Write(buffer, offset, size, immediate, timeout); + } + finally + { + bufferManager.ReturnBuffer(buffer); + } + } + + public int Read(byte[] buffer, int offset, int size, TimeSpan timeout) + { + ConnectionUtilities.ValidateBufferBounds(buffer, offset, size); + ThrowIfNotOpen(); + return ReadCore(buffer, offset, size, timeout, false); + } + + int ReadCore(byte[] buffer, int offset, int size, TimeSpan timeout, bool closing) + { + int bytesRead = 0; + TimeoutHelper timeoutHelper = new TimeoutHelper(timeout); + try + { + SetReadTimeout(timeoutHelper.RemainingTime(), true, closing); + bytesRead = socket.Receive(buffer, offset, size, SocketFlags.None); + } + catch (SocketException socketException) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelper( + ConvertReceiveException(socketException, timeoutHelper.RemainingTime()), ExceptionEventType); + } + catch (ObjectDisposedException objectDisposedException) + { + Exception exceptionToThrow = ConvertObjectDisposedException(objectDisposedException, TransferOperation.Read); + if (object.ReferenceEquals(exceptionToThrow, objectDisposedException)) + { + throw; + } + else + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelper(exceptionToThrow, ExceptionEventType); + } + } + + return bytesRead; + } + + public virtual AsyncCompletionResult BeginRead(int offset, int size, TimeSpan timeout, + Action callback, object state) + { + ConnectionUtilities.ValidateBufferBounds(AsyncReadBufferSize, offset, size); + ThrowIfNotOpen(); + return BeginReadCore(offset, size, timeout, callback, state); + } + + AsyncCompletionResult BeginReadCore(int offset, int size, TimeSpan timeout, + Action callback, object state) + { + bool abortRead = true; + + lock (ThisLock) + { + ThrowIfClosed(); + EnsureReadEventArgs(); + asyncReadState = state; + asyncReadCallback = callback; + SetUserToken(asyncReadEventArgs, this); + asyncReadPending = true; + SetReadTimeout(timeout, false, false); + } + + try + { + if (offset != asyncReadEventArgs.Offset || + size != asyncReadEventArgs.Count) + { + asyncReadEventArgs.SetBuffer(offset, size); + } + + if (ReceiveAsync()) + { + abortRead = false; + return AsyncCompletionResult.Queued; + } + + HandleReceiveAsyncCompleted(); + asyncReadSize = asyncReadEventArgs.BytesTransferred; + + abortRead = false; + return AsyncCompletionResult.Completed; + } + catch (SocketException socketException) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelper(ConvertReceiveException(socketException, TimeSpan.MaxValue), ExceptionEventType); + } + catch (ObjectDisposedException objectDisposedException) + { + Exception exceptionToThrow = ConvertObjectDisposedException(objectDisposedException, TransferOperation.Read); + if (object.ReferenceEquals(exceptionToThrow, objectDisposedException)) + { + throw; + } + else + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelper(exceptionToThrow, ExceptionEventType); + } + } + finally + { + if (abortRead) + { + AbortRead(); + } + } + } + + bool ReceiveAsync() + { + return socket.ReceiveAsync(asyncReadEventArgs); + } + + void OnReceiveAsync(object sender, SocketAsyncEventArgs eventArgs) + { + Fx.Assert(eventArgs != null, "Argument 'eventArgs' cannot be NULL."); + CancelReceiveTimer(); + + try + { + HandleReceiveAsyncCompleted(); + asyncReadSize = eventArgs.BytesTransferred; + } + catch (SocketException socketException) + { + asyncReadException = ConvertReceiveException(socketException, TimeSpan.MaxValue); + } + catch (Exception exception) + { + asyncReadException = exception; + if (Fx.IsFatal(exception)) + { + throw; + } + } + + FinishRead(); + } + + void HandleReceiveAsyncCompleted() + { + if (asyncReadEventArgs.SocketError == SocketError.Success) + { + return; + } + + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SocketException((int)asyncReadEventArgs.SocketError)); + } + + void FinishRead() + { + Action asyncReadCallback = this.asyncReadCallback; + object asyncReadState = this.asyncReadState; + + this.asyncReadState = null; + this.asyncReadCallback = null; + + asyncReadCallback(asyncReadState); + } + + // Both BeginRead/ReadAsync paths completed themselves. EndRead's only job is to deliver the result. + public int EndRead() + { + if (asyncReadException != null) + { + AbortRead(); + throw DiagnosticUtility.ExceptionUtility.ThrowHelper(asyncReadException, ExceptionEventType); + } + + lock (ThisLock) + { + if (!asyncReadPending) + { + throw Fx.AssertAndThrow("SocketConnection.EndRead called with no read pending."); + } + + SetUserToken(asyncReadEventArgs, null); + asyncReadPending = false; + + if (CloseStateVal == CloseState.Closed) + { + DisposeReadEventArgs(); + } + } + + return asyncReadSize; + } + + // This method should be called inside ThisLock + void DisposeReadEventArgs() + { + Fx.Assert(Monitor.IsEntered(ThisLock), "Lock must be taken"); + if (asyncReadEventArgs != null) + { + asyncReadEventArgs.Completed -= onReceiveAsyncCompleted; + asyncReadEventArgs.Dispose(); + } + + // We release the buffer only if there is no outstanding I/O + TryReturnReadBuffer(); + } + + void TryReturnReadBuffer() + { + // The buffer must not be returned and nulled when an abort occurs. Since the buffer + // is also accessed by higher layers, code that has not yet realized the stack is + // aborted may be attempting to read from the buffer. + if (readBuffer != null && !aborted) + { + connectionBufferPool.Return(readBuffer); + readBuffer = null; + } + } + + void SetUserToken(SocketAsyncEventArgs args, object userToken) + { + // The socket args can be pinned by the overlapped callback. Ensure SocketConnection is + // only pinned when there is outstanding IO. + if (args != null) + { + args.UserToken = userToken; + } + } + + void SetImmediate(bool immediate) + { + if (immediate != noDelay) + { + lock (ThisLock) + { + ThrowIfNotOpen(); + socket.NoDelay = immediate; + } + noDelay = immediate; + } + } + + void SetReadTimeout(TimeSpan timeout, bool synchronous, bool closing) + { + if (synchronous) + { + CancelReceiveTimer(); + + // 0 == infinite for winsock timeouts, so we should preempt and throw + if (timeout <= TimeSpan.Zero) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelper( + new TimeoutException(SR.Format(SR.TcpConnectionTimedOut, timeout)), ExceptionEventType); + } + + if (UpdateTimeout(receiveTimeout, timeout)) + { + lock (ThisLock) + { + if (!closing || CloseStateVal != CloseState.Closing) + { + ThrowIfNotOpen(); + } + socket.ReceiveTimeout = TimeoutHelper.ToMilliseconds(timeout); + } + receiveTimeout = timeout; + } + } + else + { + receiveTimeout = timeout; + if (timeout == TimeSpan.MaxValue) + { + CancelReceiveTimer(); + } + else + { + ReceiveTimer.Set(timeout); + } + } + } + + void SetWriteTimeout(TimeSpan timeout, bool synchronous) + { + if (synchronous) + { + CancelSendTimer(); + + // 0 == infinite for winsock timeouts, so we should preempt and throw + if (timeout <= TimeSpan.Zero) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelper( + new TimeoutException(SR.Format(SR.TcpConnectionTimedOut, timeout)), ExceptionEventType); + } + + if (UpdateTimeout(sendTimeout, timeout)) + { + lock (ThisLock) + { + ThrowIfNotOpen(); + socket.SendTimeout = TimeoutHelper.ToMilliseconds(timeout); + } + sendTimeout = timeout; + } + } + else + { + sendTimeout = timeout; + if (timeout == TimeSpan.MaxValue) + { + CancelSendTimer(); + } + else + { + SendTimer.Set(timeout); + } + } + } + + bool UpdateTimeout(TimeSpan oldTimeout, TimeSpan newTimeout) + { + if (oldTimeout == newTimeout) + { + return false; + } + + long threshold = oldTimeout.Ticks / 10; + long delta = Math.Max(oldTimeout.Ticks, newTimeout.Ticks) - Math.Min(oldTimeout.Ticks, newTimeout.Ticks); + + return delta > threshold; + } + + // This method should be called inside ThisLock + void EnsureReadEventArgs() + { + if (asyncReadEventArgs == null) + { + // Init ReadAsync state + if (onReceiveAsyncCompleted == null) + { + onReceiveAsyncCompleted = new EventHandler(OnReceiveAsyncCompleted); + } + + asyncReadEventArgs = new SocketAsyncEventArgs(); + asyncReadEventArgs.SetBuffer(readBuffer, 0, readBuffer.Length); + asyncReadEventArgs.Completed += onReceiveAsyncCompleted; + } + } + + // This method should be called inside ThisLock + void EnsureWriteEventArgs() + { + if (asyncWriteEventArgs == null) + { + // Init SendAsync state + if (onSocketSendCompleted == null) + { + onSocketSendCompleted = new EventHandler(OnSendAsyncCompleted); + } + + asyncWriteEventArgs = new SocketAsyncEventArgs(); + asyncWriteEventArgs.Completed += onSocketSendCompleted; + } + } + + enum CloseState + { + Open, + Closing, + Closed, + } + + enum TransferOperation + { + Write, + Read, + Undefined, + } + } + + internal interface ISocketListenerSettings + { + int BufferSize { get; } + int ListenBacklog { get; } + } + + class SocketConnectionListener : IConnectionListener + { + IPEndPoint localEndpoint; + bool isDisposed; + bool isListening; + Socket listenSocket; + ISocketListenerSettings settings; + bool useOnlyOverlappedIO; + ConnectionBufferPool connectionBufferPool; + SocketAsyncEventArgsPool socketAsyncEventArgsPool; + AsyncLock asyncLock = new AsyncLock(); + + static EventHandler s_acceptAsyncCompleted = new EventHandler(AcceptAsyncCompleted); + + public SocketConnectionListener(Socket listenSocket, ISocketListenerSettings settings, bool useOnlyOverlappedIO) + : this(settings, useOnlyOverlappedIO) + { + this.listenSocket = listenSocket; + } + + public SocketConnectionListener(IPEndPoint localEndpoint, ISocketListenerSettings settings, bool useOnlyOverlappedIO) + : this(settings, useOnlyOverlappedIO) + { + this.localEndpoint = localEndpoint; + } + + SocketConnectionListener(ISocketListenerSettings settings, bool useOnlyOverlappedIO) + { + Fx.Assert(settings != null, "Input settings should not be null"); + this.settings = settings; + this.useOnlyOverlappedIO = useOnlyOverlappedIO; + connectionBufferPool = new ConnectionBufferPool(settings.BufferSize); + } + + AsyncLock ThisLock + { + get { return asyncLock; } + } + + public async Task AcceptAsync() + { + var socketAsyncEventArgs = TakeSocketAsyncEventArgs(); + socketAsyncEventArgs.Completed += s_acceptAsyncCompleted; + try + { + while (true) + { + try + { + var socket = await InternalAcceptAsync(socketAsyncEventArgs); + if (socket == null) + return null; + + return new SocketConnection(socket, connectionBufferPool, true); + } + catch (SocketException socketException) + { + if (ShouldAcceptRecover(socketException)) + { + continue; + } + else + { + throw; + } + } + } + } + finally + { + Fx.Assert(socketAsyncEventArgs.UserToken == null, "UserToken should be nulled out by the same method that sets it"); + socketAsyncEventArgs.Completed -= s_acceptAsyncCompleted; + ReturnSocketAsyncEventArgs(socketAsyncEventArgs); + } + } + + internal async Task InternalAcceptAsync(SocketAsyncEventArgs socketAsyncEventArgs) + { + using (await ThisLock.TakeLockAsync()) + { + if (isDisposed) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ObjectDisposedException(GetType().ToString(), SR.SocketListenerDisposed)); + } + + if (!isListening) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.SocketListenerNotListening)); + } + + return await DoAcceptAsync(socketAsyncEventArgs); + } + } + + private async Task DoAcceptAsync(SocketAsyncEventArgs socketAsyncEventArgs) + { + SocketAsyncEventArgsPool.CleanupAcceptSocket(socketAsyncEventArgs); + + var tcs = new TaskCompletionSource(this); + socketAsyncEventArgs.UserToken = tcs; + + if (!listenSocket.AcceptAsync(socketAsyncEventArgs)) + { + HandleAcceptAsyncCompleted(socketAsyncEventArgs); + } + + Socket result; + try + { + result = await tcs.Task; + } + catch(Exception e) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(e); + } + finally + { + socketAsyncEventArgs.UserToken = null; + } + + return result; + } + + SocketException HandleAcceptAsyncCompleted(SocketAsyncEventArgs socketAsyncEventArgs) + { + SocketException completionException = null; + var tcs = (TaskCompletionSource)socketAsyncEventArgs.UserToken; + if (socketAsyncEventArgs.SocketError == SocketError.Success) + { + var socket = socketAsyncEventArgs.AcceptSocket; + socketAsyncEventArgs.AcceptSocket = null; + tcs.SetResult(socket); + } + else + { + completionException = new SocketException((int)socketAsyncEventArgs.SocketError); + tcs.TrySetException(completionException); + } + + return completionException; + } + + private static void AcceptAsyncCompleted(object sender, SocketAsyncEventArgs e) + { + var tcs = (TaskCompletionSource)e.UserToken; + var thisPtr = (SocketConnectionListener)tcs.Task.AsyncState; + thisPtr.HandleAcceptAsyncCompleted(e); + } + + static bool ShouldAcceptRecover(SocketException exception) + { + return ( + (exception.ErrorCode == NativeSocketErrors.WSAECONNRESET) || + (exception.ErrorCode == NativeSocketErrors.WSAEMFILE) || + (exception.ErrorCode == NativeSocketErrors.WSAENOBUFS) || + (exception.ErrorCode == NativeSocketErrors.WSAETIMEDOUT) + ); + } + + SocketAsyncEventArgs TakeSocketAsyncEventArgs() + { + return socketAsyncEventArgsPool.Take(); + } + + void ReturnSocketAsyncEventArgs(SocketAsyncEventArgs socketAsyncEventArgs) + { + Fx.Assert(socketAsyncEventArgsPool != null, "The socketAsyncEventArgsPool should not be null"); + socketAsyncEventArgsPool.Return(socketAsyncEventArgs); + } + + // This is the buffer size that is used by the System.Net for accepting new connections + static int GetAcceptBufferSize(Socket listenSocket) + { + return (listenSocket.LocalEndPoint.Serialize().Size + 16) * 2; + } + + public void Dispose() + { + using (ThisLock.TakeLock()) + { + if (!isDisposed) + { + if (listenSocket != null) + { + listenSocket.Close(); + } + + if (socketAsyncEventArgsPool != null) + { + socketAsyncEventArgsPool.Close(); + } + + isDisposed = true; + } + } + } + + + public void Listen() + { + // If you call listen() on a port, then kill the process, then immediately start a new process and + // try to listen() on the same port, you sometimes get WSAEADDRINUSE. Even if nothing was accepted. + // Ports don't immediately free themselves on process shutdown. We call listen() in a loop on a delay + // for a few iterations for this reason. + // + TimeSpan listenTimeout = TimeSpan.FromSeconds(1); + BackoffTimeoutHelper backoffHelper = new BackoffTimeoutHelper(listenTimeout); + + lock (ThisLock) + { + if (listenSocket != null) + { + listenSocket.Listen(settings.ListenBacklog); + isListening = true; + } + + while (!isListening) + { + try + { + listenSocket = new Socket(localEndpoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); + listenSocket.Bind(localEndpoint); + listenSocket.Listen(settings.ListenBacklog); + isListening = true; + } + catch (SocketException socketException) + { + bool retry = false; + + if (socketException.ErrorCode == NativeSocketErrors.WSAEADDRINUSE) + { + if (!backoffHelper.IsExpired()) + { + backoffHelper.WaitAndBackoff(); + retry = true; + } + } + + if (!retry) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + SocketConnectionListener.ConvertListenException(socketException, localEndpoint)); + } + } + } + + socketAsyncEventArgsPool = new SocketAsyncEventArgsPool(GetAcceptBufferSize(listenSocket)); + } + } + + public static Exception ConvertListenException(SocketException socketException, IPEndPoint localEndpoint) + { + if (socketException.ErrorCode == NativeSocketErrors.ERROR_INVALID_HANDLE) + { + return new CommunicationObjectAbortedException(socketException.Message, socketException); + } + if (socketException.ErrorCode == NativeSocketErrors.WSAEADDRINUSE) + { + return new AddressAlreadyInUseException(SR.Format(SR.TcpAddressInUse, localEndpoint.ToString()), socketException); + } + else + { + return new CommunicationException( + SR.Format(SR.TcpListenError, socketException.ErrorCode, socketException.Message, localEndpoint.ToString()), + socketException); + } + } + } + + internal static class NativeSocketErrors + { + public const int ERROR_INVALID_HANDLE = 6; + + //public const int WSAACCESS = 10013; + public const int WSAEMFILE = 10024; + //public const int WSAEMSGSIZE = 10040; + public const int WSAEADDRINUSE = 10048; + //public const int WSAEADDRNOTAVAIL = 10049; + //public const int WSAENETDOWN = 10050; + //public const int WSAENETUNREACH = 10051; + public const int WSAENETRESET = 10052; + public const int WSAECONNABORTED = 10053; + public const int WSAECONNRESET = 10054; + public const int WSAENOBUFS = 10055; + //public const int WSAESHUTDOWN = 10058; + public const int WSAETIMEDOUT = 10060; + //public const int WSAECONNREFUSED = 10061; + //public const int WSAEHOSTDOWN = 10064; + //public const int WSAEHOSTUNREACH = 10065; + } +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Channels/SynchronizedMessageSource.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/SynchronizedMessageSource.cs new file mode 100644 index 000000000..bbce7319d --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/SynchronizedMessageSource.cs @@ -0,0 +1,66 @@ +using System; +using CoreWCF.Runtime; +using CoreWCF; +using System.Threading; +using System.Threading.Tasks; + +namespace CoreWCF.Channels +{ + class SynchronizedMessageSource + { + IMessageSource source; + SemaphoreSlim sourceLock; + + public SynchronizedMessageSource(IMessageSource source) + { + this.source = source; + sourceLock = new SemaphoreSlim(1); + } + + public async Task WaitForMessageAsync(CancellationToken token) + { + bool lockAquired = false; + try + { + await sourceLock.WaitAsync(token); + lockAquired = true; + return await source.WaitForMessageAsync(token); + } + catch (OperationCanceledException) + { + // TODO: Fix the timeout value reported + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new TimeoutException( + SR.Format(SR.WaitForMessageTimedOut, TimeSpan.Zero), + TimeoutHelper.CreateEnterTimedOutException(TimeSpan.Zero))); + } + finally + { + if(lockAquired) + sourceLock.Release(); + } + } + + public async Task ReceiveAsync(CancellationToken token) + { + bool lockAquired = false; + try + { + await sourceLock.WaitAsync(token); + lockAquired = true; + return await source.ReceiveAsync(token); + } + catch (OperationCanceledException) + { + // TODO: Fix the timeout value reported + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new TimeoutException( + SR.Format(SR.ReceiveTimedOut2, TimeSpan.Zero), + TimeoutHelper.CreateEnterTimedOutException(TimeSpan.Zero))); + } + finally + { + if(lockAquired) + sourceLock.Release(); + } + } + } +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Channels/TcpChannelListener.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/TcpChannelListener.cs new file mode 100644 index 000000000..a0e512e65 --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/TcpChannelListener.cs @@ -0,0 +1,343 @@ +using System.Diagnostics; +using System.Net; +using System.Net.Sockets; +using System.Runtime; +using System.Security.Authentication.ExtendedProtection; +using CoreWCF; +using CoreWCF.Description; +using CoreWCF.Diagnostics; +using System.Threading; +using System.Threading.Tasks; +using CoreWCF.Runtime; +using System; +using CoreWCF.Channels; + +namespace CoreWCF.Channels +{ + static class TcpUri + { + public const int DefaultPort = 808; + } + + abstract class TcpChannelListener + : TcpChannelListener, IChannelListener + where TChannel : class, IChannel + where TChannelAcceptor : ChannelAcceptor + { + protected TcpChannelListener(TcpTransportBindingElement bindingElement, BindingContext context) + : base(bindingElement, context) + { + } + + protected abstract TChannelAcceptor ChannelAcceptor { get; } + + protected override async Task OnOpenAsync(CancellationToken token) + { + await base.OnOpenAsync(token); + token.ThrowIfCancellationRequested(); + await ChannelAcceptor.OpenAsync(token); + } + + protected override async Task OnCloseAsync(CancellationToken token) + { + await ChannelAcceptor.CloseAsync(token); + await base.OnCloseAsync(token); + } + + protected override void OnAbort() + { + ChannelAcceptor.Abort(); + base.OnAbort(); + } + + public Task AcceptChannelAsync() + { + return AcceptChannelAsync(new TimeoutHelper(DefaultReceiveTimeout).GetCancellationToken()); + } + + public Task AcceptChannelAsync(CancellationToken token) + { + base.ThrowIfNotOpened(); + return ChannelAcceptor.AcceptChannelAsync(token); + } + } + + class TcpReplyChannelListener + : TcpChannelListener, ISingletonChannelListener + { + ReplyChannelAcceptor replyAcceptor; + + public TcpReplyChannelListener(TcpTransportBindingElement bindingElement, BindingContext context) + : base(bindingElement, context) + { + replyAcceptor = new ConnectionOrientedTransportReplyChannelAcceptor(this); + } + + protected override ReplyChannelAcceptor ChannelAcceptor + { + get { return replyAcceptor; } + } + + TimeSpan ISingletonChannelListener.ReceiveTimeout + { + get { return DefaultReceiveTimeout; } + } + + void ISingletonChannelListener.ReceiveRequest(RequestContext requestContext, Action callback, bool canDispatchOnThisThread) + { + replyAcceptor.Enqueue(requestContext, callback, canDispatchOnThisThread); + } + } + + class TcpDuplexChannelListener + : TcpChannelListener>, ISessionPreambleHandler + { + InputQueueChannelAcceptor duplexAcceptor; + + public TcpDuplexChannelListener(TcpTransportBindingElement bindingElement, BindingContext context) + : base(bindingElement, context) + { + duplexAcceptor = new InputQueueChannelAcceptor(this); + } + + protected override InputQueueChannelAcceptor ChannelAcceptor + { + get { return duplexAcceptor; } + } + + void ISessionPreambleHandler.HandleServerSessionPreamble(ServerSessionPreambleConnectionReader preambleReader, + ConnectionDemuxer connectionDemuxer) + { + IDuplexSessionChannel channel = preambleReader.CreateDuplexSessionChannel( + this, new EndpointAddress(Uri), ExposeConnectionProperty, connectionDemuxer); + + duplexAcceptor.EnqueueAndDispatch(channel, preambleReader.ConnectionDequeuedCallback); + } + } + + abstract class TcpChannelListener : ConnectionOrientedTransportChannelListener + { + int listenBacklog; + + // "port 0" support + Socket ipv4ListenSocket; + Socket ipv6ListenSocket; + ExtendedProtectionPolicy extendedProtectionPolicy; + static Random randomPortGenerator = new Random(AppDomain.CurrentDomain.GetHashCode() | Environment.TickCount); + + static UriPrefixTable transportManagerTable = + new UriPrefixTable(true); + + protected TcpChannelListener(TcpTransportBindingElement bindingElement, BindingContext context) + : base(bindingElement, context) + { + listenBacklog = bindingElement.ListenBacklog; + extendedProtectionPolicy = bindingElement.ExtendedProtectionPolicy; + SetIdleTimeout(ConnectionOrientedTransportDefaults.IdleTimeout); + InitializeMaxPooledConnections(); + + // for exclusive mode, we have "port 0" functionality + if (context.ListenUriMode == ListenUriMode.Unique) + { + SetupUniquePort(context); + } + } + + public int ListenBacklog + { + get + { + return listenBacklog; + } + } + + public override T GetProperty() + { + if (typeof(T) == typeof(ExtendedProtectionPolicy)) + { + return (T)(object)extendedProtectionPolicy; + } + + return base.GetProperty(); + } + + internal Socket GetListenSocket(UriHostNameType ipHostNameType) + { + if (ipHostNameType == UriHostNameType.IPv4) + { + Socket result = ipv4ListenSocket; + ipv4ListenSocket = null; + return result; + } + else // UriHostNameType.IPv6 + { + Socket result = ipv6ListenSocket; + ipv6ListenSocket = null; + return result; + } + } + + public override string Scheme + { + get { return Uri.UriSchemeNetTcp; } + } + + internal static UriPrefixTable StaticTransportManagerTable + { + get + { + return transportManagerTable; + } + } + + internal override UriPrefixTable TransportManagerTable + { + get + { + return transportManagerTable; + } + } + + internal static void FixIpv6Hostname(UriBuilder uriBuilder, Uri originalUri) + { + if (originalUri.HostNameType == UriHostNameType.IPv6) + { + string ipv6Host = originalUri.DnsSafeHost; + uriBuilder.Host = string.Concat("[", ipv6Host, "]"); + } + } + + internal override ITransportManagerRegistration CreateTransportManagerRegistration() + { + Uri listenUri = BaseUri; + UriBuilder builder = new UriBuilder(listenUri.Scheme, listenUri.Host, listenUri.Port); + TcpChannelListener.FixIpv6Hostname(builder, listenUri); + listenUri = builder.Uri; + + return CreateTransportManagerRegistration(listenUri); + } + + internal override ITransportManagerRegistration CreateTransportManagerRegistration(Uri listenUri) + { + return new ExclusiveTcpTransportManagerRegistration(listenUri, this); + } + + Socket ListenAndBind(IPEndPoint localEndpoint) + { + Socket result = new Socket(localEndpoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); + try + { + result.Bind(localEndpoint); + } + catch (SocketException socketException) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + SocketConnectionListener.ConvertListenException(socketException, localEndpoint)); + } + + return result; + } + + void SetupUniquePort(BindingContext context) + { + IPAddress ipv4Address = IPAddress.Any; + IPAddress ipv6Address = IPAddress.IPv6Any; + + bool useIPv4 = Socket.OSSupportsIPv4; + bool useIPv6 = Socket.OSSupportsIPv6; + if (Uri.HostNameType == UriHostNameType.IPv6) + { + useIPv4 = false; + ipv6Address = IPAddress.Parse(Uri.DnsSafeHost); + } + else if (Uri.HostNameType == UriHostNameType.IPv4) + { + useIPv6 = false; + ipv4Address = IPAddress.Parse(Uri.DnsSafeHost); + } + + if (!useIPv4 && !useIPv6) + { + if (Uri.HostNameType == UriHostNameType.IPv6) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument( + "context", + SR.Format(SR.TcpV6AddressInvalid, Uri)); + } + else + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument( + "context", + SR.Format(SR.TcpV4AddressInvalid, Uri)); + } + } + + UriBuilder uriBuilder = new UriBuilder(context.ListenUriBaseAddress); + int port = -1; + if (!useIPv6) // we just want IPv4 + { + ipv4ListenSocket = ListenAndBind(new IPEndPoint(ipv4Address, 0)); + port = ((IPEndPoint)ipv4ListenSocket.LocalEndPoint).Port; + } + else if (!useIPv4) // or just IPv6 + { + ipv6ListenSocket = ListenAndBind(new IPEndPoint(ipv6Address, 0)); + port = ((IPEndPoint)ipv6ListenSocket.LocalEndPoint).Port; + } + else + { + // We need both IPv4 and IPv6 on the same port. We can't atomically bind for IPv4 and IPv6, + // so we try 10 times, which even with a 50% failure rate will statistically succeed 99.9% of the time. + // + // We look in the range of 49152-65534 for Vista default behavior parity. + // http://www.iana.org/assignments/port-numbers + // + // We also grab the 10 random numbers in a row to reduce collisions between multiple people somehow + // colliding on the same seed. + const int retries = 10; + const int lowWatermark = 49152; + const int highWatermark = 65535; + + int[] portNumbers = new int[retries]; + lock (randomPortGenerator) + { + for (int i = 0; i < retries; i++) + { + portNumbers[i] = randomPortGenerator.Next(lowWatermark, highWatermark); + } + } + + + for (int i = 0; i < retries; i++) + { + port = portNumbers[i]; + try + { + ipv4ListenSocket = ListenAndBind(new IPEndPoint(ipv4Address, port)); + ipv6ListenSocket = ListenAndBind(new IPEndPoint(ipv6Address, port)); + break; + } + catch (AddressAlreadyInUseException e) + { + DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); + if (ipv4ListenSocket != null) + { + ipv4ListenSocket.Close(); + ipv4ListenSocket = null; + } + ipv6ListenSocket = null; + } + } + + if (ipv4ListenSocket == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new AddressAlreadyInUseException(SR.Format(SR.UniquePortNotAvailable))); + } + } + + uriBuilder.Port = port; + base.SetUri(uriBuilder.Uri, context.ListenUriRelativeAddress); + } + } +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Channels/TcpTransportBindingElement.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/TcpTransportBindingElement.cs new file mode 100644 index 000000000..6abee5279 --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/TcpTransportBindingElement.cs @@ -0,0 +1,132 @@ +using CoreWCF.Channels; +using System; +using System.ComponentModel; +using System.Security.Authentication.ExtendedProtection; + +namespace CoreWCF.Channels +{ + public partial class TcpTransportBindingElement : ConnectionOrientedTransportBindingElement + { + int listenBacklog; + ExtendedProtectionPolicy extendedProtectionPolicy; + + public TcpTransportBindingElement() : base() + { + listenBacklog = TcpTransportDefaults.GetListenBacklog(); + extendedProtectionPolicy = ChannelBindingUtility.DefaultPolicy; + } + protected TcpTransportBindingElement(TcpTransportBindingElement elementToBeCloned) : base(elementToBeCloned) + { + listenBacklog = elementToBeCloned.listenBacklog; + extendedProtectionPolicy = elementToBeCloned.ExtendedProtectionPolicy; + } + + public int ListenBacklog + { + get + { + return listenBacklog; + } + + set + { + if (value <= 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", + SR.ValueMustBePositive)); + } + + listenBacklog = value; + } + } + + public override string Scheme + { + get { return "net.tcp"; } + } + + public ExtendedProtectionPolicy ExtendedProtectionPolicy + { + get + { + return extendedProtectionPolicy; + } + set + { + if (value == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value"); + } + + if (value.PolicyEnforcement == PolicyEnforcement.Always && + !ExtendedProtectionPolicy.OSSupportsExtendedProtection) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new PlatformNotSupportedException(SR.ExtendedProtectionNotSupported)); + } + + extendedProtectionPolicy = value; + } + } + + public override BindingElement Clone() + { + return new TcpTransportBindingElement(this); + } + + public override IChannelListener BuildChannelListener(BindingContext context) + { + if (context == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("context"); + } + + if (!CanBuildChannelListener(context)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("TChannel", SR.Format(SR.ChannelTypeNotSupported, typeof(TChannel))); + } + + TcpChannelListener listener; + if (typeof(TChannel) == typeof(IReplyChannel)) + { + listener = new TcpReplyChannelListener(this, context); + } + else if (typeof(TChannel) == typeof(IDuplexSessionChannel)) + { + listener = new TcpDuplexChannelListener(this, context); + } + else + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("TChannel", SR.Format(SR.ChannelTypeNotSupported, typeof(TChannel))); + } + + return (IChannelListener)(object)listener; + } + + public override T GetProperty(BindingContext context) + { + if (context == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("context"); + } + // TODO: Decide whether to support DeliveryRequirementsAttribute + //if (typeof(T) == typeof(IBindingDeliveryCapabilities)) + //{ + // return (T)(object)new BindingDeliveryCapabilitiesHelper(); + //} + else if (typeof(T) == typeof(ExtendedProtectionPolicy)) + { + return (T)(object)ExtendedProtectionPolicy; + } + // TODO: Support ITransportCompressionSupport + //else if (typeof(T) == typeof(ITransportCompressionSupport)) + //{ + // return (T)(object)new TransportCompressionSupportHelper(); + //} + else + { + return base.GetProperty(context); + } + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Channels/TcpTransportManager.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/TcpTransportManager.cs new file mode 100644 index 000000000..559022d07 --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/TcpTransportManager.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoreWCF.Channels +{ + abstract class TcpTransportManager : ConnectionOrientedTransportManager + { + internal TcpTransportManager() + { + } + + internal override string Scheme + { + get { return Uri.UriSchemeNetTcp; } + } + + protected virtual bool IsCompatible(TcpChannelListener channelListener) + { + return base.IsCompatible(channelListener); + } + } +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Channels/TimeoutStream.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/TimeoutStream.cs new file mode 100644 index 000000000..00ca4f752 --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/TimeoutStream.cs @@ -0,0 +1,100 @@ +using System; +using System.Diagnostics.Contracts; +using System.IO; +using CoreWCF.Runtime; +using System.Threading; +using System.Threading.Tasks; + +namespace CoreWCF.Channels +{ + // Enforces an overall timeout based on the TimeoutHelper passed in + internal class TimeoutStream : DelegatingStream + { + private TimeoutHelper _timeoutHelper; + private bool _disposed; + private byte[] _oneByteArray = new byte[1]; + + public TimeoutStream(Stream stream, TimeSpan timeout) + : base(stream) + { + if (!stream.CanTimeout) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("stream", SR.StreamDoesNotSupportTimeout); + } + + _timeoutHelper = new TimeoutHelper(timeout); + ReadTimeout = TimeoutHelper.ToMilliseconds(timeout); + WriteTimeout = ReadTimeout; + } + + public override int Read(byte[] buffer, int offset, int count) + { + return ReadAsyncInternal(buffer, offset, count, CancellationToken.None).WaitForCompletion(); + } + + public override int ReadByte() + { + int r = Read(_oneByteArray, 0, 1); + if (r == 0) + return -1; + return _oneByteArray[0]; + } + + public override async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + // Supporting a passed in cancellationToken as well as honoring the timeout token in this class would require + // creating a linked token source on every call which is extra allocation and needs disposal. As this is an + // internal classs, it's okay to add this extra constraint to usage of this method. + Fx.Assert(!cancellationToken.CanBeCanceled, "cancellationToken shouldn't be cancellable"); + var cancelToken = _timeoutHelper.GetCancellationToken(); + return await base.ReadAsync(buffer, offset, count, cancelToken); + } + + private async Task ReadAsyncInternal(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + await TaskHelpers.EnsureDefaultTaskScheduler(); + return await ReadAsync(buffer, offset, count, cancellationToken); + } + + public override void Write(byte[] buffer, int offset, int count) + { + WriteAsyncInternal(buffer, offset, count, CancellationToken.None).WaitForCompletion(); + } + + public override void WriteByte(byte value) + { + _oneByteArray[0] = value; + Write(_oneByteArray, 0, 1); + } + + public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + // Supporting a passed in cancellationToken as well as honoring the timeout token in this class would require + // creating a linked token source on every call which is extra allocation and needs disposal. As this is an + // internal classs, it's okay to add this extra constraint to usage of this method. + Fx.Assert(!cancellationToken.CanBeCanceled, "cancellationToken shouldn't be cancellable"); + var cancelToken = _timeoutHelper.GetCancellationToken(); + await base.WriteAsync(buffer, offset, count, cancelToken); + } + + private async Task WriteAsyncInternal(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + await TaskHelpers.EnsureDefaultTaskScheduler(); + await WriteAsync(buffer, offset, count, cancellationToken); + } + + protected override void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + _timeoutHelper = default(TimeoutHelper); + } + + _disposed = true; + } + base.Dispose(disposing); + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Channels/TransportChannelListener.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/TransportChannelListener.cs new file mode 100644 index 000000000..30ebb3ace --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/TransportChannelListener.cs @@ -0,0 +1,611 @@ +using CoreWCF.Runtime; +using CoreWCF.Description; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Net; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace CoreWCF.Channels +{ + abstract class TransportChannelListener + : ChannelListenerBase, ITransportFactorySettings + { + // Double-checked locking pattern requires volatile for read/write synchronization + static volatile bool addressPrefixesInitialized = false; + static volatile string exactGeneratedAddressPrefix; + static volatile string strongWildcardGeneratedAddressPrefix; + static volatile string weakWildcardGeneratedAddressPrefix; + static object staticLock = new object(); + + Uri baseUri; + BufferManager bufferManager; + HostNameComparisonMode hostNameComparisonMode; + bool inheritBaseAddressSettings; + bool manualAddressing; + long maxBufferPoolSize; + long maxReceivedMessageSize; + MessageEncoderFactory messageEncoderFactory; + MessageVersion messageVersion; + Uri uri; + string hostedVirtualPath; + Action messageReceivedCallback; + TransportManagerContainer transportManagerContainer; + + protected TransportChannelListener(TransportBindingElement bindingElement, BindingContext context) + : this(bindingElement, context, TransportDefaults.GetDefaultMessageEncoderFactory()) + { + } + + protected TransportChannelListener(TransportBindingElement bindingElement, BindingContext context, + MessageEncoderFactory defaultMessageEncoderFactory) + : this(bindingElement, context, defaultMessageEncoderFactory, TransportDefaults.HostNameComparisonMode) + { + } + + protected TransportChannelListener(TransportBindingElement bindingElement, BindingContext context, + HostNameComparisonMode hostNameComparisonMode) + : this(bindingElement, context, TransportDefaults.GetDefaultMessageEncoderFactory(), hostNameComparisonMode) + { + } + + protected TransportChannelListener(TransportBindingElement bindingElement, BindingContext context, + MessageEncoderFactory defaultMessageEncoderFactory, HostNameComparisonMode hostNameComparisonMode) + : base(context.Binding) + { + HostNameComparisonModeHelper.Validate(hostNameComparisonMode); + this.hostNameComparisonMode = hostNameComparisonMode; + manualAddressing = bindingElement.ManualAddressing; + maxBufferPoolSize = bindingElement.MaxBufferPoolSize; + maxReceivedMessageSize = bindingElement.MaxReceivedMessageSize; + + Collection messageEncoderBindingElements + = context.BindingParameters.FindAll(); + + if (messageEncoderBindingElements.Count > 1) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.MultipleMebesInParameters)); + } + else if (messageEncoderBindingElements.Count == 1) + { + messageEncoderFactory = messageEncoderBindingElements[0].CreateMessageEncoderFactory(); + context.BindingParameters.Remove(); + } + else + { + messageEncoderFactory = defaultMessageEncoderFactory; + } + + if (null != messageEncoderFactory) + messageVersion = messageEncoderFactory.MessageVersion; + else + messageVersion = MessageVersion.None; + + if ((context.ListenUriMode == ListenUriMode.Unique) && (context.ListenUriBaseAddress == null)) + { + UriBuilder uriBuilder = new UriBuilder(Scheme, DnsCache.MachineName); + uriBuilder.Path = GeneratedAddressPrefix; + context.ListenUriBaseAddress = uriBuilder.Uri; + } + + UriHelper.ValidateBaseAddress(context.ListenUriBaseAddress, "baseAddress"); + if (context.ListenUriBaseAddress.Scheme != Scheme) + { + // URI schemes are case-insensitive, so try a case insensitive compare now + if (string.Compare(context.ListenUriBaseAddress.Scheme, Scheme, StringComparison.OrdinalIgnoreCase) != 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument( + "context.ListenUriBaseAddress", + SR.Format(SR.InvalidUriScheme, context.ListenUriBaseAddress.Scheme, Scheme)); + } + } + + Fx.Assert(context.ListenUriRelativeAddress != null, ""); // validated by BindingContext + if (context.ListenUriMode == ListenUriMode.Explicit) + { + SetUri(context.ListenUriBaseAddress, context.ListenUriRelativeAddress); + } + else // ListenUriMode.Unique: + { + string relativeAddress = context.ListenUriRelativeAddress; + if (relativeAddress.Length > 0 && !relativeAddress.EndsWith("/", StringComparison.Ordinal)) + { + relativeAddress += "/"; + } + + SetUri(context.ListenUriBaseAddress, relativeAddress + Guid.NewGuid().ToString()); + } + + transportManagerContainer = new TransportManagerContainer(this); + } + + internal Uri BaseUri + { + get + { + return baseUri; + } + } + + string GeneratedAddressPrefix + { + get + { + EnsureAddressPrefixesInitialized(); + + // We use different address prefixes based on hostname comparison mode in order to avoid creating + // starved reservations. For example, if we register http://+:80/TLA/G1 and http://*:80/TLA/G1, the + // latter will never receive any traffic. We handle this case by instead using http://+:80/TLA/G1 + // and http://*:80/TLA/G2. + switch (hostNameComparisonMode) + { + case HostNameComparisonMode.Exact: + return exactGeneratedAddressPrefix; + case HostNameComparisonMode.StrongWildcard: + return strongWildcardGeneratedAddressPrefix; + case HostNameComparisonMode.WeakWildcard: + return weakWildcardGeneratedAddressPrefix; + default: + Fx.Assert("invalid HostnameComparisonMode value"); + return null; + } + } + } + + internal string HostedVirtualPath + { + get + { + return hostedVirtualPath; + } + } + + internal bool InheritBaseAddressSettings + { + get + { + return inheritBaseAddressSettings; + } + + set + { + inheritBaseAddressSettings = value; + } + } + + public BufferManager BufferManager + { + get + { + return bufferManager; + } + } + + internal HostNameComparisonMode HostNameComparisonModeInternal + { + get + { + return hostNameComparisonMode; + } + } + + public bool ManualAddressing + { + get + { + return manualAddressing; + } + } + + public long MaxBufferPoolSize + { + get + { + return maxBufferPoolSize; + } + } + + public virtual long MaxReceivedMessageSize + { + get + { + return maxReceivedMessageSize; + } + } + + public MessageEncoderFactory MessageEncoderFactory + { + get + { + return messageEncoderFactory; + } + } + + public MessageVersion MessageVersion + { + get + { + return messageVersion; + } + } + + internal abstract UriPrefixTable TransportManagerTable + { + get; + } + + public abstract string Scheme { get; } + + public override Uri Uri + { + get + { + return uri; + } + } + + public override T GetProperty() + { + if (typeof(T) == typeof(MessageVersion)) + { + return (T)(object)MessageVersion; + } + + if (typeof(T) == typeof(FaultConverter)) + { + if (null == MessageEncoderFactory) + return null; + else + return MessageEncoderFactory.Encoder.GetProperty(); + } + + if (typeof(T) == typeof(ITransportFactorySettings)) + { + return (T)(object)this; + } + + return base.GetProperty(); + } + + internal bool IsScopeIdCompatible(HostNameComparisonMode hostNameComparisonMode, Uri uri) + { + if (this.hostNameComparisonMode != hostNameComparisonMode) + { + return false; + } + + if (hostNameComparisonMode == HostNameComparisonMode.Exact && uri.HostNameType == UriHostNameType.IPv6) + { + // the hostname type of the channel listener MUST be IPv6 if we got here. + // as this should have been enforced by UriPrefixTable. + if (Uri.HostNameType != UriHostNameType.IPv6) + { + return false; + } + + IPAddress channelListenerIP = IPAddress.Parse(Uri.DnsSafeHost); + IPAddress otherIP = IPAddress.Parse(uri.DnsSafeHost); + + if (channelListenerIP.ScopeId != otherIP.ScopeId) + { + return false; + } + } + + return true; + } + + internal virtual void ApplyHostedContext(string virtualPath, bool isMetadataListener) + { + // Save the original hosted virtual path. + hostedVirtualPath = virtualPath; + } + + static Uri AddSegment(Uri baseUri, Uri fullUri) + { + Uri result = null; + if (baseUri.AbsolutePath.Length < fullUri.AbsolutePath.Length) + { + UriBuilder builder = new UriBuilder(baseUri); + TcpChannelListener.FixIpv6Hostname(builder, baseUri); + if (!builder.Path.EndsWith("/", StringComparison.Ordinal)) + { + builder.Path = builder.Path + "/"; + baseUri = builder.Uri; + } + Uri relativeUri = baseUri.MakeRelativeUri(fullUri); + string relativePath = relativeUri.OriginalString; + int slashIndex = relativePath.IndexOf('/'); + string segment = (slashIndex == -1) ? relativePath : relativePath.Substring(0, slashIndex); + builder.Path = builder.Path + segment; + result = builder.Uri; + } + return result; + } + + internal virtual ITransportManagerRegistration CreateTransportManagerRegistration() + { + return CreateTransportManagerRegistration(BaseUri); + } + + internal abstract ITransportManagerRegistration CreateTransportManagerRegistration(Uri listenUri); + + static void EnsureAddressPrefixesInitialized() + { + if (!addressPrefixesInitialized) + { + lock (staticLock) + { + if (!addressPrefixesInitialized) + { + // we use the ephemeral namespace prefix plus a GUID for our App-Domain (which is the + // extent to which we can share a TransportManager prefix) + exactGeneratedAddressPrefix = "Temporary_Listen_Addresses/" + Guid.NewGuid().ToString(); + strongWildcardGeneratedAddressPrefix = "Temporary_Listen_Addresses/" + Guid.NewGuid().ToString(); + weakWildcardGeneratedAddressPrefix = "Temporary_Listen_Addresses/" + Guid.NewGuid().ToString(); + addressPrefixesInitialized = true; + } + } + } + } + + internal virtual int GetMaxBufferSize() + { + if (MaxReceivedMessageSize > int.MaxValue) + return int.MaxValue; + else + return (int)MaxReceivedMessageSize; + } + + protected override void OnOpening() + { + base.OnOpening(); + bufferManager = BufferManager.CreateBufferManager(MaxBufferPoolSize, GetMaxBufferSize()); + } + + protected override Task OnOpenAsync(CancellationToken token) + { + return transportManagerContainer.OpenAsync(new SelectTransportManagersCallback(SelectTransportManagers)); + } + + protected override void OnOpened() + { + base.OnOpened(); + } + + internal TransportManagerContainer GetTransportManagers() + { + return TransportManagerContainer.TransferTransportManagers(transportManagerContainer); + } + + protected override void OnAbort() + { + transportManagerContainer.Abort(); + } + + protected override Task OnCloseAsync(CancellationToken token) + { + return transportManagerContainer.CloseAsync(token); + } + + protected override void OnClosed() + { + base.OnClosed(); + if (bufferManager != null) + { + bufferManager.Clear(); + } + } + + bool TryGetTransportManagerRegistration(out ITransportManagerRegistration registration) + { + if (!InheritBaseAddressSettings) + { + return TryGetTransportManagerRegistration(hostNameComparisonMode, out registration); + } + + if (TryGetTransportManagerRegistration(HostNameComparisonMode.StrongWildcard, out registration)) + { + return true; + } + + if (TryGetTransportManagerRegistration(HostNameComparisonMode.Exact, out registration)) + { + return true; + } + + if (TryGetTransportManagerRegistration(HostNameComparisonMode.WeakWildcard, out registration)) + { + return true; + } + + registration = null; + return false; + } + + protected virtual bool TryGetTransportManagerRegistration(HostNameComparisonMode hostNameComparisonMode, + out ITransportManagerRegistration registration) + { + return TransportManagerTable.TryLookupUri(Uri, hostNameComparisonMode, out registration); + } + + // This is virtual so that PeerChannelListener and MsmqChannelListener can override it. + // Will be called under "lock (this.TransportManagerTable)" from TransportManagerContainer.Open + internal virtual IList SelectTransportManagers() + { + IList foundTransportManagers = null; + + // Look up an existing transport manager registration. + ITransportManagerRegistration registration; + if (!TryGetTransportManagerRegistration(out registration)) + { + // Don't create TransportManagerRegistration in hosted case. + if (HostedVirtualPath == null) + { + // Create a new registration at the default point in the URI hierarchy. + registration = CreateTransportManagerRegistration(); + TransportManagerTable.RegisterUri(registration.ListenUri, hostNameComparisonMode, registration); + } + } + + // Use the registration to select/create a set of compatible transport managers. + if (registration != null) + { + foundTransportManagers = registration.Select(this); + if (foundTransportManagers == null) + { + // Don't create TransportManagerRegistration in hosted case. + if (HostedVirtualPath == null) + { + // Create a new registration one segment down from the existing incompatible registration. + Uri nextUri = AddSegment(registration.ListenUri, Uri); + if (nextUri != null) + { + registration = CreateTransportManagerRegistration(nextUri); + TransportManagerTable.RegisterUri(nextUri, hostNameComparisonMode, registration); + foundTransportManagers = registration.Select(this); + } + } + } + } + + if (foundTransportManagers == null) + { + ThrowTransportManagersNotFound(); + } + + return foundTransportManagers; + } + + void ThrowTransportManagersNotFound() + { + if (HostedVirtualPath != null) + { + if ((string.Compare(Uri.Scheme, Uri.UriSchemeHttp, StringComparison.OrdinalIgnoreCase) == 0) || + (string.Compare(Uri.Scheme, Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase) == 0) + ) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new InvalidOperationException( + SR.Format(SR.Hosting_NoHttpTransportManagerForUri, Uri))); + } + else if ((string.Compare(Uri.Scheme, Uri.UriSchemeNetTcp, StringComparison.OrdinalIgnoreCase) == 0) || + (string.Compare(Uri.Scheme, Uri.UriSchemeNetPipe, StringComparison.OrdinalIgnoreCase) == 0) + ) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new InvalidOperationException(SR.Format( + SR.Hosting_NoTcpPipeTransportManagerForUri, Uri))); + } + } + + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format( + SR.NoCompatibleTransportManagerForUri, Uri))); + } + + protected void SetUri(Uri baseAddress, string relativeAddress) + { + Uri fullUri = baseAddress; + + // Ensure that baseAddress Path does end with a slash if we have a relative address + if (relativeAddress != string.Empty) + { + if (!baseAddress.AbsolutePath.EndsWith("/", StringComparison.Ordinal)) + { + UriBuilder uriBuilder = new UriBuilder(baseAddress); + TcpChannelListener.FixIpv6Hostname(uriBuilder, baseAddress); + uriBuilder.Path = uriBuilder.Path + "/"; + baseAddress = uriBuilder.Uri; + } + + fullUri = new Uri(baseAddress, relativeAddress); + + // now see if we need to update our base address (for cases like relative path = "/foo") + if (!baseAddress.IsBaseOf(fullUri)) + { + baseAddress = fullUri; + } + } + + baseUri = baseAddress; + ValidateUri(fullUri); + uri = fullUri; + } + + protected virtual void ValidateUri(Uri uri) + { + } + + long ITransportFactorySettings.MaxReceivedMessageSize + { + get { return MaxReceivedMessageSize; } + } + + BufferManager ITransportFactorySettings.BufferManager + { + get { return BufferManager; } + } + + bool ITransportFactorySettings.ManualAddressing + { + get { return ManualAddressing; } + } + + MessageEncoderFactory ITransportFactorySettings.MessageEncoderFactory + { + get { return MessageEncoderFactory; } + } + + public IAnonymousUriPrefixMatcher AnonymousUriPrefixMatcher => throw new NotImplementedException(); + + internal void SetMessageReceivedCallback(Action messageReceivedCallback) + { + this.messageReceivedCallback = messageReceivedCallback; + } + + internal void RaiseMessageReceived() + { + Action callback = messageReceivedCallback; + if (callback != null) + { + callback(); + } + } + } + + interface ITransportManagerRegistration + { + HostNameComparisonMode HostNameComparisonMode { get; } + Uri ListenUri { get; } + IList Select(TransportChannelListener factory); + } + + abstract class TransportManagerRegistration : ITransportManagerRegistration + { + HostNameComparisonMode hostNameComparisonMode; + Uri listenUri; + + protected TransportManagerRegistration(Uri listenUri, HostNameComparisonMode hostNameComparisonMode) + { + this.listenUri = listenUri; + this.hostNameComparisonMode = hostNameComparisonMode; + } + + public HostNameComparisonMode HostNameComparisonMode + { + get { return hostNameComparisonMode; } + } + + public Uri ListenUri + { + get + { + return listenUri; + } + } + + public abstract IList Select(TransportChannelListener factory); + } +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Channels/TransportDefaults.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/TransportDefaults.cs new file mode 100644 index 000000000..94d0a60ea --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/TransportDefaults.cs @@ -0,0 +1,69 @@ +using CoreWCF; +using System; +using System.Net.Security; +using System.Security.Authentication; + +namespace CoreWCF.Channels +{ + internal static class TransportDefaults + { + internal const bool ExtractGroupsForWindowsAccounts = true; //SspiSecurityTokenProvider.DefaultExtractWindowsGroupClaims; + internal const long MaxReceivedMessageSize = 65536; + internal const HostNameComparisonMode HostNameComparisonMode = CoreWCF.HostNameComparisonMode.Exact; + internal const int MaxDrainSize = (int)MaxReceivedMessageSize; + internal const long MaxBufferPoolSize = 512 * 1024; + internal const int MaxBufferSize = (int)MaxReceivedMessageSize; + + internal const SslProtocols SslProtocols = System.Security.Authentication.SslProtocols.Tls | + System.Security.Authentication.SslProtocols.Tls11 | + System.Security.Authentication.SslProtocols.Tls12; + + internal static MessageEncoderFactory GetDefaultMessageEncoderFactory() + { + return new BinaryMessageEncodingBindingElement().CreateMessageEncoderFactory(); + } + } + + internal static class ConnectionOrientedTransportDefaults + { + internal const int ConnectionBufferSize = 8192; + internal const HostNameComparisonMode HostNameComparisonMode = CoreWCF.HostNameComparisonMode.StrongWildcard; + internal static TimeSpan IdleTimeout { get { return TimeSpan.FromMinutes(2); } } + internal static TimeSpan ChannelInitializationTimeout { get { return TimeSpan.FromSeconds(30); } } + internal const int MaxContentTypeSize = 256; + internal static TimeSpan MaxOutputDelay { get { return TimeSpan.FromMilliseconds(200); } } + + internal const int MaxViaSize = 2048; + internal const ProtectionLevel ProtectionLevel = System.Net.Security.ProtectionLevel.EncryptAndSign; + internal const TransferMode TransferMode = CoreWCF.TransferMode.Buffered; + + internal static int GetMaxConnections() + { + return GetMaxPendingConnections(); + } + + internal static int GetMaxPendingConnections() + { + return 12 * Environment.ProcessorCount; + } + + internal static int GetMaxPendingAccepts() + { + return 2 * Environment.ProcessorCount; + } + } + + static class TcpTransportDefaults + { + internal const int ListenBacklogConst = 0; + internal static TimeSpan ConnectionLeaseTimeout { get { return TimeSpan.FromMinutes(5); } } + internal const string ConnectionLeaseTimeoutString = "00:05:00"; + //internal const bool PortSharingEnabled = false; + //internal const bool TeredoEnabled = false; + + internal static int GetListenBacklog() + { + return 12 * Environment.ProcessorCount; + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Channels/TransportDuplexSessionChannel.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/TransportDuplexSessionChannel.cs new file mode 100644 index 000000000..3cb8299b6 --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/TransportDuplexSessionChannel.cs @@ -0,0 +1,568 @@ +using System; +using System.Diagnostics; +using CoreWCF.Runtime; +using CoreWCF.Runtime.Diagnostics; +using System.Security.Authentication.ExtendedProtection; +using CoreWCF; +using CoreWCF.Diagnostics; +using CoreWCF.Security; +using System.Threading; +using System.Threading.Tasks; + +namespace CoreWCF.Channels +{ + abstract class TransportDuplexSessionChannel : TransportOutputChannel, IDuplexSessionChannel + { + BufferManager bufferManager; + IDuplexSession duplexSession; + bool isInputSessionClosed; + bool isOutputSessionClosed; + MessageEncoder messageEncoder; + SynchronizedMessageSource messageSource; + SecurityMessageProperty remoteSecurity; + EndpointAddress localAddress; + SemaphoreSlim sendLock; + Uri localVia; + ChannelBinding channelBindingToken; + + protected TransportDuplexSessionChannel( + ChannelManagerBase manager, + ITransportFactorySettings settings, + EndpointAddress localAddress, + Uri localVia, + EndpointAddress remoteAddresss, + Uri via) + : base(manager, remoteAddresss, via, settings.ManualAddressing, settings.MessageVersion) + { + this.localAddress = localAddress; + this.localVia = localVia; + bufferManager = settings.BufferManager; + sendLock = new SemaphoreSlim(1); + messageEncoder = settings.MessageEncoderFactory.CreateSessionEncoder(); + Session = new ConnectionDuplexSession(this); + } + + public EndpointAddress LocalAddress + { + get { return localAddress; } + } + + public SecurityMessageProperty RemoteSecurity + { + get { return remoteSecurity; } + protected set { remoteSecurity = value; } + } + + public IDuplexSession Session + { + get { return duplexSession; } + protected set { duplexSession = value; } + } + + public SemaphoreSlim SendLock + { + get { return sendLock; } + } + + protected ChannelBinding ChannelBinding + { + get + { + return channelBindingToken; + } + } + + protected BufferManager BufferManager + { + get + { + return bufferManager; + } + } + + protected Uri LocalVia + { + get { return localVia; } + } + + protected MessageEncoder MessageEncoder + { + get { return messageEncoder; } + set { messageEncoder = value; } + } + + protected SynchronizedMessageSource MessageSource + { + get { return messageSource; } + } + + protected abstract bool IsStreamedOutput { get; } + + public Task ReceiveAsync() + { + var timeoutHelper = new TimeoutHelper(DefaultReceiveTimeout); + return ReceiveAsync(timeoutHelper.GetCancellationToken()); + } + + public async Task ReceiveAsync(CancellationToken token) + { + Message message = null; + if (DoneReceivingInCurrentState()) + { + return null; + } + + bool shouldFault = true; + try + { + message = await messageSource.ReceiveAsync(token); + OnReceiveMessage(message); + shouldFault = false; + return message; + } + finally + { + if (shouldFault) + { + if (message != null) + { + message.Close(); + message = null; + } + + Fault(); + } + } + } + + // TODO: Move these methods which are copied from CommunicationObject to common helper class + internal bool DoneReceivingInCurrentState() + { + ThrowPending(); + + switch (State) + { + case CommunicationState.Created: + throw TraceUtility.ThrowHelperError(CreateNotOpenException(), Guid.Empty, this); + + case CommunicationState.Opening: + throw TraceUtility.ThrowHelperError(CreateNotOpenException(), Guid.Empty, this); + + case CommunicationState.Opened: + return false; + + case CommunicationState.Closing: + return true; + + case CommunicationState.Closed: + return true; + + case CommunicationState.Faulted: + return true; + + default: + throw Fx.AssertAndThrow("DoneReceivingInCurrentState: Unknown CommunicationObject.state"); + } + } + + internal void ThrowIfFaulted() + { + ThrowPending(); + + switch (State) + { + case CommunicationState.Created: + break; + + case CommunicationState.Opening: + break; + + case CommunicationState.Opened: + break; + + case CommunicationState.Closing: + break; + + case CommunicationState.Closed: + break; + + case CommunicationState.Faulted: + throw TraceUtility.ThrowHelperError(CreateFaultedException(), Guid.Empty, this); + + default: + throw Fx.AssertAndThrow("ThrowIfFaulted: Unknown CommunicationObject.state"); + } + } + + internal Exception CreateFaultedException() + { + string message = SR.Format(SR.CommunicationObjectFaulted1, GetCommunicationObjectType().ToString()); + return new CommunicationObjectFaultedException(message); + } + + private Exception CreateNotOpenException() + { + return new InvalidOperationException(SR.Format(SR.CommunicationObjectCannotBeUsed, GetCommunicationObjectType().ToString(), State.ToString())); + } + + public async Task> TryReceiveAsync(CancellationToken token) + { + try + { + var message = await ReceiveAsync(token); + return TryAsyncResult.FromResult(message); + } + catch (TimeoutException e) + { + DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); + return TryAsyncResult.FailedResult; + } + } + + public async Task WaitForMessageAsync(CancellationToken token) + { + if (DoneReceivingInCurrentState()) + { + return true; + } + + bool shouldFault = true; + try + { + bool success = await messageSource.WaitForMessageAsync(token); + shouldFault = !success; // need to fault if we've timed out because we're now toast + return success; + } + finally + { + if (shouldFault) + { + Fault(); + } + } + } + + protected void SetChannelBinding(ChannelBinding channelBinding) + { + Fx.Assert(channelBindingToken == null, "ChannelBinding token can only be set once."); + channelBindingToken = channelBinding; + } + + protected void SetMessageSource(IMessageSource messageSource) + { + this.messageSource = new SynchronizedMessageSource(messageSource); + } + + protected async Task CloseOutputSessionAsync(CancellationToken token) + { + ThrowIfNotOpened(); + ThrowIfFaulted(); + + try + { + await sendLock.WaitAsync(token); + } + catch(OperationCanceledException) + { + // TODO: Fix the timeout value reported + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new TimeoutException( + SR.Format(SR.CloseTimedOut, TimeSpan.Zero), + TimeoutHelper.CreateEnterTimedOutException(TimeSpan.Zero))); + } + + try + { + // check again in case the previous send faulted while we were waiting for the lock + ThrowIfFaulted(); + + // we're synchronized by sendLock here + if (isOutputSessionClosed) + { + return; + } + + isOutputSessionClosed = true; + bool shouldFault = true; + try + { + await CloseOutputSessionCoreAsync(token); + OnOutputSessionClosed(token); + shouldFault = false; + } + finally + { + if (shouldFault) + { + Fault(); + } + } + } + finally + { + sendLock.Release(); + } + + } + + protected abstract Task CloseOutputSessionCoreAsync(CancellationToken token); + + // used to return cached connection to the pool/reader pool + protected abstract void ReturnConnectionIfNecessary(bool abort, CancellationToken token); + + protected override void OnAbort() + { + ReturnConnectionIfNecessary(true, CancellationToken.None); + } + + protected override void OnFaulted() + { + base.OnFaulted(); + ReturnConnectionIfNecessary(true, CancellationToken.None); + } + + protected override async Task OnCloseAsync(CancellationToken token) + { + await CloseOutputSessionAsync(token); + + // close input session if necessary + if (!isInputSessionClosed) + { + await EnsureInputClosedAsync(token); + OnInputSessionClosed(); + } + + await CompleteCloseAsync(token); + } + + protected override void OnClosed() + { + base.OnClosed(); + + // clean up the CBT after transitioning to the closed state + ChannelBindingUtility.Dispose(ref channelBindingToken); + } + + protected virtual void OnReceiveMessage(Message message) + { + if (message == null) + { + OnInputSessionClosed(); + } + else + { + PrepareMessage(message); + } + } + + protected void ApplyChannelBinding(Message message) + { + ChannelBindingUtility.TryAddToMessage(channelBindingToken, message, false); + } + + protected virtual void PrepareMessage(Message message) + { + message.Properties.Via = localVia; + + ApplyChannelBinding(message); + } + + protected abstract Task StartWritingBufferedMessageAsync(Message message, ArraySegment messageData, bool allowOutputBatching, CancellationToken token); + + protected abstract Task CloseOutputAsync(CancellationToken token); + + protected virtual void FinishWritingMessage() + { + } + + protected abstract ArraySegment EncodeMessage(Message message); + + protected abstract void OnSendCore(Message message, TimeSpan timeout); + + protected abstract Task StartWritingStreamedMessageAsync(Message message, CancellationToken token); + + protected override async Task OnSendAsync(Message message, CancellationToken token) + { + ThrowIfDisposedOrNotOpen(); + + try + { + await sendLock.WaitAsync(token); + } + catch (OperationCanceledException) + { + // TODO: Fix the timeout value reported + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new TimeoutException( + SR.Format(SR.SendToViaTimedOut, TimeSpan.Zero), + TimeoutHelper.CreateEnterTimedOutException(TimeSpan.Zero))); + } + + byte[] buffer = null; + + try + { + // check again in case the previous send faulted while we were waiting for the lock + ThrowIfDisposedOrNotOpen(); + ThrowIfOutputSessionClosed(); + + bool success = false; + try + { + ApplyChannelBinding(message); + + if (IsStreamedOutput) + { + await StartWritingStreamedMessageAsync(message, token); + } + else + { + bool allowOutputBatching; + ArraySegment messageData; + allowOutputBatching = message.Properties.AllowOutputBatching; + messageData = EncodeMessage(message); + + buffer = messageData.Array; + await StartWritingBufferedMessageAsync( + message, + messageData, + allowOutputBatching, + token); + } + + success = true; + } + finally + { + if (!success) + { + Fault(); + } + } + } + finally + { + sendLock.Release(); + } + if (buffer != null) + { + bufferManager.ReturnBuffer(buffer); + } + } + + // cleanup after the framing handshake has completed + protected abstract Task CompleteCloseAsync(CancellationToken token); + + // must be called under sendLock + void ThrowIfOutputSessionClosed() + { + if (isOutputSessionClosed) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SendCannotBeCalledAfterCloseOutputSession))); + } + } + + async Task EnsureInputClosedAsync(CancellationToken token) + { + Message message = await MessageSource.ReceiveAsync(token); + if (message != null) + { + using (message) + { + ProtocolException error = ProtocolExceptionHelper.ReceiveShutdownReturnedNonNull(message); + throw TraceUtility.ThrowHelperError(error, message); + } + } + } + + void OnInputSessionClosed() + { + lock (ThisLock) + { + if (isInputSessionClosed) + { + return; + } + + isInputSessionClosed = true; + } + } + + void OnOutputSessionClosed(CancellationToken token) + { + bool releaseConnection = false; + lock (ThisLock) + { + if (isInputSessionClosed) + { + // we're all done, release the connection + releaseConnection = true; + } + } + + if (releaseConnection) + { + ReturnConnectionIfNecessary(false, token); + } + } + + internal class ConnectionDuplexSession : IDuplexSession + { + static UriGenerator uriGenerator; + TransportDuplexSessionChannel channel; + string id; + + public ConnectionDuplexSession(TransportDuplexSessionChannel channel) + : base() + { + this.channel = channel; + } + + public string Id + { + get + { + if (id == null) + { + lock (channel) + { + if (id == null) + { + id = UriGenerator.Next(); + } + } + } + + return id; + } + } + + public TransportDuplexSessionChannel Channel + { + get { return channel; } + } + + static UriGenerator UriGenerator + { + get + { + if (uriGenerator == null) + { + uriGenerator = new UriGenerator(); + } + + return uriGenerator; + } + } + + public Task CloseOutputSessionAsync() + { + var timeoutHelper = new TimeoutHelper(channel.DefaultCloseTimeout); + return CloseOutputSessionAsync(timeoutHelper.GetCancellationToken()); + } + + public Task CloseOutputSessionAsync(CancellationToken token) + { + return channel.CloseOutputSessionAsync(token); + } + } + } +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Channels/TransportManager.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/TransportManager.cs new file mode 100644 index 000000000..efae1b47e --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/TransportManager.cs @@ -0,0 +1,256 @@ +using CoreWCF.Runtime; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace CoreWCF.Channels +{ + abstract class TransportManager + { + int openCount; + AsyncLock thisLock = new AsyncLock(); + + internal abstract string Scheme { get; } + + internal AsyncLock ThisLock + { + get { return thisLock; } + } + + internal Task CloseAsync(TransportChannelListener channelListener, CancellationToken token) + { + return CleanupAsync(channelListener, false, token); + } + + Task CleanupAsync(TransportChannelListener channelListener, bool aborting, CancellationToken token) + { + Unregister(channelListener); + lock (ThisLock) + { + if (openCount <= 0) + { + throw Fx.AssertAndThrow("Invalid Open/Close state machine."); + } + + openCount--; + + if (openCount == 0) + { + // Wrap the final close here with transfers. + if (aborting) + { + OnAbort(); + return Task.CompletedTask; + } + else + { + return OnCloseAsync(token); + } + } + } + + return Task.CompletedTask; + } + + internal static void EnsureRegistered(UriPrefixTable addressTable, + TChannelListener channelListener, HostNameComparisonMode registeredComparisonMode) + where TChannelListener : TransportChannelListener + { + TChannelListener existingFactory; + if (!addressTable.TryLookupUri(channelListener.Uri, registeredComparisonMode, out existingFactory) || + (existingFactory != channelListener)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format( + SR.ListenerFactoryNotRegistered, channelListener.Uri))); + } + } + + // Must be called under lock(ThisLock). + protected void Fault(UriPrefixTable addressTable, Exception exception) + where TChannelListener : ChannelListenerBase + { + foreach (KeyValuePair pair in addressTable.GetAll()) + { + TChannelListener listener = pair.Value; + listener.Fault(exception); + listener.Abort(); + } + } + + internal abstract Task OnCloseAsync(CancellationToken token); + // OpenAsync doesn't take a CancellationToken so OnOpenAsync doesn't either + internal abstract Task OnOpenAsync(); + internal virtual void OnAbort() { } + + internal async Task OpenAsync(TransportChannelListener channelListener) + { + Register(channelListener); + try + { + using (await ThisLock.TakeLockAsync()) + { + if (openCount == 0) + { + await OnOpenAsync(); + } + + openCount++; + } + } + catch + { + Unregister(channelListener); + throw; + } + } + + internal void Abort(TransportChannelListener channelListener) + { + CleanupAsync(channelListener, true, CancellationToken.None); + } + + internal abstract void Register(TransportChannelListener channelListener); + + // should only call this under ThisLock (unless accessing purely for inspection) + protected void ThrowIfOpen() + { + if (openCount > 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new InvalidOperationException(SR.TransportManagerOpen)); + } + } + + internal abstract void Unregister(TransportChannelListener channelListener); + } + + + delegate IList SelectTransportManagersCallback(); + class TransportManagerContainer + { + IList transportManagers; + TransportChannelListener listener; + bool closed; + AsyncLock tableLock; + + public TransportManagerContainer(TransportChannelListener listener) + { + this.listener = listener; + tableLock = listener.TransportManagerTable.AsyncLock; + transportManagers = new List(); + } + + TransportManagerContainer(TransportManagerContainer source) + { + listener = source.listener; + tableLock = source.tableLock; + transportManagers = new List(); + for (int i = 0; i < source.transportManagers.Count; i++) + { + transportManagers.Add(source.transportManagers[i]); + } + } + + // copy contents into a new container (used for listener/channel lifetime decoupling) + public static TransportManagerContainer TransferTransportManagers(TransportManagerContainer source) + { + TransportManagerContainer result = null; + + using (source.tableLock.TakeLock()) + { + if (source.transportManagers.Count > 0) + { + result = new TransportManagerContainer(source); + source.transportManagers.Clear(); + } + } + + return result; + } + + public void Abort() + { + CloseAsync(true, CancellationToken.None).GetAwaiter().GetResult(); + } + + public async Task OpenAsync(SelectTransportManagersCallback selectTransportManagerCallback) + { + using (await tableLock.TakeLockAsync()) + { + if (closed) // if we've been aborted then don't get transport managers + { + return; + } + + IList foundTransportManagers = selectTransportManagerCallback(); + if (foundTransportManagers == null) // nothing to do + { + return; + } + + for (int i = 0; i < foundTransportManagers.Count; i++) + { + TransportManager transportManager = foundTransportManagers[i]; + await transportManager.OpenAsync(listener); + transportManagers.Add(transportManager); + } + } + } + + public Task CloseAsync(CancellationToken token) + { + return CloseAsync(false, token); + } + + public async Task CloseAsync(bool aborting, CancellationToken token) + { + if (closed) + { + return; + } + + IList transportManagersCopy; + using (await tableLock.TakeLockAsync()) + { + if (closed) + { + return; + } + + closed = true; + + transportManagersCopy = new List(transportManagers); + transportManagers.Clear(); + + TimeoutException timeoutException = null; + foreach (TransportManager transportManager in transportManagersCopy) + { + try + { + if (!aborting && timeoutException == null) + { + await transportManager.CloseAsync(listener, token); + } + else + { + transportManager.Abort(listener); + } + } + catch (TimeoutException ex) + { + timeoutException = ex; + transportManager.Abort(listener); + } + } + + if (timeoutException != null) + { + // TODO: Find a way to propagate the timeout value + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new TimeoutException(SR.Format(SR.TimeoutOnClose, TimeSpan.Zero), timeoutException)); + } + } + } + } +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Channels/TransportOutputChannel.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/TransportOutputChannel.cs new file mode 100644 index 000000000..a19fa1cd8 --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/TransportOutputChannel.cs @@ -0,0 +1,163 @@ +using System; +using System.Collections.Generic; +using CoreWCF; +using System.Diagnostics; +using System.Xml; +using CoreWCF.Runtime.Diagnostics; +using CoreWCF.Runtime; + +namespace CoreWCF.Channels +{ + abstract class TransportOutputChannel : OutputChannel + { + bool anyHeadersToAdd; + bool manualAddressing; + MessageVersion messageVersion; + EndpointAddress to; + Uri via; + ToHeader toHeader; + + protected TransportOutputChannel(ChannelManagerBase channelManager, EndpointAddress to, Uri via, bool manualAddressing, MessageVersion messageVersion) + : base(channelManager) + { + this.manualAddressing = manualAddressing; + this.messageVersion = messageVersion; + this.to = to; + this.via = via; + + if (!manualAddressing && to != null) + { + Uri toUri; + if (to.IsAnonymous) + { + toUri = this.messageVersion.Addressing.AnonymousUri(); + } + else if (to.IsNone) + { + toUri = this.messageVersion.Addressing.NoneUri(); + } + else + { + toUri = to.Uri; + } + + if (toUri != null) + { + XmlDictionaryString dictionaryTo = new ToDictionary(toUri.AbsoluteUri).To; + toHeader = ToHeader.Create(toUri, dictionaryTo, messageVersion.Addressing); + } + + anyHeadersToAdd = to.Headers.Count > 0; + } + } + + protected bool ManualAddressing + { + get + { + return manualAddressing; + } + } + + public MessageVersion MessageVersion + { + get + { + return messageVersion; + } + } + + public override EndpointAddress RemoteAddress + { + get + { + return to; + } + } + + public override Uri Via + { + get + { + return via; + } + } + + protected override void AddHeadersTo(Message message) + { + base.AddHeadersTo(message); + + if (toHeader != null) + { + // TODO: Removed performance enhancement to avoid exposing another internal method. + // Evaluate whether we should do something to bring this back. My thoughts are we + // remove the SetToHeader method as we should be using the same mechanism as third + // parties transports have to use. + message.Headers.To = toHeader.To; + // we don't use to.ApplyTo(message) since it's faster to cache and + // use the actual header then to call message.Headers.To = Uri... + //message.Headers.SetToHeader(toHeader); + if (anyHeadersToAdd) + { + to.Headers.AddHeadersTo(message); + } + } + } + + class ToDictionary : IXmlDictionary + { + XmlDictionaryString to; + + public ToDictionary(string to) + { + this.to = new XmlDictionaryString(this, to, 0); + } + + public XmlDictionaryString To + { + get + { + return to; + } + } + + public bool TryLookup(string value, out XmlDictionaryString result) + { + if (value == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value"); + if (value == to.Value) + { + result = to; + return true; + } + result = null; + return false; + } + + public bool TryLookup(int key, out XmlDictionaryString result) + { + if (key == 0) + { + result = to; + return true; + } + result = null; + return false; + } + + public bool TryLookup(XmlDictionaryString value, out XmlDictionaryString result) + { + if (value == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value"); + if (value == to) + { + result = to; + return true; + } + result = null; + return false; + } + } + } + +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Channels/TransportReplyChannelAcceptor.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/TransportReplyChannelAcceptor.cs new file mode 100644 index 000000000..decbf4781 --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/TransportReplyChannelAcceptor.cs @@ -0,0 +1,107 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace CoreWCF.Channels +{ + class TransportReplyChannelAcceptor : ReplyChannelAcceptor + { + TransportManagerContainer transportManagerContainer; + TransportChannelListener listener; + + public TransportReplyChannelAcceptor(TransportChannelListener listener) + : base(listener) + { + this.listener = listener; + } + + protected override ReplyChannel OnCreateChannel() + { + return new TransportReplyChannel(ChannelManager, null); + } + + protected override void OnOpening() + { + base.OnOpening(); + transportManagerContainer = listener.GetTransportManagers(); + listener = null; + } + + protected override void OnAbort() + { + base.OnAbort(); + if (transportManagerContainer != null && !TransferTransportManagers()) + { + transportManagerContainer.Abort(); + } + } + + protected override async Task OnCloseAsync(CancellationToken token) + { + await base.OnCloseAsync(token); + if (transportManagerContainer != null && !TransferTransportManagers()) + { + await transportManagerContainer.CloseAsync(token); + } + } + + // used to decouple our channel and listener lifetimes + bool TransferTransportManagers() + { + TransportReplyChannel singletonChannel = (TransportReplyChannel)base.GetCurrentChannel(); + if (singletonChannel == null) + { + return false; + } + else + { + return singletonChannel.TransferTransportManagers(transportManagerContainer); + } + } + + // tracks TransportManager so that the channel can outlive the Listener + protected class TransportReplyChannel : ReplyChannel + { + TransportManagerContainer transportManagerContainer; + + public TransportReplyChannel(ChannelManagerBase channelManager, EndpointAddress localAddress) + : base(channelManager, localAddress) + { + } + + public bool TransferTransportManagers(TransportManagerContainer transportManagerContainer) + { + lock (ThisLock) + { + if (State != CommunicationState.Opened) + { + return false; + } + + this.transportManagerContainer = transportManagerContainer; + return true; + } + } + + protected override void OnAbort() + { + if (transportManagerContainer != null) + { + transportManagerContainer.Abort(); + } + base.OnAbort(); + } + + protected override async Task OnCloseAsync(CancellationToken token) + { + if (transportManagerContainer != null) + { + await transportManagerContainer.CloseAsync(token); + } + await base.OnCloseAsync(token); + } + } + } +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Channels/TransportSecurityHelpers.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/TransportSecurityHelpers.cs new file mode 100644 index 000000000..2842375c8 --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/TransportSecurityHelpers.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoreWCF.Channels +{ + internal static class TransportSecurityHelpers + { + public static Uri GetListenUri(Uri baseAddress, string relativeAddress) + { + Uri fullUri = baseAddress; + + // Ensure that baseAddress Path does end with a slash if we have a relative address + if (!String.IsNullOrEmpty(relativeAddress)) + { + if (!baseAddress.AbsolutePath.EndsWith("/", StringComparison.Ordinal)) + { + UriBuilder uriBuilder = new UriBuilder(baseAddress); + TcpChannelListener.FixIpv6Hostname(uriBuilder, baseAddress); + uriBuilder.Path = uriBuilder.Path + "/"; + baseAddress = uriBuilder.Uri; + } + + fullUri = new Uri(baseAddress, relativeAddress); + } + + return fullUri; + } + } +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Channels/UriGenerator.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/UriGenerator.cs new file mode 100644 index 000000000..6b7ebf289 --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/UriGenerator.cs @@ -0,0 +1,39 @@ +using System; +using System.Threading; +using System.Globalization; + +namespace CoreWCF.Channels +{ + internal class UriGenerator + { + long id; + string prefix; + + public UriGenerator() + : this("uuid") + { + } + + public UriGenerator(string scheme) + : this(scheme, ";") + { + } + + public UriGenerator(string scheme, string delimiter) + { + if (scheme == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("scheme")); + + if (scheme.Length == 0) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.UriGeneratorSchemeMustNotBeEmpty, "scheme")); + + prefix = string.Concat(scheme, ":", Guid.NewGuid().ToString(), delimiter, "id="); + } + + public string Next() + { + long nextId = Interlocked.Increment(ref id); + return prefix + nextId.ToString(CultureInfo.InvariantCulture); + } + } +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Channels/UriHelper.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/UriHelper.cs new file mode 100644 index 000000000..f106abd8a --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Channels/UriHelper.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoreWCF.Channels +{ + internal static class UriHelper + { + internal static void ValidateBaseAddress(Uri uri, string argumentName) + { + if (uri == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(argumentName); + } + + if (!uri.IsAbsoluteUri) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(argumentName, SR.BaseAddressMustBeAbsolute); + } + + if (!string.IsNullOrEmpty(uri.UserInfo)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(argumentName, SR.BaseAddressCannotHaveUserInfo); + } + + if (!string.IsNullOrEmpty(uri.Query)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(argumentName, SR.BaseAddressCannotHaveQuery); + } + + if (!string.IsNullOrEmpty(uri.Fragment)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(argumentName, SR.BaseAddressCannotHaveFragment); + } + } + } +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Configuration/FramingConnectionHandshakeBuilder.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Configuration/FramingConnectionHandshakeBuilder.cs new file mode 100644 index 000000000..bb3ff6cbf --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Configuration/FramingConnectionHandshakeBuilder.cs @@ -0,0 +1,77 @@ +using Microsoft.AspNetCore.Connections; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CoreWCF.Configuration +{ + public class FramingConnectionHandshakeBuilder : IFramingConnectionHandshakeBuilder + { + private readonly IList> _components = new List>(); + private FramingConnectionHandshakeBuilder connectionHandshakeBuilder; + + public FramingConnectionHandshakeBuilder(IServiceProvider serviceProvider) + { + Properties = new Dictionary(StringComparer.Ordinal); + HandshakeServices = serviceProvider; + } + + public FramingConnectionHandshakeBuilder(FramingConnectionHandshakeBuilder connectionHandshakeBuilder) + { + Properties = new Dictionary(connectionHandshakeBuilder.Properties, StringComparer.Ordinal); + } + + public IServiceProvider HandshakeServices + { + get + { + return GetProperty("handshake.Services"); + } + set + { + SetProperty("handshake.Services", value); + } + } + + public IDictionary Properties { get; } + + private T GetProperty(string key) + { + object value; + return Properties.TryGetValue(key, out value) ? (T)value : default(T); + } + + private void SetProperty(string key, T value) + { + Properties[key] = value; + } + + public IFramingConnectionHandshakeBuilder Use(Func middleware) + { + _components.Add(middleware); + return this; + } + + public IFramingConnectionHandshakeBuilder New() + { + return new FramingConnectionHandshakeBuilder(this); + } + + public HandshakeDelegate Build() + { + HandshakeDelegate app = context => + { + return Task.CompletedTask; + }; + + foreach (var component in _components.Reverse()) + { + app = component(app); + } + + return app; + } + } +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Configuration/IFramingConnectionHandshakeBuilder.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Configuration/IFramingConnectionHandshakeBuilder.cs new file mode 100644 index 000000000..2c0df4013 --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Configuration/IFramingConnectionHandshakeBuilder.cs @@ -0,0 +1,47 @@ +using Microsoft.AspNetCore.Connections; +using CoreWCF.Channels.Framing; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; + +namespace CoreWCF.Configuration +{ + public delegate Task HandshakeDelegate(FramingConnection connection); + + /// + /// Defines a class that provides the mechanisms to configure a connection handshake pipeline. + /// + public interface IFramingConnectionHandshakeBuilder + { + /// + /// Gets or sets the that provides access to the application's service container. + /// + IServiceProvider HandshakeServices { get; set; } + + /// + /// Gets a key/value collection that can be used to share data between middleware. + /// + IDictionary Properties { get; } + + /// + /// Adds a middleware delegate to the connection handshake pipeline. + /// + /// The middleware delegate. + /// The . + IFramingConnectionHandshakeBuilder Use(Func middleware); + + /// + /// Creates a new that shares the of this + /// . + /// + /// The new . + IFramingConnectionHandshakeBuilder New(); + + /// + /// Builds the delegate used by this application to process ServiceModel Framed requests. + /// + /// The request handling delegate. + HandshakeDelegate Build(); + } +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Configuration/MapExtensions.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Configuration/MapExtensions.cs new file mode 100644 index 000000000..a1c7a8188 --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Configuration/MapExtensions.cs @@ -0,0 +1,52 @@ +using Microsoft.AspNetCore.Connections; +using CoreWCF.Channels.Framing; +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoreWCF.Configuration +{ + /// + /// Extension methods for the . + /// + public static class MapExtensions + { + /// + /// Branches the handshake pipeline based on the result of a predicate. + /// If the predicate returns true the branch is executed. + /// + /// The instance. + /// The request path to match. + /// The branch to take for positive path matches. + /// The instance. + public static IFramingConnectionHandshakeBuilder Map(this IFramingConnectionHandshakeBuilder handshakeBuilder, Func predicate, Action configuration) + { + if (handshakeBuilder == null) + { + throw new ArgumentNullException(nameof(handshakeBuilder)); + } + + if (configuration == null) + { + throw new ArgumentNullException(nameof(configuration)); + } + + if (predicate == null) + { + throw new ArgumentNullException(nameof(predicate)); + } + + // create branch + var branchBuilder = handshakeBuilder.New(); + configuration(branchBuilder); + var branch = branchBuilder.Build(); + + var options = new MapOptions + { + Branch = branch, + Predicate = predicate, + }; + return handshakeBuilder.Use(next => new MapMiddleware(next, options).Invoke); + } + } +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Configuration/MapMiddleware.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Configuration/MapMiddleware.cs new file mode 100644 index 000000000..35539fc5b --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Configuration/MapMiddleware.cs @@ -0,0 +1,51 @@ +using Microsoft.AspNetCore.Connections; +using CoreWCF.Channels.Framing; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; + +namespace CoreWCF.Configuration +{ + /// + /// Represents a middleware that maps a request path to a sub-request pipeline. + /// + public class MapMiddleware + { + private readonly HandshakeDelegate _next; + private readonly MapOptions _options; + + /// + /// Creates a new instance of . + /// + /// The delegate representing the next middleware in the request pipeline. + /// The middleware options. + public MapMiddleware(HandshakeDelegate next, MapOptions options) + { + _next = next ?? throw new ArgumentNullException(nameof(next)); + _options = options ?? throw new ArgumentNullException(nameof(options)); + } + + /// + /// Executes the middleware. + /// + /// The for the current request. + /// A task that represents the execution of this middleware. + public async Task Invoke(FramingConnection connection) + { + if (connection == null) + { + throw new ArgumentNullException(nameof(connection)); + } + + if (_options.Predicate(connection)) + { + await _options.Branch(connection); + } + else + { + await _next(connection); + } + } + } +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Configuration/MapOptions.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Configuration/MapOptions.cs new file mode 100644 index 000000000..ec30ccec6 --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Configuration/MapOptions.cs @@ -0,0 +1,24 @@ +using Microsoft.AspNetCore.Connections; +using CoreWCF.Channels.Framing; +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoreWCF.Configuration +{ + /// + /// Options for the . + /// + public class MapOptions + { + /// + /// The path to match. + /// + public Func Predicate { get; set; } + + /// + /// The branch taken for a positive match. + /// + public HandshakeDelegate Branch { get; set; } + } +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Configuration/ServiceModelWebHostBuilderExtensions.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Configuration/ServiceModelWebHostBuilderExtensions.cs new file mode 100644 index 000000000..d20eb979a --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Configuration/ServiceModelWebHostBuilderExtensions.cs @@ -0,0 +1,75 @@ +using Microsoft.AspNetCore.Connections; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Server.Kestrel.Core; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Options; +using CoreWCF.Channels.Framing; +using System; +using System.Collections.Generic; +using System.Net; + +namespace CoreWCF.Configuration +{ + public static class ServiceModelWebHostBuilderExtensions + { + public static IWebHostBuilder UseNetTcp(this IWebHostBuilder webHostBuilder) + { + return webHostBuilder.UseNetTcp(808); + } + + public static IWebHostBuilder UseNetTcp(this IWebHostBuilder webHostBuilder, int port) + { + // Using default port + webHostBuilder.ConfigureServices(services => + { + services.AddSingleton(NetMessageFramingConnectionHandler.BuildAddressTable); + services.AddNetTcpServices(new IPEndPoint(IPAddress.Any, port)); + services.AddTransient(); + }); + + return webHostBuilder; + } + + private static IServiceCollection AddNetTcpServices(this IServiceCollection services, IPEndPoint endPoint) + { + services.TryAddEnumerable(ServiceDescriptor.Singleton, NetTcpFramingOptionsSetup>()); + + services.Configure(o => + { + o.EndPoints.Add(endPoint); + }); + + return services; + } + } + + // Exposes options for how to bind. This could include port sharing in the future + internal class NetTcpFramingOptions + { + public List EndPoints { get; } = new List(); + } + + internal class NetTcpFramingOptionsSetup : IConfigureOptions + { + private readonly NetTcpFramingOptions _options; + + public NetTcpFramingOptionsSetup(IOptions options) + { + _options = options.Value; + } + + public void Configure(KestrelServerOptions options) + { + IServiceBuilder serviceBuilder = options.ApplicationServices.GetRequiredService(); + foreach (var endpoint in _options.EndPoints) + { + serviceBuilder.BaseAddresses.Add(new Uri($"net.tcp://localhost:{endpoint.Port}/")); + options.Listen(endpoint, builder => + { + builder.UseConnectionHandler(); + }); + } + } + } +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Configuration/UseMiddlewareFramingConnectionHandshakeExtensions.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Configuration/UseMiddlewareFramingConnectionHandshakeExtensions.cs new file mode 100644 index 000000000..2670e2658 --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Configuration/UseMiddlewareFramingConnectionHandshakeExtensions.cs @@ -0,0 +1,184 @@ +using Microsoft.AspNetCore.Connections; +using Microsoft.Extensions.DependencyInjection; +using CoreWCF.Channels.Framing; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace CoreWCF.Configuration +{ + /// + /// Extension methods for adding typed middleware to a . + /// + public static class UseMiddlewareFramingConnectionHandshakeExtensions + { + internal const string OnConnectedAsyncMethodName = "OnConnectedAsync"; + + private static readonly MethodInfo GetServiceInfo = typeof(UseMiddlewareFramingConnectionHandshakeExtensions).GetMethod(nameof(GetService), BindingFlags.NonPublic | BindingFlags.Static); + + /// + /// Adds a middleware type to the connection handshake pipeline. + /// + /// The middleware type. + /// The instance. + /// The arguments to pass to the middleware type instance's constructor. + /// The instance. + public static IFramingConnectionHandshakeBuilder UseMiddleware(this IFramingConnectionHandshakeBuilder app, params object[] args) + { + return app.UseMiddleware(typeof(TMiddleware), args); + } + + /// + /// Adds a middleware type to the connection handshake pipeline. + /// + /// The instance. + /// The middleware type. + /// The arguments to pass to the middleware type instance's constructor. + /// The instance. + public static IFramingConnectionHandshakeBuilder UseMiddleware(this IFramingConnectionHandshakeBuilder app, Type middleware, params object[] args) + { + var handshakeServices = app.HandshakeServices; + return app.Use(next => + { + var methods = middleware.GetMethods(BindingFlags.Instance | BindingFlags.Public); + var invokeMethods = methods.Where(m => + string.Equals(m.Name, OnConnectedAsyncMethodName, StringComparison.Ordinal) + ).ToArray(); + + if (invokeMethods.Length > 1) + { + // TODO: String resources + throw new InvalidOperationException($"Resources.FormatException_UseMiddleMutlipleInvokes({OnConnectedAsyncMethodName}"); + } + + if (invokeMethods.Length == 0) + { + // TODO: String resources + throw new InvalidOperationException($"Resources.FormatException_UseMiddlewareNoInvokeMethod({OnConnectedAsyncMethodName}, {middleware}"); + } + + var methodInfo = invokeMethods[0]; + if (!typeof(Task).IsAssignableFrom(methodInfo.ReturnType)) + { + // TODO: String resources + throw new InvalidOperationException($"Resources.FormatException_UseMiddlewareNonTaskReturnType({OnConnectedAsyncMethodName}, {nameof(Task)}"); + } + + var parameters = methodInfo.GetParameters(); + if (parameters.Length == 0 || parameters[0].ParameterType != typeof(FramingConnection)) + { + // TODO: String resources + throw new InvalidOperationException($"Resources.FormatException_UseMiddlewareNoParameters({OnConnectedAsyncMethodName}, {nameof(FramingConnection)}"); + } + + var ctorArgs = new object[args.Length + 1]; + ctorArgs[0] = next; + Array.Copy(args, 0, ctorArgs, 1, args.Length); + var instance = ActivatorUtilities.CreateInstance(app.HandshakeServices, middleware, ctorArgs); + if (parameters.Length == 1) + { + return (HandshakeDelegate)methodInfo.CreateDelegate(typeof(HandshakeDelegate), instance); + } + + var factory = Compile(methodInfo, parameters); + + return context => + { + var serviceProvider = handshakeServices; + if (serviceProvider == null) + { + // TODO: String resources + throw new InvalidOperationException($"Resources.FormatException_UseMiddlewareIServiceProviderNotAvailable({nameof(IServiceProvider)}"); + } + + return factory(instance, context, serviceProvider); + }; + }); + } + + private static Func Compile(MethodInfo methodInfo, ParameterInfo[] parameters) + { + // If we call something like + // + // public class Middleware + // { + // public Task Invoke(ConnectionContext context, ILoggerFactory loggerFactory) + // { + // + // } + // } + // + + // We'll end up with something like this: + // Generic version: + // + // Task Invoke(Middleware instance, ConnectionContext httpContext, IServiceProvider provider) + // { + // return instance.Invoke(httpContext, (ILoggerFactory)UseMiddlewareConnectionHandshakeExtensions.GetService(provider, typeof(ILoggerFactory)); + // } + + // Non generic version: + // + // Task Invoke(object instance, ConnectionContext httpContext, IServiceProvider provider) + // { + // return ((Middleware)instance).Invoke(httpContext, (ILoggerFactory)UseMiddlewareConnectionHandshakeExtensions.GetService(provider, typeof(ILoggerFactory)); + // } + + var middleware = typeof(T); + + var connectionContextArg = Expression.Parameter(typeof(FramingConnection), "connectionContext"); + var providerArg = Expression.Parameter(typeof(IServiceProvider), "serviceProvider"); + var instanceArg = Expression.Parameter(middleware, "middleware"); + + var methodArguments = new Expression[parameters.Length]; + methodArguments[0] = connectionContextArg; + for (int i = 1; i < parameters.Length; i++) + { + var parameterType = parameters[i].ParameterType; + if (parameterType.IsByRef) + { + // TODO: String resources + throw new NotSupportedException($"Resources.FormatException_InvokeDoesNotSupportRefOrOutParams({OnConnectedAsyncMethodName})"); + } + + var parameterTypeExpression = new Expression[] + { + providerArg, + Expression.Constant(parameterType, typeof(Type)), + Expression.Constant(methodInfo.DeclaringType, typeof(Type)) + }; + + var getServiceCall = Expression.Call(GetServiceInfo, parameterTypeExpression); + methodArguments[i] = Expression.Convert(getServiceCall, parameterType); + } + + Expression middlewareInstanceArg = instanceArg; + if (methodInfo.DeclaringType != typeof(T)) + { + middlewareInstanceArg = Expression.Convert(middlewareInstanceArg, methodInfo.DeclaringType); + } + + var body = Expression.Call(middlewareInstanceArg, methodInfo, methodArguments); + + var lambda = Expression.Lambda>(body, instanceArg, connectionContextArg, providerArg); + + return lambda.Compile(); + } + + private static object GetService(IServiceProvider sp, Type type, Type middleware) + { + var service = sp.GetService(type); + if (service == null) + { + // TODO: String resources + throw new InvalidOperationException($"Resources.FormatException_InvokeMiddlewareNoService({type}, {middleware})"); + } + + return service; + } + } +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/DiagnosticUtility.cs b/src/CoreWCF.NetTcp/src/CoreWCF/DiagnosticUtility.cs new file mode 100644 index 000000000..995a9d830 --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/DiagnosticUtility.cs @@ -0,0 +1,188 @@ +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using CoreWCF.Runtime; + +namespace CoreWCF +{ + internal class DiagnosticUtility + { + private static ExceptionUtility exceptionUtility = (ExceptionUtility)null; + private static object lockObject = new object(); + + internal static ExceptionUtility ExceptionUtility + { + get + { + return exceptionUtility ?? GetExceptionUtility(); + } + } + + private static ExceptionUtility GetExceptionUtility() + { + lock (lockObject) + { + if (exceptionUtility == null) + // TODO: Make this generic shared code used by multiple assemblies + //exceptionUtility = new ExceptionUtility("System.ServiceModel", "System.ServiceModel 4.0.0.0", (object)DiagnosticUtility.diagnosticTrace, (object)FxTrace.Exception); + exceptionUtility = new ExceptionUtility(); + } + + return exceptionUtility; + } + + internal static void TraceHandledException(Exception exception, TraceEventType traceEventType) + { + //FxTrace.Exception.TraceHandledException(exception, traceEventType); + } + + [Conditional("DEBUG")] + internal static void DebugAssert(bool condition, string message) + { + if (!condition) + { + DebugAssert(message); + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + [Conditional("DEBUG")] + internal static void DebugAssert(string message) + { + Fx.Assert(message); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + internal static Exception FailFast(string message) + { + try + { + try + { + ExceptionUtility.TraceFailFast(message); + } + finally + { + Environment.FailFast(message); + } + } + catch + { + } + Environment.FailFast(message); + return (Exception)null; + } + } + + internal class ExceptionUtility + { + internal ArgumentException ThrowHelperArgument(string message) + { + return (ArgumentException)ThrowHelperError(new ArgumentException(message)); + } + + internal ArgumentException ThrowHelperArgument(string paramName, string message) + { + return (ArgumentException)ThrowHelperError(new ArgumentException(message, paramName)); + } + + internal ArgumentNullException ThrowHelperArgumentNull(string paramName) + { + return (ArgumentNullException)ThrowHelperError(new ArgumentNullException(paramName)); + } + + internal Exception ThrowHelperFatal(string message, Exception innerException) + { + return ThrowHelperError(new FatalException(message, innerException)); + } + + internal Exception ThrowHelperError(Exception exception) + { + return ThrowHelper(exception, TraceEventType.Error); + } + + internal Exception ThrowHelperWarning(Exception exception) + { + return ThrowHelper(exception, TraceEventType.Warning); + } + + internal Exception ThrowHelper(Exception exception, TraceEventType eventType) + { + return ThrowHelper(exception, eventType, null); + } + + internal Exception ThrowHelper(Exception exception, TraceEventType eventType, TraceRecord extendedData) + { + //if ((_diagnosticTrace == null ? 0 : (_diagnosticTrace.ShouldTrace(eventType) ? 1 : 0)) != 0) + //{ + // using ( + // ExceptionUtility.useStaticActivityId + // ? Activity.CreateActivity(ExceptionUtility.activityId) + // : (Activity)null) + // _diagnosticTrace.TraceEvent(eventType, 131075, + // LegacyDiagnosticTrace.GenerateMsdnTraceCode("System.ServiceModel.Diagnostics", + // "ThrowingException"), TraceSR.Format("ThrowingException"), extendedData, exception, + // (object)null); + // IDictionary data = exception.Data; + // if (data != null && !data.IsReadOnly && !data.IsFixedSize) + // { + // object obj = + // data[(object)"System.ServiceModel.Diagnostics.ExceptionUtility.ExceptionStackAsString"]; + // string str1 = obj == null ? "" : obj as string; + // if (str1 != null) + // { + // string stackTrace = exception.StackTrace; + // if (!string.IsNullOrEmpty(stackTrace)) + // { + // string str2 = str1 + (str1.Length == 0 ? "" : Environment.NewLine) + "throw" + + // Environment.NewLine + stackTrace + Environment.NewLine + "catch" + + // Environment.NewLine; + // data[(object)"System.ServiceModel.Diagnostics.ExceptionUtility.ExceptionStackAsString"] + // = (object)str2; + // } + // } + // } + //} + //this.exceptionTrace.TraceEtwException(exception, eventType); + return exception; + } + + internal Exception ThrowHelperCallback(Exception innerException) + { + return ThrowHelperCallback(TraceSR.GenericCallbackException, innerException); + } + + internal Exception ThrowHelperCallback(string message, Exception innerException) + { + return ThrowHelperCritical(new CallbackException(message, innerException)); + } + + internal Exception ThrowHelperCritical(Exception exception) + { + return ThrowHelper(exception, TraceEventType.Critical); + } + + internal class TraceRecord + { + } + + [MethodImpl(MethodImplOptions.NoInlining)] + internal void TraceFailFast(string message) + { + //Microsoft.Runtime.Diagnostics.EventLogger logger = null; + //try + //{ + // logger = new Microsoft.Runtime.Diagnostics.EventLogger(this.eventSourceName, this.diagnosticTrace); + //} + //finally + //{ + // TraceFailFast(message, logger); + //} + } + + internal Exception ThrowHelperArgumentNull(string paramName, string message) + { + return (ArgumentNullException)ThrowHelperError(new ArgumentNullException(paramName, message)); + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Dispatcher/ErrorBehaviorHelper.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Dispatcher/ErrorBehaviorHelper.cs new file mode 100644 index 000000000..7cc677d57 --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Dispatcher/ErrorBehaviorHelper.cs @@ -0,0 +1,53 @@ +using CoreWCF.Channels; +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoreWCF.Dispatcher +{ + internal static class ErrorBehaviorHelper + { + // This ensures that people debugging first-chance Exceptions see this Exception, + // and that the Exception shows up in the trace logs. + internal static void ThrowAndCatch(Exception e, Message message) + { + try + { + if (System.Diagnostics.Debugger.IsAttached) + { + if (message == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(e); + } + else + { + throw TraceUtility.ThrowHelperError(e, message); + } + } + else + { + if (message == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(e); + } + else + { + TraceUtility.ThrowHelperError(e, message); + } + } + } + catch (Exception e2) + { + if (!object.ReferenceEquals(e, e2)) + { + throw; + } + } + } + + internal static void ThrowAndCatch(Exception e) + { + ThrowAndCatch(e, null); + } + } +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Dispatcher/ExceptionHandlerHelper.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Dispatcher/ExceptionHandlerHelper.cs new file mode 100644 index 000000000..1c9244c3a --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Dispatcher/ExceptionHandlerHelper.cs @@ -0,0 +1,46 @@ +using CoreWCF.Runtime; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; + +namespace CoreWCF.Dispatcher +{ + internal static class ExceptionHandlerHelper + { + internal static bool HandleTransportExceptionHelper(Exception exception) + { + if (exception == null) + { + throw Fx.AssertAndThrow("Null exception passed to HandleTransportExceptionHelper."); + } + + ExceptionHandler handler = ExceptionHandler.TransportExceptionHandler; + if (handler == null) + { + return false; + } + + try + { + if (!handler.HandleException(exception)) + { + return false; + } + } + catch (Exception thrownException) + { + if (Fx.IsFatal(thrownException)) + { + throw; + } + + DiagnosticUtility.TraceHandledException(thrownException, TraceEventType.Error); + return false; + } + + DiagnosticUtility.TraceHandledException(exception, TraceEventType.Error); + return true; + } + } +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/NetTcpBinding.cs b/src/CoreWCF.NetTcp/src/CoreWCF/NetTcpBinding.cs new file mode 100644 index 000000000..b8c6d9ea8 --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/NetTcpBinding.cs @@ -0,0 +1,175 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// ------------------------------------------------------------------------------ + +using System; +using System.ComponentModel; +using System.Xml; +using CoreWCF.Runtime; +using CoreWCF.Channels; + +namespace CoreWCF +{ + public class NetTcpBinding : Binding + { + // private BindingElements + TcpTransportBindingElement transport; + BinaryMessageEncodingBindingElement encoding; + NetTcpSecurity security = new NetTcpSecurity(); + + public NetTcpBinding() { Initialize(); } + public NetTcpBinding(SecurityMode securityMode) + : this() + { + security.Mode = securityMode; + } + + public TransferMode TransferMode + { + get { return transport.TransferMode; } + set { transport.TransferMode = value; } + } + + public HostNameComparisonMode HostNameComparisonMode + { + get { return transport.HostNameComparisonMode; } + set { transport.HostNameComparisonMode = value; } + } + + [DefaultValue(TransportDefaults.MaxBufferPoolSize)] + public long MaxBufferPoolSize + { + get { return transport.MaxBufferPoolSize; } + set + { + transport.MaxBufferPoolSize = value; + } + } + + public int MaxBufferSize + { + get { return transport.MaxBufferSize; } + set { transport.MaxBufferSize = value; } + } + + public int MaxConnections + { + get { return transport.MaxPendingConnections; } + set + { + transport.MaxPendingConnections = value; + } + } + + internal bool IsMaxConnectionsSet + { + get { return transport.IsMaxPendingConnectionsSet; } + } + + public int ListenBacklog + { + get { return transport.ListenBacklog; } + set { transport.ListenBacklog = value; } + } + + public long MaxReceivedMessageSize + { + get { return transport.MaxReceivedMessageSize; } + set { transport.MaxReceivedMessageSize = value; } + } + + public XmlDictionaryReaderQuotas ReaderQuotas + { + get { return encoding.ReaderQuotas; } + set + { + if (value == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value"); + value.CopyTo(encoding.ReaderQuotas); + } + } + + //TODO: Work out if we want IBindingRuntimePreferences. Probably not as we're aiming for 100% async here + //bool IBindingRuntimePreferences.ReceiveSynchronously + //{ + // get { return false; } + //} + + public override string Scheme { get { return transport.Scheme; } } + + public EnvelopeVersion EnvelopeVersion + { + get { return EnvelopeVersion.Soap12; } + } + + public NetTcpSecurity Security + { + get { return security; } + set + { + if (value == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value"); + security = value; + } + } + + void Initialize() + { + transport = new TcpTransportBindingElement(); + encoding = new BinaryMessageEncodingBindingElement(); + } + + private void CheckSettings() + { + NetTcpSecurity security = Security; + if (security == null) + { + return; + } + + SecurityMode mode = security.Mode; + if (mode == SecurityMode.None) + { + return; + } + else if (mode == SecurityMode.Message) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.Format(SR.UnsupportedSecuritySetting, "Mode", mode))); + } + + // Message.ClientCredentialType = Certificate, IssuedToken or Windows are not supported. + if (mode == SecurityMode.TransportWithMessageCredential) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.Format(SR.UnsupportedSecuritySetting, "Mode", mode))); + } + } + + public override BindingElementCollection CreateBindingElements() + { + CheckSettings(); + + // return collection of BindingElements + BindingElementCollection bindingElements = new BindingElementCollection(); + // order of BindingElements is important + // add encoding + bindingElements.Add(encoding); + // add transport security + BindingElement transportSecurity = CreateTransportSecurity(); + if (transportSecurity != null) + { + bindingElements.Add(transportSecurity); + } + // TODO: Add ExtendedProtectionPolicy + transport.ExtendedProtectionPolicy = security.Transport.ExtendedProtectionPolicy; + // add transport (tcp) + bindingElements.Add(transport); + + return bindingElements.Clone(); + } + + BindingElement CreateTransportSecurity() + { + return security.CreateTransportSecurity(); + } + } +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/NetTcpSecurity.cs b/src/CoreWCF.NetTcp/src/CoreWCF/NetTcpSecurity.cs new file mode 100644 index 000000000..035719efe --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/NetTcpSecurity.cs @@ -0,0 +1,65 @@ +using CoreWCF.Runtime; +using CoreWCF.Channels; +using System; +using System.ComponentModel; + +namespace CoreWCF +{ + public sealed partial class NetTcpSecurity + { + internal const SecurityMode DefaultMode = SecurityMode.Transport; + + SecurityMode mode; + TcpTransportSecurity transportSecurity; + + public NetTcpSecurity() + : this(DefaultMode, new TcpTransportSecurity()) + { + } + + NetTcpSecurity(SecurityMode mode, TcpTransportSecurity transportSecurity) + { + Fx.Assert(SecurityModeHelper.IsDefined(mode), string.Format("Invalid SecurityMode value: {0}.", mode.ToString())); + + this.mode = mode; + this.transportSecurity = transportSecurity == null ? new TcpTransportSecurity() : transportSecurity; + } + + [DefaultValue(DefaultMode)] + public SecurityMode Mode + { + get { return mode; } + set + { + if (!SecurityModeHelper.IsDefined(value)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value")); + } + mode = value; + } + } + + public TcpTransportSecurity Transport + { + get { return transportSecurity; } + set { transportSecurity = value; } + } + + internal BindingElement CreateTransportSecurity() + { + if (mode == SecurityMode.TransportWithMessageCredential) + { + throw new PlatformNotSupportedException("TransportWithMessageCredential"); + //return this.transportSecurity.CreateTransportProtectionOnly(); + } + else if (mode == SecurityMode.Transport) + { + return transportSecurity.CreateTransportProtectionAndAuthentication(); + } + else + { + return null; + } + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Runtime/AsyncCompletionResult.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Runtime/AsyncCompletionResult.cs new file mode 100644 index 000000000..34531aaf7 --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Runtime/AsyncCompletionResult.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoreWCF.Runtime +{ + enum AsyncCompletionResult + { + /// + /// Inidicates that the operation has been queued for completion. + /// + Queued, + + /// + /// Indicates the operation has completed. + /// + Completed, + } +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Runtime/BackoffTimeoutHelper.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Runtime/BackoffTimeoutHelper.cs new file mode 100644 index 000000000..f79c3fdcf --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Runtime/BackoffTimeoutHelper.cs @@ -0,0 +1,134 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; + +namespace CoreWCF.Runtime +{ + internal sealed class BackoffTimeoutHelper + { + readonly static int maxSkewMilliseconds = 15; + readonly static long maxDriftTicks = maxSkewMilliseconds * 2 * TimeSpan.TicksPerMillisecond; + readonly static TimeSpan defaultInitialWaitTime = TimeSpan.FromMilliseconds(1); + readonly static TimeSpan defaultMaxWaitTime = TimeSpan.FromMinutes(1); + + DateTime deadline; + TimeSpan maxWaitTime; + TimeSpan waitTime; + IOThreadTimer backoffTimer; + Action backoffCallback; + object backoffState; + Random random; + TimeSpan originalTimeout; + + internal BackoffTimeoutHelper(TimeSpan timeout) + : this(timeout, BackoffTimeoutHelper.defaultMaxWaitTime) + { + } + + internal BackoffTimeoutHelper(TimeSpan timeout, TimeSpan maxWaitTime) + : this(timeout, maxWaitTime, BackoffTimeoutHelper.defaultInitialWaitTime) + { + } + + internal BackoffTimeoutHelper(TimeSpan timeout, TimeSpan maxWaitTime, TimeSpan initialWaitTime) + { + random = new Random(GetHashCode()); + this.maxWaitTime = maxWaitTime; + originalTimeout = timeout; + Reset(timeout, initialWaitTime); + } + + public TimeSpan OriginalTimeout + { + get + { + return originalTimeout; + } + } + + void Reset(TimeSpan timeout, TimeSpan initialWaitTime) + { + if (timeout == TimeSpan.MaxValue) + { + deadline = DateTime.MaxValue; + } + else + { + deadline = DateTime.UtcNow + timeout; + } + waitTime = initialWaitTime; + } + + public bool IsExpired() + { + if (deadline == DateTime.MaxValue) + { + return false; + } + else + { + return (DateTime.UtcNow >= deadline); + } + } + + public void WaitAndBackoff(Action callback, object state) + { + if (backoffCallback != callback || backoffState != state) + { + if (backoffTimer != null) + { + backoffTimer.Cancel(); + } + backoffCallback = callback; + backoffState = state; + backoffTimer = new IOThreadTimer(callback, state, false, BackoffTimeoutHelper.maxSkewMilliseconds); + } + + TimeSpan backoffTime = WaitTimeWithDrift(); + Backoff(); + backoffTimer.Set(backoffTime); + } + + // TODO: Consider making Async + public void WaitAndBackoff() + { + Thread.Sleep(WaitTimeWithDrift()); + Backoff(); + } + + TimeSpan WaitTimeWithDrift() + { + return Ticks.ToTimeSpan(Math.Max( + Ticks.FromTimeSpan(BackoffTimeoutHelper.defaultInitialWaitTime), + Ticks.Add(Ticks.FromTimeSpan(waitTime), + (long)(uint)random.Next() % (2 * BackoffTimeoutHelper.maxDriftTicks + 1) - BackoffTimeoutHelper.maxDriftTicks))); + } + + void Backoff() + { + if (waitTime.Ticks >= (maxWaitTime.Ticks / 2)) + { + waitTime = maxWaitTime; + } + else + { + waitTime = TimeSpan.FromTicks(waitTime.Ticks * 2); + } + + if (deadline != DateTime.MaxValue) + { + TimeSpan remainingTime = deadline - DateTime.UtcNow; + if (waitTime > remainingTime) + { + waitTime = remainingTime; + if (waitTime < TimeSpan.Zero) + { + waitTime = TimeSpan.Zero; + } + } + } + } + } + +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Runtime/CallbackException.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Runtime/CallbackException.cs new file mode 100644 index 000000000..df147d6d5 --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Runtime/CallbackException.cs @@ -0,0 +1,23 @@ +using System; + +namespace CoreWCF.Runtime +{ + //[Serializable] + class CallbackException : FatalException + { + public CallbackException() + { + } + + public CallbackException(string message, Exception innerException) : base(message, innerException) + { + // This can't throw something like ArgumentException because that would be worse than + // throwing the callback exception that was requested. + Fx.Assert(innerException != null, "CallbackException requires an inner exception."); + Fx.Assert(!Fx.IsFatal(innerException), "CallbackException can't be used to wrap fatal exceptions."); + } + //protected CallbackException(SerializationInfo info, StreamingContext context) : base(info, context) + //{ + //} + } +} \ No newline at end of file diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Runtime/Diagnostics/EtwDiagnosticTrace.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Runtime/Diagnostics/EtwDiagnosticTrace.cs new file mode 100644 index 000000000..dcab9bbc3 --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Runtime/Diagnostics/EtwDiagnosticTrace.cs @@ -0,0 +1,74 @@ +using System; + +namespace CoreWCF.Runtime.Diagnostics +{ + internal sealed class EtwDiagnosticTrace + { + static readonly public Guid ImmutableDefaultEtwProviderId = new Guid("{c651f5f6-1c0d-492e-8ae1-b4efd7c9d503}"); + private static Guid s_defaultEtwProviderId = ImmutableDefaultEtwProviderId; + + static public Guid DefaultEtwProviderId + { + get + { + return s_defaultEtwProviderId; + } + set + { + s_defaultEtwProviderId = value; + } + } + + public EtwDiagnosticTrace(string traceSourceName, Guid etwProviderId) + //: base(traceSourceName) + { +// try +// { +// this.TraceSourceName = traceSourceName; +// this.EventSourceName = string.Concat(this.TraceSourceName, " ", EventSourceVersion); +// CreateTraceSource(); +// } +// catch (Exception exception) +// { +// if (Fx.IsFatal(exception)) +// { +// throw; +// } + +//#pragma warning disable 618 +// EventLogger logger = new EventLogger(this.EventSourceName, null); +// logger.LogEvent(TraceEventType.Error, TracingEventLogCategory, (uint)System.Runtime.Diagnostics.EventLogEventId.FailedToSetupTracing, false, +// exception.ToString()); +//#pragma warning restore 618 +// } + +// try +// { +// CreateEtwProvider(etwProviderId); +// } +// catch (Exception exception) +// { +// if (Fx.IsFatal(exception)) +// { +// throw; +// } + +// this.etwProvider = null; +//#pragma warning disable 618 +// EventLogger logger = new EventLogger(this.EventSourceName, null); +// logger.LogEvent(TraceEventType.Error, TracingEventLogCategory, (uint)System.Runtime.Diagnostics.EventLogEventId.FailedToSetupTracing, false, +// exception.ToString()); +//#pragma warning restore 618 + +// } + +// if (this.TracingEnabled || this.EtwTracingEnabled) +// { +//#pragma warning disable 618 +// this.AddDomainEventHandlersForCleanup(); +//#pragma warning restore 618 +// } + } + + } +} \ No newline at end of file diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Security/ProtectionLevelHelper.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Security/ProtectionLevelHelper.cs new file mode 100644 index 000000000..ede961d9a --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Security/ProtectionLevelHelper.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Net.Security; +using System.Text; + +namespace CoreWCF.Security +{ + internal static class ProtectionLevelHelper + { + internal static bool IsDefined(ProtectionLevel value) + { + return (value == ProtectionLevel.None + || value == ProtectionLevel.Sign + || value == ProtectionLevel.EncryptAndSign); + } + + internal static void Validate(ProtectionLevel value) + { + if (!IsDefined(value)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidEnumArgumentException("value", (int)value, + typeof(ProtectionLevel))); + } + } + + internal static bool IsStronger(ProtectionLevel v1, ProtectionLevel v2) + { + return ((v1 == ProtectionLevel.EncryptAndSign && v2 != ProtectionLevel.EncryptAndSign) + || (v1 == ProtectionLevel.Sign && v2 == ProtectionLevel.None)); + } + + internal static bool IsStrongerOrEqual(ProtectionLevel v1, ProtectionLevel v2) + { + return (v1 == ProtectionLevel.EncryptAndSign + || (v1 == ProtectionLevel.Sign && v2 != ProtectionLevel.EncryptAndSign)); + } + + internal static ProtectionLevel Max(ProtectionLevel v1, ProtectionLevel v2) + { + return IsStronger(v1, v2) ? v1 : v2; + } + + internal static int GetOrdinal(Nullable p) + { + if (p.HasValue) + { + switch ((ProtectionLevel)p) + { + default: + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidEnumArgumentException("p", (int)p, + typeof(ProtectionLevel))); + case ProtectionLevel.None: + return 2; + case ProtectionLevel.Sign: + return 3; + case ProtectionLevel.EncryptAndSign: + return 4; + } + } + else + return 1; + } + + } +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/Security/SslProtocolsHelper.cs b/src/CoreWCF.NetTcp/src/CoreWCF/Security/SslProtocolsHelper.cs new file mode 100644 index 000000000..5ddd1b0cc --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/Security/SslProtocolsHelper.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Security.Authentication; +using System.Text; + +namespace CoreWCF.Security +{ + static class SslProtocolsHelper + { + internal static bool IsDefined(SslProtocols value) + { + SslProtocols allValues = SslProtocols.None; + foreach (var protocol in Enum.GetValues(typeof(SslProtocols))) + { + allValues |= (SslProtocols)protocol; + } + return (value & allValues) == value; + } + + internal static void Validate(SslProtocols value) + { + if (!IsDefined(value)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidEnumArgumentException("value", (int)value, + typeof(SslProtocols))); + } + } + } +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/SecurityModeHelper.cs b/src/CoreWCF.NetTcp/src/CoreWCF/SecurityModeHelper.cs new file mode 100644 index 000000000..f3445e753 --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/SecurityModeHelper.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoreWCF +{ + internal static class SecurityModeHelper + { + internal static bool IsDefined(SecurityMode value) + { + return (value == SecurityMode.None || + value == SecurityMode.Transport || + value == SecurityMode.Message || + value == SecurityMode.TransportWithMessageCredential); + } + } +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/ServiceDefaults.cs b/src/CoreWCF.NetTcp/src/CoreWCF/ServiceDefaults.cs new file mode 100644 index 000000000..c804c8a11 --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/ServiceDefaults.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoreWCF +{ + internal static class ServiceDefaults + { + //internal static TimeSpan ServiceHostCloseTimeout { get { return TimeSpanHelper.FromSeconds(10, ServiceHostCloseTimeoutString); } } + //internal static TimeSpan ServiceHostCloseTimeout => TimeSpan.FromSeconds(10); + //internal const string ServiceHostCloseTimeoutString = "00:00:10"; + //internal static TimeSpan CloseTimeout { get { return TimeSpanHelper.FromMinutes(1, CloseTimeoutString); } } + internal static TimeSpan CloseTimeout => TimeSpan.FromMinutes(1); + internal const string CloseTimeoutString = "00:01:00"; + //internal static TimeSpan OpenTimeout { get { return TimeSpanHelper.FromMinutes(1, OpenTimeoutString); } } + internal static TimeSpan OpenTimeout => TimeSpan.FromMinutes(1); + internal const string OpenTimeoutString = "00:01:00"; + //internal static TimeSpan ReceiveTimeout { get { return TimeSpanHelper.FromMinutes(10, ReceiveTimeoutString); } } + internal static TimeSpan ReceiveTimeout => TimeSpan.FromMinutes(10); + internal const string ReceiveTimeoutString = "00:10:00"; + //internal static TimeSpan SendTimeout { get { return TimeSpanHelper.FromMinutes(1, SendTimeoutString); } } + internal static TimeSpan SendTimeout => TimeSpan.FromMinutes(1); + internal const string SendTimeoutString = "00:01:00"; + //internal static TimeSpan TransactionTimeout { get { return TimeSpanHelper.FromMinutes(1, TransactionTimeoutString); } } + //internal const string TransactionTimeoutString = "00:00:00"; + } +} diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/TcpClientCredentialType.cs b/src/CoreWCF.NetTcp/src/CoreWCF/TcpClientCredentialType.cs new file mode 100644 index 000000000..841e7d22e --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/TcpClientCredentialType.cs @@ -0,0 +1,19 @@ +namespace CoreWCF +{ + public enum TcpClientCredentialType + { + None = 0, + Windows = 1, + Certificate = 2, + } + + internal static class TcpClientCredentialTypeHelper + { + internal static bool IsDefined(TcpClientCredentialType value) + { + return (value == TcpClientCredentialType.None || + value == TcpClientCredentialType.Windows || + value == TcpClientCredentialType.Certificate); + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/TcpTransportSecurity.cs b/src/CoreWCF.NetTcp/src/CoreWCF/TcpTransportSecurity.cs new file mode 100644 index 000000000..20fddc8c3 --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/TcpTransportSecurity.cs @@ -0,0 +1,119 @@ +using CoreWCF.Channels; +using CoreWCF.Security; +using System; +using System.ComponentModel; +using System.Net.Security; +using System.Security.Authentication; +using System.Security.Authentication.ExtendedProtection; + +namespace CoreWCF +{ + public sealed partial class TcpTransportSecurity + { + internal const TcpClientCredentialType DefaultClientCredentialType = TcpClientCredentialType.Windows; + internal const ProtectionLevel DefaultProtectionLevel = ProtectionLevel.EncryptAndSign; + + private TcpClientCredentialType clientCredentialType; + private ProtectionLevel protectionLevel; + private ExtendedProtectionPolicy extendedProtectionPolicy; + SslProtocols sslProtocols; + + public TcpTransportSecurity() + { + clientCredentialType = DefaultClientCredentialType; + protectionLevel = DefaultProtectionLevel; + extendedProtectionPolicy = ChannelBindingUtility.DefaultPolicy; + sslProtocols = TransportDefaults.SslProtocols; + } + + [DefaultValue(DefaultClientCredentialType)] + public TcpClientCredentialType ClientCredentialType + { + get { return clientCredentialType; } + set + { + if (!TcpClientCredentialTypeHelper.IsDefined(value)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value")); + } + clientCredentialType = value; + } + } + + [DefaultValue(DefaultProtectionLevel)] + public ProtectionLevel ProtectionLevel + { + get { return protectionLevel; } + set + { + if (!ProtectionLevelHelper.IsDefined(value)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value")); + } + protectionLevel = value; + } + } + + public ExtendedProtectionPolicy ExtendedProtectionPolicy + { + get + { + return extendedProtectionPolicy; + } + set + { + if (value == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value"); + } + + if (value.PolicyEnforcement == PolicyEnforcement.Always && + !System.Security.Authentication.ExtendedProtection.ExtendedProtectionPolicy.OSSupportsExtendedProtection) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new PlatformNotSupportedException(SR.ExtendedProtectionNotSupported)); + } + extendedProtectionPolicy = value; + } + } + + [DefaultValue(TransportDefaults.SslProtocols)] + public SslProtocols SslProtocols + { + get { return sslProtocols; } + set + { + SslProtocolsHelper.Validate(value); + sslProtocols = value; + } + } + + SslStreamSecurityBindingElement CreateSslBindingElement(bool requireClientCertificate) + { + if (protectionLevel != ProtectionLevel.EncryptAndSign) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format( + SR.UnsupportedSslProtectionLevel, protectionLevel))); + } + + SslStreamSecurityBindingElement result = new SslStreamSecurityBindingElement(); + result.RequireClientCertificate = requireClientCertificate; + result.SslProtocols = sslProtocols; + return result; + } + + internal BindingElement CreateTransportProtectionAndAuthentication() + { + if (clientCredentialType == TcpClientCredentialType.Certificate || clientCredentialType == TcpClientCredentialType.None) + { + return CreateSslBindingElement(clientCredentialType == TcpClientCredentialType.Certificate); + } + else + { + WindowsStreamSecurityBindingElement result = new WindowsStreamSecurityBindingElement(); + result.ProtectionLevel = this.protectionLevel; + return result; + } + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.NetTcp/src/CoreWCF/TraceUtility.cs b/src/CoreWCF.NetTcp/src/CoreWCF/TraceUtility.cs new file mode 100644 index 000000000..37b92edc7 --- /dev/null +++ b/src/CoreWCF.NetTcp/src/CoreWCF/TraceUtility.cs @@ -0,0 +1,27 @@ +using CoreWCF.Channels; +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoreWCF +{ + internal static class TraceUtility + { + internal static Exception ThrowHelperError(Exception exception, Message message) + { + // If the message is closed, we won't get an activity + //Guid activityId = TraceUtility.ExtractActivityId(message); + //if (DiagnosticUtility.ShouldTraceError) + //{ + // DiagnosticUtility.DiagnosticTrace.TraceEvent(TraceEventType.Error, TraceCode.ThrowingException, GenerateMsdnTraceCode(TraceCode.ThrowingException), + // TraceSR.Format(TraceSR.ThrowingException), null, exception, activityId, null); + //} + return exception; + } + + internal static Exception ThrowHelperError(Exception exception, Guid activityId, object source) + { + return exception; + } + } +} diff --git a/src/CoreWCF.NetTcp/src/Resources/Strings.resx b/src/CoreWCF.NetTcp/src/Resources/Strings.resx new file mode 100644 index 000000000..eece0c28a --- /dev/null +++ b/src/CoreWCF.NetTcp/src/Resources/Strings.resx @@ -0,0 +1,7944 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + No IPEndpoints were found for host {0}. + + + No DNS entries exist for host {0}. + + + Attribute '{0}' is required on element '{1}'. + + + Crypto algorithm {0} not supported in this context. + + + The custom crypto algorithm '{0}' obtained using CryptoConfig is not a valid or supported hash algorithm. + + + The client credential entered was invalid. + + + Either the client credential was invalid or there was an error collecting the client credentials by the SSPI. + + + The custom crypto algorithm '{0}' obtained using CryptoConfig is not a valid or supported asymmetric signature algorithm. + + + The security token serializer must be specified on the security token provider. + + + The key length '{0}' is not a multiple of 8 for symmetric keys. + + + The channel behaviors configured for the issuer address '{0}' cannot contain a behavior of type '{1}'. + + + Operation Action={0} + + + The security token manager cannot create a token provider for requirement '{0}'. + + + The security token manager cannot create a token authenticator for requirement '{0}'. + + + The signature verification failed. Please see inner exception for fault details. + + + The security token manager cannot create a token serializer for security token version '{0}'. + + + The supporting signature is not signed with a derived key. The binding's supporting token parameter '{0}' requires key derivation. + + + The primary signature is not signed with a derived key. The binding's primary token parameter '{0}' requires key derivation. + + + The primary signature is not signed with a key derived from the encrypted key. The binding's token parameter '{0}' requires key derivation. + + + The message is not encrypted with a key derived from the encrypted key. The binding's token parameter '{0}' requires key derivation. + + + The DataProtectionSecurityStateEncoder is unable to decode the byte array. Ensure that a 'UserProfile' is loaded, if this is a 'web farm scenario' ensure all servers are running as the same user with the roaming profiles or provide a custom SecurityStateEncoder'. + + + The DataProtectionSecurityStateEncoder is unable to encode the byte array. Ensure that a 'UserProfile' is loaded, if this is a 'web farm scenario' ensure all servers are running as the same user with the roaming profiles or provide a custom SecurityStateEncoder'. + + + The message is not encrypted with a key derived from the encryption token. The binding's token parameter '{0}' requires key derivation. + + + The security token manager requires the security binding element to be specified in order to create a token authenticator for requirement '{0}'. + + + The security token manager requires the security binding element to be specified in order to create a token provider for requirement '{0}'. + + + The security session received an unexpected close response from the other party. + + + The security session received an unexpected close from the other party. + + + The service was unable to verify the cipher strengths negotiated as part of the SSL handshake. + + + SecurityVersion.WSSecurityJan2004 does not support header encryption. Header with name '{0}' and namespace '{1}' is configured for encryption. Consider using SecurityVersion.WsSecurity11 and above or use transport security to encrypt the full message. + + + The Header ('{0}', '{1}') was encrypted but not signed. All encrypted headers outside the security header should be signed. + + + Unable to obtain XmlDictionaryReaderQuotas from the Binding. If you have specified a custom EncodingBindingElement, verify that the EncodingBindingElement can handle XmlDictionaryReaderQuotas in its GetProperty<T>() method. + + + SecurityVersion.WSSecurityJan2004 does not support header decryption. Use SecurityVersion.WsSecurity11 and above or use transport security to encrypt the full message. + + + Unable to decrypt an encrypted data block. Please verify that the encryption algorithm and keys used by the sender and receiver match. + + + The authenticate method in the ServiceAuthenticationManager returned null. If you do not want to return any authorization policies in the collection then return an empty ReadOnlyCollection instead. + + + There was an error serializing the security token. Please see the inner exception for more details. + + + There was an error creating the security key identifier clause from the security token XML. Please see the inner exception for more details. + + + There was an error deserializing the security token XML. Please see the inner exception for more details. + + + The token requirement '{0}' does not specify the target address. This is required by the token manager for creating the corresponding security token provider. + + + The derived key has not been computed for the security token. + + + The binding ('{0}', '{1}') has been configured with a security algorithm suite '{2}' that is incompatible with the issued token key size '{3}' specified on the binding. + + + The IssuedToken security authentication mode requires the issued token to contain a symmetric key. + + + The binding ('{0}', '{1}') uses an Issued Token with Bearer Key Type in a invalid context. The Issued Token with a Bearer Key Type can only be used as a Signed Supporting token or a Signed Encrypted Supporting token. See the SecurityBindingElement.EndpointSupportingTokenParameters property. + + + Policy for multiple issuer endpoints was retrieved from '{0}' but the relying party's policy does not specify which issuer endpoint to use. One of the endpoints was selected as the issuer endpoint to use. If you are using svcutil, the other endpoints will be available in commented form in the configuration as <alternativeIssuedTokenParameters>. Check the configuration to ensure that the right issuer endpoint was selected. + + + The AuthenticationManager cannot be added to the binding parameters because the binding parameters already contains a AuthenticationManager '{0}'. If you are configuring a custom AuthenticationManager for the service, please first remove any existing AuthenticationManagers from the behaviors collection before adding the custom AuthenticationManager. + + + The AuthenticationSchemes cannot be added to the binding parameters because the binding parameters already contains AuthenticationSchemes '{0}'. If you are configuring custom AuthenticationSchemes for the service, please first remove any existing AuthenticationSchemes from the behaviors collection before adding custom AuthenticationSchemes. + + + Unable to find a SecurityBindingElement. + + + The ServiceCredentials cannot be added to the binding parameters because the binding parameters already contains a SecurityCredentialsManager '{0}'. If you are configuring custom credentials for the service, please first remove any existing ServiceCredentials from the behaviors collection before adding the custom credential. + + + The ClientCredentials cannot be added to the binding parameters because the binding parameters already contains a SecurityCredentialsManager '{0}'. If you are configuring custom credentials for the channel, please first remove any existing ClientCredentials from the behaviors collection before adding the custom credential. + + + The binding ('{0}', '{1}') has been configured with a MutualCertificateDuplexBindingElement that requires a client certificate. The client certificate is currently missing. + + + The binding ('{0}', '{1}') is configured with a security token parameter '{2}' that has an incompatible security token inclusion mode '{3}'. Specify an alternate security token inclusion mode (for example, '{4}'). + + + Unable to create a bi-directional (request-reply or duplex) channel for security negotiation. Please ensure that the binding is capable of creating a bi-directional channel. + + + There are too many active security negotiations or secure conversations at the service. Please retry later. + + + There are too many pending secure conversations on the server. Please retry later. + + + The RequestSecurityToken message does not match the endpoint filters the service '{0}' is expecting incoming messages to match. This may be because the RequestSecurityToken was intended to be sent to a different service. + + + The security session requires a security token authenticator that implements '{0}'. '{1}' does not implement '{0}'. + + + The security session requires a security token resolver that implements '{1}'. The security token resolver '{0}' does not implement '{1}'. + + + The session security token authenticator returned a token of type '{0}'. The token type expected is '{1}'. + + + The session security token provider returned a token of type '{0}'. The token type expected is '{1}'. + + + The security standards manager was not specified on '{0}'. + + + The security negotiation message with action '{0}' is larger than the maximum allowed buffer size '{1}'. If you are using a streamed transport consider increasing the maximum buffer size on the transport. + + + The channel demuxer Open failed previously with exception '{0}'. + + + The security channel listener was not specified on '{0}'. + + + ExtendedProtectionPolicy specified a PolicyEnforcement of 'Always' which is not supported for the authentication mode requested. This prevents the ExtendedProtectionPolicy from being enforced. For StandardBindings use a SecurityMode of TransportWithMessageCredential and a ClientCredential type of Windows. For CustomBindings use SspiNegotiationOverTransport or KerberosOverTransport. Alternatively, specify a PolicyEnforcement of 'Never'. + + + ExtendedProtectionPolicy specified a PolicyEnforcement of 'Always' and a ChannelBinding was not found. This prevents the ExtendedProtectionPolicy from being enforced. Change the binding to make a ChannelBinding available, for StandardBindings use a SecurityMode of TransportWithMessageCredential and a ClientCredential type of Windows. For CustomBindings use SspiNegotiationOverTransport or KerberosOverTransport. Alternatively, specify a PolicyEnforcement of 'Never'. + + + The security settings lifetime manager was not specified on '{0}'. + + + The listener is not accepting new secure conversations because it is closing. + + + The server is not accepting new secure conversations currently because it is closing. Please retry later. + + + The cipher key negotiated by SSL is too small ('{0}' bits). Keys of such lengths are not allowed as they may result in information disclosure. Please configure the initiator machine to negotiate SSL cipher keys that are '{1}' bits or longer. + + + The length ('{0}' bytes) of the derived key's Nonce exceeds the maximum length ('{1}' bytes) allowed. + + + The length ('{0}' bytes) of the derived key's Label exceeds the maximum length ('{1}' bytes) allowed. + + + The derived key's Offset ('{0}' bytes) exceeds the maximum offset ('{1}' bytes) allowed. + + + The derived key's generation ('{0}') and length ('{1}' bytes) result in a key derivation offset that is greater than the maximum offset ('{2}' bytes) allowed. + + + The number of derived keys in the message has exceeded the maximum allowed number '{0}'. + + + The number of encrypted keys in the message has exceeded the maximum allowed number '{0}'. + + + Unable to finish reading Base64 data as the given buffer quota has been exceeded. Buffer quota: {0}. Consider increasing the MaxReceivedMessageSize quota on the TransportBindingElement. Please note that a very high value for MaxReceivedMessageSize will result in buffering a large message and might open the system to DOS attacks. + + + Manual addressing is not supported with message level security. Configure the binding ('{0}', '{1}') to use transport security or to not do manual addressing. + + + The target service address was not specified on '{0}'. + + + The issued token cache was not specified on '{0}'. + + + The security algorithm suite was not specified on '{0}'. + + + A security token ('{0}', '{1}') was found outside the security header. The message may have been altered in transit. + + + The SecurityTokenProvider '{0}' could not resolve the token. + + + A secure conversation cancellation is not allowed by the binding. + + + The security binding element for bootstrap security was not specified on '{0}'. + + + The context for building the issuer channel was not specified on '{0}'. + + + The binding to use to communicate to the federation service at '{0}' is not specified. + + + It is likely that certificate '{0}' may not have a private key that is capable of key exchange or the process may not have access rights for the private key. Please see inner exception for detail. + + + The certificate '{0}' must have a private key. The process must have access rights for the private key. + + + No outgoing EndpointAddress is available to check the identity on a message to be sent. + + + No outgoing EndpointAddress is available to check the identity on a received reply. + + + No signing token is available to do an incoming identity check. + + + The PSHA1 key length '{0}' is invalid. + + + Clone() was not implemented properly by '{0}'. The cloned object was '{1}'. + + + The issued token is of unexpected type '{0}'. Expected token type '{1}'. + + + The service operation '{0}' that belongs to the contract with the '{1}' name and the '{2}' namespace does not allow impersonation. + + + The RequestSecurityTokenResponse has multiple RequestedSecurityToken elements. + + + The RequestSecurityTokenResponse has multiple RequestedProofToken elements. + + + The proof token XML element is not expected in the response. + + + The key length '{0}' requested is invalid. + + + The security token parameters to use for the issued token are not set on '{0}'. + + + The message could not be processed because the action '{0}' is invalid or unrecognized. + + + Token inclusion mode '{0}' is not supported. + + + The policy to import a process cannot import a binding for contract ({0},{1}). The protection requirements for the binding are not compatible with a binding already imported for the contract. You must reconfigure the binding. + + + The symmetric security protocol can either be configured with a symmetric token provider and a symmetric token authenticator or an asymmetric token provider. It cannot be configured with both. + + + ClientCredentialType.None is not valid for the TransportWithMessageCredential security mode. Specify a message credential type or use a different security mode. + + + The security session id '{0}' is already present in the filter table. + + + A supporting token that satisfies parameters '{0}' and attachment mode '{1}' was not provided. + + + The supporting token provided for parameters '{0}' did not endorse the primary signature. + + + The supporting token provided for parameters '{0}' was not signed as part of the primary signature. + + + The supporting token provided for parameters '{0}' was not encrypted. + + + A basic token is not expected in the security header in this context. + + + The request for security token could not be satisfied because authentication failed. + + + The caller was not authenticated by the service. + + + The request for security token has invalid or malformed elements. + + + A signed supporting token is not expected in the security header in this context. + + + Security token parameters must be specified with supporting tokens for each message. + + + The signature token '{0}' is not the same token as the encryption token '{1}'. + + + The reverting operation failed with the exception '{0}'. + + + Unrecognized supporting token '{0}' was encountered. + + + More than one supporting signature was encountered using the same supporting token '{0}'. + + + An unsecured or incorrectly secured fault was received from the other party. See the inner FaultException for the fault code and detail. + + + At least one security token in the message could not be validated. + + + The message could not be processed. This is most likely because the action '{0}' is incorrect or because the message contains an invalid or expired security context token or because there is a mismatch between bindings. The security context token would be invalid if the service aborted the channel due to inactivity. To prevent the service from aborting idle sessions prematurely increase the Receive timeout on the service endpoint's binding. + + + The security context token is expired or is not valid. The message was not processed. + + + Transport security negotiation failed due to an underlying IO error: {0}. + + + The security negotiation with '{0}' cannot be initiated because the confidential endpoint address header ('{1}', '{2}') cannot be encrypted during the course of the negotiation. + + + An error occurred when processing the security tokens in the message. + + + An error occurred when verifying security for the message. + + + The service does not allow you to log on anonymously. + + + Obtaining metadata from issuer '{0}' failed with error '{1}'. + + + Importing metadata from issuer '{0}' failed with error '{1}'. + + + Multiple correlation tokens were found in the security correlation state. + + + No correlation token was found in the security correlation state. + + + Multiple supporting token authenticators with the token parameter type equal to '{0}' cannot be specified. If more than one Supporting Token of the same type is expected in the response, then configure the supporting token collection with just one entry for that SecurityTokenParameters. The SecurityTokenAuthenticator that gets created from the SecurityTokenParameters will be used to authenticate multiple tokens. It is not possible to add SecurityTokenParameters of the same type in the SupportingTokenParameters collection or repeat it across EndpointSupportingTokenParameters and OperationSupportingTokenParameters. + + + A leg of the federated security chain contains multiple IssuedSecurityTokenParameters. The InfoCard system only supports one IssuedSecurityTokenParameters for each leg. + + + An unrecognized token authenticator '{0}' was used for token processing. + + + The SecurityTokenParameters and SecurityToken tuple specified for use in the security header must both be null or must both be non-null. + + + The CloneCore method of {0} type returned an invalid result. + + + Certificate-based client authentication is not supported in TransportCredentialOnly security mode. Select the Transport security mode. + + + BasicHttp binding requires that BasicHttpBinding.Security.Message.ClientCredentialType be equivalent to the BasicHttpMessageCredentialType.Certificate credential type for secure messages. Select Transport or TransportWithMessageCredential security for UserName credentials. + + + The client must provide key entropy in key entropy mode '{0}'. + + + A Proof Token was found in the response that was returned by the Security Token Service for a Bearer Key Type token request. Note that Proof Tokens should not be generated when a Bearer Key Type request is made. + + + Bearer Key Type is not supported with WSFederationHttpBinding. Please use WS2007FederationHttpBinding. + + + Unable to create Key Type element for the Key Type '{0}'. This might be due to a wrong version of MessageSecurityVersion set on the SecurityBindingElement. + + + The issuer cannot provide key entropy or a proof token in key entropy mode '{0}'. + + + The client cannot provide key entropy in key entropy mode '{0}'. + + + The issuer must provide a proof token in key entropy mode '{0}'. + + + The issuer must provide a computed key in key entropy mode '{0}'. + + + The issuer must provide key entropy in key entropy mode '{0}'. + + + The issuer cannot provide a computed key in key entropy mode '{0}'. + + + The computed key algorithm '{0}' is not supported. + + + The ReplayWindow and ClockSkew cannot be the maximum possible value when replay detection is enabled. + + + The session channel must be opened before the session ID can be accessed. + + + The binding ('{0}','{1}') for contract ('{2}','{3}') has been configured with an incompatible security version that does not support unattached references to EncryptedKeys. Use '{4}' or higher as the security version for the binding. + + + The '{0}','{1}' binding for the '{2}','{3}' contract is configured with a security version that does not support external references to X.509 tokens using the certificate's thumbprint value. Use '{4}' or higher as the security version for the binding. + + + The SecurityBinding for the ('{0}','{1}') binding for the ('{2}','{3}') contract only supports the OneWay operation. + + + Cannot map Windows user '{0}' to a UserPrincipalName that can be used for S4U impersonation. + + + Resolving an External reference token requires appropriate SecurityTokenParameters to be specified. + + + The SecurityContextSecurityToken's key needs to be renewed. + + + The client's security session was not able to close its output session within the configured timeout ({0}). + + + Client is unable to finish the security negotiation within the configured timeout ({0}). The current negotiation leg is {1} ({2}). + + + Client is unable to request the security session within the configured timeout ({0}). + + + The service's security session was not able to close its output session within the configured timeout ({0}). + + + The service's security session did not receive a 'close' message from the client within the configured timeout ({0}). + + + The client's security session did not receive a 'close response' message from the service within the configured timeout ({0}). + + + Cannot renew the security session key. + + + Cannot renew the security session key. Session Key Renewal is not supported. + + + Error parsing SecurityContextSecurityToken Cookie XML. + + + The SecurityContextSecurityToken's Cookie element either does not contain '{0}' or has a wrong value for it. + + + Error decoding the Cookie element of SecurityContextSecurityToken. + + + Issuing cookie SecurityContextSecurityToken is not supported. + + + Security policy import failed. The security policy contains supporting token requirements at the operation scope. The contract description does not specify the action for the request message associated with this operation. + + + Signature confirmation is not expected in the security header. + + + The signature confirmation elements cannot occur after the primary signature. + + + Signature confirmation was expected to be present in the security header. + + + The SecurityVersion '{0}' does not support signature confirmation. Use a later SecurityVersion. + + + The protocol factory must support Request/Reply security in order to offer signature confirmation. + + + Not all the signatures in the request message were confirmed in the reply message. + + + The request did not have any signatures but the reply has signature confirmations. + + + There are too many renewed session keys that have not been used. + + + The session key must be renewed before it can secure application messages. + + + The token's crypto collection has multiple objects of type '{0}'. + + + The token's crypto collection does not support algorithm '{0}'. + + + SymmetricSecurityBindingElement cannot build a channel or listener factory. The ProtectionTokenParameters property is required but not set. Binding element configuration: {0} + + + AsymmetricSecurityBindingElement cannot build a channel or listener factory. The InitiatorTokenParameters property is required but not set. Binding element configuration: {0} + + + AsymmetricSecurityBindingElement cannot build a channel or listener factory. The RecipientTokenParameters property is required but not set. Binding element configuration: {0} + + + The service cannot cache the negotiation state as the capacity '{0}' has been reached. Retry the request. + + + Internal SSL error (refer to Win32 status code for details). Check the server certificate to determine if it is capable of key exchange. + + + The key rollover interval cannot be greater than the key renewal interval. + + + The request message must be protected. This is required by an operation of the contract ('{0}','{1}'). The protection must be provided by the binding ('{2}','{3}'). + + + The response message must be protected. This is required by an operation of the contract ('{0}', '{1}'). The protection must be provided by the binding ('{2}', '{3}'). + + + The contract ('{0}','{1}') contains some unknown header ('{2}','{3}') which cannot be secured. Please choose ProtectionLevel.None for this header. + + + The binding ('{0}','{1}') supports streaming which cannot be configured together with message level security. Consider choosing a different transfer mode or choosing the transport level security. + + + The supporting token in the renew message has a different generation '{0}' than the current session token's generation '{1}'. + + + Security Support Provider Interface (SSPI) authentication failed. The server may not be running in an account with identity '{0}'. If the server is running in a service account (Network Service for example), specify the account's ServicePrincipalName as the identity in the EndpointAddress for the server. If the server is running in a user account, specify the account's UserPrincipalName as the identity in the EndpointAddress for the server. + + + For this security protocol, the incoming signing token must be an EncryptedKey. + + + The security session was terminated This may be because no messages were received on the session for too long. + + + No AppliesTo element is present in the deserialized RequestSecurityToken/RequestSecurityTokenResponse. + + + Symmetric Key length {0} is not supported by the algorithm suite '{1}'. + + + For replay detection to be done ProtectionLevel must be Sign or EncryptAndSign. + + + Can't infer an external reference for '{0}' token type. + + + Unable to create Attached or Unattached reference for '{0}'. + + + The configured Trust version does not support sessions. Use WSTrustFeb2005 or above. + + + The configured WS-Trust version does not support issued tokens. WS-Trust February 2005 or later is required. + + + The binding ('{0}','{1}') for contract ('{2}','{3}') supports impersonation only on Windows 2003 Server and newer version of Windows. Use SspiNegotiated authentication and a binding with Secure Conversation with cancellation enabled. + + + Impersonation using the client token is not possible. The binding ('{0}', '{1}') for contract ('{2}', '{3}') uses the Username Security Token for client authentication with a Membership Provider registered. Use a different type of security token for the client. + + + Cannot establish a reliable session without secure conversation. Enable secure conversation. + + + Failed to revert impersonation. {0} + + + In order to flow a transaction, flowing issued tokens must also be supported. + + + The configured SecurityVersion does not support signature confirmation. Use WsSecurity11 or above. + + + The configured SecureConversation version does not support sessions. Use WSSecureConversationFeb2005 or above. + + + SOAP security negotiation failed. See inner exception for more details. + + + SOAP security negotiation with '{0}' for target '{1}' failed. See inner exception for more details. + + + The one-way operation returned a fault message. The reason for the fault was '{0}'. + + + The one-way operation returned a fault message with Action='{0}'. + + + The one-way operation returned a non-null message with Action='{0}'. + + + Cannot find the security session with the ID '{0}'. + + + The SecurityContextSecurityToken with Context-id={0} (generation-id={1}) has expired. + + + The SecurityContextSecurityToken with Context-id={0} (no key generation-id) has expired. + + + Security sessions require all messages to be signed. + + + Required timestamp missing in security header. + + + The request message in the request context received from channel '{0}' is null. + + + The key effective and expiration times must be bounded by the token effective and expiration times. + + + The valid from time is greater than the valid to time. + + + No session token was present in the message. + + + Key length '{0}' is not a multiple of 8 for symmetric keys. + + + Invalid binary representation of an X.509 certificate. + + + Security policy export failed. The binding contains a TransportSecurityBindingElement but no transport binding element that implements ITransportTokenAssertionProvider. Policy export for such a binding is not supported. Make sure the transport binding element in the binding implements the ITransportTokenAssertionProvider interface. + + + Cannot import the security policy. The protection requirements for the secure conversation bootstrap binding are not supported. Protection requirements for the secure conversation bootstrap must require both the request and the response to be signed and encrypted. + + + Cannot import the policy. The value of the attribute '{0}' must be either 'true', 'false', '1' or '0'. The following error occurred: '{1}'. + + + The security policy expert failed. The provided transport token assertion of type '{0}' did not create a transport token assertion to include the sp:TransportBinding security policy assertion. + + + Message security policy for the '{0}' action requires confidentiality without integrity. Confidentiality without integrity is not supported. + + + The primary signature must be encrypted. + + + A symmetric crypto could not be created from token '{0}'. + + + The key size requirements for the '{0}' algorithm suite are not met by the '{1}' token which has key size of '{2}'. + + + The received message does not meet the required message protection order '{0}'. + + + Primary signature must be computed before supporting token signatures. + + + Element to sign must have id. + + + The token Serializer cannot serialize '{0}'. If this is a custom type you must supply a custom serializer. + + + Signing without primary signature requires timestamp. + + + This operation cannot be done after processing is started. + + + The recursive policy fetching limit has been reached. Check to determine if there is a loop in the federation service chain. + + + The ('{0}', '{1}') signed header contains the ('{2}', '{3}') attribute. The expected attribute is ('{4}', '{5}'). + + + The address of the security token issuer is not specified. An explicit issuer address must be specified in the binding for target '{0}' or the local issuer address must be configured in the credentials. + + + More than one SecurityBindingElement found in the binding ('{0}', '{1}) for contract ('{2}', '{3}'). Only one SecurityBindingElement is allowed. + + + ClientCredentials cannot create a local token provider for token requirement {0}. + + + A security policy was imported for the endpoint. The security policy contains requirements that cannot be represented in a Windows Communication Foundation configuration. Look for a comment about the SecurityBindingElement parameters that are required in the configuration file that was generated. Create the correct binding element with code. The binding configuration that is in the configuration file is not secure. + + + The configuration schema is insufficient to describe the non-standard configuration of the following security binding element: + + + The wsdl schema that was used to create this configuration file contained a 'RequireIssuerSerialReference' assertion for a X509Token. This can not be represented in configuration, you will need to programatically adjust the appropriate X509SecurityTokenParameters.X509KeyIdentifierClauseType to X509KeyIdentifierClauseType.IssuerSerial. The default of X509KeyIdentifierClauseType.Thumbprint will be used, which may cause interop issues. + + + The security protocol '{0}' cannot do replay detection. + + + Security processor was unable to find a security header with actor '{0}' in the message. This might be because the message is an unsecured fault or because there is a binding mismatch between the communicating parties. This can occur if the service is configured for security and the client is not using security. + + + Security processor was unable to find a security header in the message. This might be because the message is an unsecured fault or because there is a binding mismatch between the communicating parties. This can occur if the service is configured for security and the client is not using security. + + + No primary signature available for supporting token signature verification. + + + Supporting token signatures not expected. + + + Cannot read the token from the '{0}' element with the '{1}' namespace for BinarySecretSecurityToken, with a '{2}' ValueType. If this element is expected to be valid, ensure that security is configured to consume tokens with the name, namespace and value type specified. + + + Element '{0}' with namespace '{1}' not found. + + + Expected element '{0}' or element '{1}' (from namespace '{2}'). + + + AcceleratedTokenAuthenticator does not expect RequestSecurityTokenResponse from the client. + + + The '{0}' binding with the '{1}' namespace is configured to issue cookie security context tokens. COM+ Integration services does not support cookie security context tokens. + + + The signature must be in the security header. + + + The '{0}' required message part was not signed. + + + The '{0}', '{1}' required message part was not signed. + + + The '{0}' required message part was not encrypted. + + + The '{0}', '{1}' required message part was not encrypted. + + + Signature verification failed. + + + Cannot issue the token type '{0}'. + + + There is no negotiation message to send. + + + The issued token has an invalid key size '{0}'. + + + Cannot determine the key size of the issued token. + + + The negotiation has not yet completed. + + + The negotiation has already completed. + + + Request Message is missing a MessageID header. One is required to correlate a reply. + + + Cannot create a security session. Retry later. + + + The security session with id '{0}' is already pending. + + + No security session with id '{0}' is pending. + + + No security session listener was found for message with action '{0}'. + + + The session token was not closed by the server. + + + '{0}' protocol can only be used by the Initiator. + + + '{0}' protocol can only be used at the Recipient. + + + Unexpected code path for server security application, sending outgoing message on Recipient. + + + Only body return values are supported currently for protection, MessagePartDescription was specified. + + + Unknown token attachment mode: {0}. + + + Security protocol must be '{0}', type is: '{1}'.; + + + The initial request context was already specified. Can not create two for same message. + + + {0}.OnCloseMessageReceived when state == Created. + + + Shutdown request was not received. + + + Unknown filter type: '{0}'. + + + Standards manager of filter does not match that of filter table. Can not have two different filters. + + + Session filter's isStrictMode differs from filter table's isStrictMode. + + + SecuritySessionServerSettings.CreateAcceptor, channelAcceptor must be null, can not create twice. + + + Invalid TransactionFlowOption value. + + + Security token manager could not parse token with name '{0}', namespace '{1}', valueType '{2}'. + + + Security negotiation message has incorrect action '{0}'. + + + The specified key size {0} is invalid. The key size must be between {1} and {2}. + + + Could not get token information (error=0x{0:X}). + + + Unexpected end of file. + + + The security timestamp is invalid because its creation time ('{0}') is greater than or equal to its expiration time ('{1}'). + + + The security timestamp is stale because its expiration time ('{0}') is in the past. Current time is '{1}' and allowed clock skew is '{2}'. + + + The security timestamp is invalid because its creation time ('{0}') is in the future. Current time is '{1}' and allowed clock skew is '{2}'. + + + The security timestamp is stale because its creation time ('{0}') is too far back in the past. Current time is '{1}', maximum timestamp lifetime is '{2}' and allowed clock skew is '{3}'. + + + The nonce is invalid or replayed. + + + Message part specification must be made constant before being set. + + + Issuer entropy is not BinarySecretSecurityToken or WrappedKeySecurityToken. + + + No RequestSecurityTokenResponse elements were found. + + + The SecurityContextSecurityToken does not have a cookie. + + + TokenProvider returned token of incorrect type '{0}'. + + + {0} is not available in deserialized RequestSecurityToken. + + + {0} is only available in a deserialized RequestSecurityToken. + + + {0} is not available in deserialized RequestSecurityTokenResponse. + + + {0} is only available in a deserialized RequestSecurityTokenResponse. + + + The RequestSecurityTokenResponseCollection received has more than one RequestSecurityTokenResponse element. Only one RequestSecurityTokenResponse element was expected. + + + VirtualPathExtension is not allowed to be removed. + + + The protocol '{0}' is not supported. + + + The BaseUriWithWildcard object has invalid fields after deserialization. + + + Registered relativeAddress '{0}' in configuration file is not a valid one. Possible causes could be : You specified an empty addreess or an absolute address (i.e., starting with '/' or '\\'), or the address contains invalid character[s]. The supported relativeAddress formats are "[folder/]filename" or "~/[folder/]filename". + + + '{0}' is an absolute address. The supported relativeAddress formats are "[subfolder/]filename" or "~/[subfolder/]filename". + + + Cannot create security binding element based on the configuration data. When secure conversation authentication mode is selected, the secure conversation bootstrap binding element must also be specified. + + + Setting minFreeMemoryPercentageToActivateService requires full trust privilege. Please change the application's trust level or remove this setting from the configuration file. + + + This service requires ASP.NET compatibility and must be hosted in IIS. Either host the service in IIS with ASP.NET compatibility turned on in web.config or set the AspNetCompatibilityRequirementsAttribute.AspNetCompatibilityRequirementsMode property to a value other than Required. + + + The '{0}' protocol binding '{1}' specifies an invalid port number '{2}'. + + + The protocol binding '{0}' does not conform to the syntax for '{1}'. The following is an example of valid '{1}' protocol bindings: '{2}'. + + + The protocol binding '{0}' is not valid for '{1}'. This might be because the port number is out of range. + + + There is no compatible TransportManager found for URI '{0}'. This may be because you have used an absolute address that points outside of the virtual application. Please use a relative address instead. + + + There is no compatible TransportManager found for URI '{0}'. This may be because you have used an absolute address that points outside of the virtual application, or the binding settings of the endpoint do not match those that have been set by other services or endpoints. Note that all bindings for the same protocol should have the same settings in the same application. + + + '{0}' cannot be invoked within the current hosting environment. This API requires that the calling application be hosted in IIS or WAS. + + + The requested service, '{0}' could not be activated. See the server's diagnostic trace logs for more information. + + + The value for the Service attribute was not provided in the ServiceHost directive. + + + The service endpoint failed to listen on the URI '{0}' because access was denied. Verify that the current user is granted access in the appropriate allowAccounts section of SMSvcHost.exe.config. + + + The service endpoint failed to listen on the URI '{0}' because the shared memory section was not found. Verify that the '{1}' service is running. + + + The TransportManager failed to listen on the supplied URI using the {0} service: {1}. + + + failed to start the service ({0}). Refer to the Event Log for more details + + + failed to start the service because it is disabled. An administrator can enable it by running 'sc.exe config {0} start= demand'. + + + failed to start the service. Refer to the Event Log for more details + + + failed to look up the service process in the SCM ({0}) + + + failed to look up the service SID in the SCM ({0}) + + + failed to read the service's endpoint with native error code {0}. See inner exception for details + + + the service failed the security checks + + + failed to retrieve the UserSid of the service process ({0}) + + + failed to retrieve the UserSid of the current process + + + failed to retrieve the LogonSid of the service process ({0}) + + + failed to establish a data connection to the service + + + failed to create a data connection to the service + + + failed to establish the data connection because of an I/O error + + + the version is not supported by the service + + + failed to grant the PROCESS_DUP_HANDLE access right to the target service's account SID '{0}'. + + + the URI is too long + + + the quota was exceeded + + + the protocol is not supported + + + the URI is already registered with the service + + + the service failed to listen + + + The message could not be dispatched to the service at address '{0}'. Refer to the server Event Log for more details + + + The message could not be dispatched because the service at the endpoint address '{0}' is unavailable for the protocol of the address. + + + The endpoint address for the NT service '{0}' read from shared memory is empty. + + + The message could not be dispatched because the transport manager has been stopped. This can happen if the application is being recycled or disabled. + + + The '{0}' from the '{1}' namespace is empty and does not specify a valid identity claim. + + + '{0}' from namespace '{1}' is not expected. Expecting element '{2}' from namespace '{3}' + + + '{0}' from namespace '{1}' is not expected to appear more than once + + + An unsupported security policy assertion was detected during the security policy import: {0} + + + The extensions cannot contain an Identity if one is supplied as a constructor argument. + + + Value '{0}' provided for '{1}' from namespace '{2}' is an invalid absolute URI. + + + The binding ('{0}','{1}') for contract ('{2}','{3}') is configured with SecureConversation, but the authentication mode is not able to provide the request/reply-based integrity and confidentiality required for the negotiation. + + + The '{0}'.'{1}' binding for the '{2}'.'{3}' contract is configured with an authentication mode that requires transport level integrity and confidentiality. However the transport cannot provide integrity and confidentiality. + + + The contract operation '{0}' requires Windows identity for automatic impersonation. A Windows identity that represents the caller is not provided by binding ('{1}','{2}') for contract ('{3}','{4}'. + + + A listen URI must be specified in order to open this {0}. + + + Channel interface type '{0}' is not supported. + + + This property cannot be changed after the transport manager has been opened. + + + This operation is only valid after the transport manager has been opened. + + + Unrecognized identity type Name='{0}', Namespace='{1}'. + + + Cannot read the Identity element. The Identity type is not supported or the Identity element is empty. + + + Cannot load the X.509 certificate identity specified in the configuration. + + + The ClaimType '{0}' is not recognized. Expected ClaimType '{1}'. + + + An AsyncCallback threw an exception. + + + You cannot Send messages on a channel after CloseOutputSession has been called. + + + The communication object, {0}, cannot be modified while it is in the {1} state. + + + The communication object, {0}, cannot be modified unless it is in the Created state. + + + The communication object, {0}, is in the {1} state. Communication objects cannot be used for communication unless they are in the Opened state. + + + The communication object, {0}, cannot be used for communication because it is in the Faulted state. + + + The communication object, {0}, cannot be used for communication because it is in the Faulted state: {1} + + + The communication object, {0}, cannot be used for communication because it has been Aborted. + + + The communication object, {0}, cannot be used for communication because it has been Aborted: {1} + + + The communication object, {0}, has overridden the virtual function {1} but it does not call version defined in the base class. + + + The communication object, {0}, is not part of WCF and is in an unsupported state '{1}'. This indicates an internal error in the implementation of that communication object. + + + The communication object, {0}, cannot be used due to an error that occurred during close. + + + A call to IChannelFactory.CreateChannel made on an object of type {0} failed because Open has not been called on this object. + + + Cannot modify channel parameters because the {0} is in the {1} state. This operation is only supported in the Created state. + + + Cannot propagate channel parameters because the {0} is in the {1} state. This operation is only supported in the Opening or Opened state when the collection is locked. + + + Binding '{0}' is not configured properly. OneWayBindingElement requires an inner binding element that supports IRequestChannel/IReplyChannel or IDuplexSessionChannel. + + + The specified channel type {0} is not supported by this channel manager. + + + SecurityContext for the UltimateReceiver role is missing from the SecurityContextProperty of the request message with action '{0}'. + + + Cannot start impersonation because the SecurityContext for the UltimateReceiver role from the request message with the '{0}' action is not mapped to a Windows identity. + + + Unexpected internal enum value: {0}. + + + Invalid decoder state machine. + + + Operation property of OperationAttributeGenerationContext is required to generate an attribute based on settings. + + + The username/password Membership provider {0} specified in the configuration is invalid. No such provider was found registered under system.web/membership/providers. + + + The RoleProvider {0} specified in the configuration is invalid. No such provider was found registered under system.web/roleManager/providers. + + + The {0} object has been disposed. + + + The XmlReader used for the body of the message must be positioned on an element. + + + A property with the name '{0}' already exists. + + + A property with the name '{0}' is not present. + + + The message header with name '{0}' and namespace '{1}' is already present in the set of understood headers. + + + The message header with name '{0}' and namespace '{1}' is not present in the set of understood headers. + + + Multiple headers with name '{0}' and namespace '{1}' found. + + + Multiple headers with name '{0}' and namespace '{1}' and role '{2}' found. + + + Multiple RelatesTo headers with relationship '{0}' found. Only one is allowed per relationship. + + + Additional XML content is present in the fault detail element. Only a single element is allowed. + + + The body of the message cannot be read because it is empty. + + + Message is closed. + + + The operation cannot be completed because the stream is closed. + + + The body writer returned from OnCreateBufferedCopy was not buffered. + + + The body writer does not support writing more than once because it is not buffered. + + + KeySize element not present in RequestSecurityTokenResponse. + + + A reply message cannot be created because the request message does not have a MessageID. + + + There is not a header with name {0} and namespace {1} in the message. + + + MessageBuffer is closed. + + + The text encoding '{0}' used in the text message format is not supported. + + + At least one fault reason must be specified. + + + The translation set cannot contain nulls. + + + The fault does not have detail information. + + + Expected XML qualified name, found '{0}'. + + + Unbound prefix used in qualified name '{0}'. + + + ... + + + ... stream ... + + + ... Error reading body: {0}: {1} ... + + + The fault reason does not contain any text translations. + + + Client cannot determine the Service Principal Name based on the identity in the target address '{0}' for the purpose of SspiNegotiation/Kerberos. The target address identity must be a UPN identity (like acmedomain\\alice) or SPN identity (like host/bobs-machine). + + + Required xml:lang attribute value is missing. + + + Unrecognized charSet '{0}' in contentType. + + + Unrecognized contentType ({0}). Expected: {1}. + + + Cannot process contentType. + + + The envelope version of the incoming message ({0}) does not match that of the encoder ({1}). Make sure the binding is configured with the same version as the expected messages. + + + The message version of the outgoing message ({0}) does not match that of the encoder ({1}). Make sure the binding is configured with the same version as the message. + + + MessageVersion '{0}' not supported by MTOM encoder. + + + Read is not supported on this stream. + + + Seek is not supported on this stream. + + + An asynchronous write is pending on the stream. Ensure that there are no uncompleted asynchronous writes before attempting the next write. + + + A newly accepted connection did not receive initialization data from the sender within the configured ChannelInitializationTimeout ({0}). As a result, the connection will be aborted. If you are on a highly congested network, or your sending machine is heavily loaded, consider increasing this value or load-balancing your server. + + + The remote endpoint of the socket ({0}) did not respond to a close request within the allotted timeout ({1}). It is likely that the remote endpoint is not calling Close after receiving the EOF signal (null) from Receive. The time allotted to this operation may have been a portion of a longer timeout. + + + A graceful close was attempted on the socket, but the other side ({0}) is still sending data. + + + The pipe cannot be closed while a write to the pipe is pending. + + + The shutdown indicator could not be written to the pipe. The application on the other end of the pipe may not be listening for it. The pipe will still be closed. + + + The shutdown indicator was not received from the pipe. The application on the other end of the pipe may not have sent it. The pipe will still be closed. + + + The pipe name could not be obtained for the pipe URI: {0} + + + The pipe name could not be obtained for {0}. + + + The pipe was not able to be set to message mode: {0} + + + The pipe could not close gracefully. This may be caused by the application on the other end of the pipe exiting. + + + The pipe cannot be written to because it is already in the process of shutting down. + + + The read from the pipe expected just a signal, but received actual data. + + + The pipe cannot be written to or read from because it is already in the process of being closed. + + + Server cannot accept pipe: {0} + + + Cannot listen on pipe '{0}': {1} + + + Cannot listen on pipe name '{0}' because another pipe endpoint is already listening on that name. + + + Cannot listen on pipe '{0}' because the pipe name could not be reserved: {1} + + + The pipe listener has been disposed. + + + Connections cannot be created until the pipe has started listening. Call Listen() before attempting to accept a connection. + + + A pipe endpoint exists for '{0}', but the connect failed: {1} + + + Cannot connect to endpoint '{0}'. + + + Cannot connect to endpoint '{0}' within the allotted timeout of {1}. The time allotted to this operation may have been a portion of a longer timeout. + + + Cannot connect to endpoint '{0}' within the allotted timeout of {1}. The server has likely reached the MaxConnections quota and is too busy to accept new connections. The time allotted to this operation may have been a portion of a longer timeout. + + + The pipe endpoint '{0}' could not be found on your local machine. + + + URIs used with pipes must use the scheme: 'net.pipe'. + + + The pipe write did not write all the bytes. + + + The operation cannot be completed because the pipe was closed. This may have been caused by the application on the other end of the pipe exiting. + + + The read from the pipe did not complete within the allotted timeout of {0}. The time allotted to this operation may have been a portion of a longer timeout. + + + The write to the pipe did not complete within the allotted timeout of {0}. The time allotted to this operation may have been a portion of a longer timeout. + + + The pipe connection was aborted because an asynchronous read from the pipe did not complete within the allotted timeout of {0}. The time allotted to this operation may have been a portion of a longer timeout. + + + The pipe connection was aborted because an asynchronous write to the pipe did not complete within the allotted timeout of {0}. The time allotted to this operation may have been a portion of a longer timeout. + + + There was an error writing to the pipe: {0}. + + + There was an error reading from the pipe: {0}. + + + Unrecognized error {0} (0x{1}) + + + {0} ({1}, 0x{2}) + + + There is already a write in progress for the pipe. Wait for the first operation to complete before attempting to write again. + + + There is already a read in progress for the pipe. Wait for the first operation to complete before attempting to read again. + + + There was an error duplicating the. + + + The Session value '{0}' is invalid. Please specify 'CurrentSession','ServiceSession' or a valid non-negative Windows Session Id. + + + The package full name '{0}' is invalid. + + + The socket was aborted because an asynchronous receive from the socket did not complete within the allotted timeout of {0}. The time allotted to this operation may have been a portion of a longer timeout. + + + The socket connection was aborted because an asynchronous send to the socket did not complete within the allotted timeout of {0}. The time allotted to this operation may have been a portion of a longer timeout. + + + This operation is not valid until security negotiation is complete. + + + Error while reading message framing format at position {0} of stream (state: {1}) + + + More data was expected, but EOF was reached. + + + Expected record type '{0}', found '{1}'. + + + Framing major version {0} is not supported. + + + Framing mode {0} is not supported. + + + Specified size is too large for this implementation. + + + The framing via size ({0}) exceeds the quota. + + + The framing via ({0}) is not a valid URI. + + + The framing fault size ({0}) exceeds the quota. + + + The framing content type size ({0}) exceeds the quota. + + + The value cannot be accessed because it has not yet been fully decoded. + + + An attempt was made to decode a value after the framing stream was ended. + + + Stream Security is required at {0}, but no security context was negotiated. This is likely caused by the remote endpoint missing a StreamSecurityBindingElement from its binding. + + + The binary encoder session information exceeded the maximum size quota ({0}). To increase this quota, use the MaxSessionSize property on the BinaryMessageEncodingBindingElement. + + + The binary encoder session is not valid. There was an error decoding a previous message. + + + The binary encoder session information is not properly formed. + + + The channel received an unexpected fault input message while closing. The fault reason given is: '{0}' + + + The channel received an unexpected fault input message with Action = '{0}' while closing. You should only close your channel when you are not expecting any more input messages. + + + The channel received an unexpected input message with Action '{0}' while closing. You should only close your channel when you are not expecting any more input messages. + + + The maximum message size quota for incoming messages ({0}) has been exceeded. To increase the quota, use the MaxReceivedMessageSize property on the appropriate binding element. + + + The maximum message size quota for outgoing messages ({0}) has been exceeded. + + + The maximum message size quota for incoming messages has been exceeded for the remote channel. See the server logs for more details. + + + TimeoutStream requires an inner Stream that supports timeouts; its CanTimeout property must be true. + + + The filter already exists in the filter table. + + + An internal error has occurred. Unexpected error modifying filter table. + + + The number of XML infoset nodes inspected by the navigator has exceeded the quota ({0}). + + + Value cannot be negative. + + + The set of actions cannot be empty. + + + The prefix '{0}' is not defined. + + + Multiple filters matched. + + + The type of IMessageFilterTable created for a particular Filter type must always be the same. + + + The MessageFilterTable state is corrupt. The requested lookup cannot be performed. + + + The IMessageFilterTable created for a Filter cannot be a MessageFilterTable or a subclass of MessageFilterTable. + + + NodeQuota must be greater than 0. + + + Parameter value cannot be an empty string. + + + Required inner element '{0}' was not found. + + + Invalid attribute on the XPath. + + + When present, the dialect attribute must have the value '{0}'. + + + Could not compile the XPath expression '{0}' with the given XsltContext. + + + XmlReader not positioned at a start element. + + + The position is not valid for this navigator. + + + Cannot call '{0}' on a non-atomized navigator. + + + XML unique ID not supported. + + + A filter has attempted to access the body of a Message. Use a MessageBuffer instead if body filtering is required. + + + Not allowed to override prefix '{0}'. + + + The function '{0}' is not implemented. + + + XPathNavigator positions cannot be compared. + + + XPathNavigator must be a SeekableXPathNavigator. + + + Context node is not supported in node sequences. + + + IXsltContextFunction return type '{0}' not supported. + + + IXsltContextVariable type '{0}' not supported. + + + IXsltContextVariables cannot return null. + + + The argument to an IXsltContextFunction could not be converted to a string. + + + An internal error has occurred. Item already exists. + + + Positioned before first element. + + + Positioned after last element. + + + The XPathNodeIterator has been invalidated. XPathNodeIterators passed as arguments to IXsltContextFunctions are only valid within the function. They cannot be cached for later use or returned as the result of the function. + + + The string value can't be determined because the XPathNodeIterator has been moved past the first node. + + + {0} {1} + + + Addressing10 ({0}) + + + Addressing200408 ({0}) + + + AddressingNone ({0}) + + + Addressing Version '{0}' is not supported. + + + The '{0}' addressing mode is not supported. + + + Soap11 ({0}) + + + Soap12 ({0}) + + + EnvelopeNone ({0}) + + + The IMessageProperty could not be copied. CreateCopy returned null. + + + Unrecognized message version. + + + Unrecognized envelope version: {0}. + + + Envelope Version '{0}' is not supported. + + + Cannot detect WS-Addressing version. EndpointReference does not start with an Element. + + + Envelope Version '{0}' does not support adding Message Headers. + + + Addressing Version '{0}' does not support adding WS-Addressing headers. + + + The element '{0}' in namespace '{1}' is not valid. This either means that element '{0}' is a duplicate element, or that it is not a legal extension because extension elements cannot be in the addressing namespace. + + + The '{0}' header cannot be added because it does not support the specified message version '{1}'. + + + This message cannot support the operation because it has been copied. + + + This message cannot support the operation because it has been written. + + + This message cannot support the operation because it has been read. + + + An internal error has occurred. Invalid MessageState. + + + The body reader is in ReadState '{0}' and cannot be consumed. + + + The size necessary to buffer the XML content exceeded the buffer quota. + + + An internal error has occurred. The XML buffer is not in the correct state to perform the operation. + + + A body element was not found inside the message envelope. + + + The version of the header(s) ({0}) differs from the version of the message ({1}). + + + Manual addressing is enabled on this factory, so all messages sent must be pre-addressed. + + + A one-way header was expected on this message and none was found. It is possible that your bindings are mismatched. + + + Receive on local address {0} timed out after {1}. The time allotted to this operation may have been a portion of a longer timeout. + + + Receive timed out after {0}. The time allotted to this operation may have been a portion of a longer timeout. + + + WaitForMessage timed out after {0}. The time allotted to this operation may have been a portion of a longer timeout. + + + Receive timed out after {0}. The time allotted to this operation may have been a portion of a longer timeout. + + + Receive request timed out after {0}. The time allotted to this operation may have been a portion of a longer timeout. + + + Receive request on local address {0} timed out after {1}. The time allotted to this operation may have been a portion of a longer timeout. + + + Sending to via {0} timed out after {1}. The time allotted to this operation may have been a portion of a longer timeout. + + + Close timed out after {0}. Increase the timeout value passed to the call to Close or increase the CloseTimeout value on the Binding. The time allotted to this operation may have been a portion of a longer timeout. + + + Open timed out after {0} while establishing a transport session to {1}. The time allotted to this operation may have been a portion of a longer timeout. + + + Request timed out after {0} while establishing a transport connection to {1}. The time allotted to this operation may have been a portion of a longer timeout. + + + Connecting to via {0} timed out after {1}. Connection attempts were made to {2} of {3} available addresses ({4}). Check the RemoteAddress of your channel and verify that the DNS records for this endpoint correspond to valid IP Addresses. The time allotted to this operation may have been a portion of a longer timeout. + + + The request channel timed out attempting to send after {0}. Increase the timeout value passed to the call to Request or increase the SendTimeout value on the Binding. The time allotted to this operation may have been a portion of a longer timeout. + + + The request channel timed out while waiting for a reply after {0}. Increase the timeout value passed to the call to Request or increase the SendTimeout value on the Binding. The time allotted to this operation may have been a portion of a longer timeout. + + + The policy being imported for contract '{0}:{1}' contains multiple HTTP authentication scheme assertions. Since at most one such assertion is allowed, policy import has failed. This may be resolved by updating the policy to contain no more than one HTTP authentication scheme assertion. + + + More than one '{0}' objects were found in the BindingParameters of the BindingContext. This is usually caused by having multiple '{0}' objects in a CustomBinding. Remove all but one of these elements. + + + The '{0}' can only be used with HTTP (or HTTPS) transport. + + + The value specified, '{0}', for the If-Modified-Since header does not parse into a valid date. Check the property value and ensure that it is of the proper format. + + + The SOAP action specified on the message, '{0}', does not match the action specified on the HttpRequestMessageProperty, '{1}'. + + + The SOAP action specified on the message, '{0}', does not match the action specified in the content-type of the HttpRequestMessageProperty, '{1}'. + + + The SOAP action specified on the message, '{0}', does not match the HTTP SOAP Action, '{1}'. + + + An error ({0}) occurred while parsing the content type of the HTTP request. The content type was: {1}. + + + The HTTP service located at {0} is unavailable. This could be because the service is too busy or because no endpoint was found listening at the specified address. Please ensure that the address is correct and try accessing the service again later. + + + The HTTP request to '{0}' was aborted. This may be due to the local channel being closed while the request was still in progress. If this behavior is not desired, then update your code so that it does not close the channel while request operations are still in progress. + + + The HTTP request to '{0}' has exceeded the allotted timeout of {1}. The time allotted to this operation may have been a portion of a longer timeout. + + + The HTTP request to '{0}' has exceeded the allotted timeout of {1} while reading the response. The time allotted to this operation may have been a portion of a longer timeout. + + + An error ({0}) occurred while transmitting data over the HTTP channel. + + + An error occurred while receiving the HTTP response to {0}. This could be due to the service endpoint binding not using the HTTP protocol. This could also be due to an HTTP request context being aborted by the server (possibly due to the service shutting down). See server logs for more details. + + + An error occurred while making the HTTP request to {0}. This could be due to the fact that the server certificate is not configured properly with HTTP.SYS in the HTTPS case. This could also be caused by a mismatch of the security binding between the client and the server. + + + HTTP request streaming cannot be used in conjunction with HTTP authentication. Either disable request streaming or specify anonymous HTTP authentication. + + + A reply has already been sent from this RequestContext. + + + Unable to start the HTTP listener. The URI provided, '{0}', is invalid for listening. Check the base address of your service and verify that it is a valid URI. + + + The requestContext has been aborted. + + + The receive context, {0}, is in the {1} state. Receive contexts cannot be used for sending delayed acks unless they are in the Received state. + + + The receive context, {0}, is in an unsupported state '{1}'. This indicates an internal error in the implementation of that receive context. + + + The receive context, {0}, cannot be used for sending delayed acks because it is in the Faulted state. + + + Invalid HostNameComparisonMode value: {0}. + + + Invalid data buffer. + + + A security session renew response was received with an invalid action '{0}'. + + + A security session close response was received with an invalid action '{0}', + + + TransactedBatchingBehavior cannot be used when ReceiveContext is being used. + + + Could not formulate request message for security session operation '{0}'. + + + There is no handler registered for session token issuance event. + + + There is no handler registered for session token renew event. + + + The identity of the security session renew message does not match the identity of the session token. + + + The RequestSecurityToken has an invalid or unspecified RequestType '{0}'. + + + The RequestSecurityToken must specify a CloseTarget. + + + Secure channel cannot be opened because security negotiation with the remote endpoint has failed. This may be due to absent or incorrectly specified EndpointIdentity in the EndpointAddress used to create the channel. Please verify the EndpointIdentity specified or implied by the EndpointAddress correctly identifies the remote endpoint. + + + The CloseTarget specified '{0}' does not identify the security token that signed the message. + + + The renew security session message does not have the session token as a supporting token. + + + The RequestSecurityToken must specify a RenewTarget. + + + There is no endorsing session token that matches the specified RenewTarget '{0}'. + + + Invalid format for encrypted body. + + + The EncryptedData or EncryptedKey is in an invalid state for this operation. + + + No signature message parts were specified for messages with the '{0}' action. + + + No encryption message parts were specified for messages with the '{0}' action. + + + The receiver sent back a security session fault message. Retry the request. + + + The Inner listener factory of {0} must be set before this operation. + + + Cannot create security binding element based on configuration data. The secure conversation bootstrap requires another secure conversation which is not supported. + + + Cannot open ChannelFactory as the inner channel factory was not set during the initialization process. + + + Duplex security is not supported by the security protocol factory '{0}'. + + + Request-reply security is not supported by the security protocol factory '{0}'. + + + The security protocol factory must be set before this operation is performed. + + + Security session protocol factory must be set before this operation is performed. + + + Security channel or listener factory creation failed. Secure conversation security token parameters do not specify the bootstrap security binding element. + + + The required '{0}' property on the '{1}' security protocol factory is not set or has an invalid value. + + + The protocol factory cannot create a protocol. + + + The identity check failed for the outgoing message. The expected identity is '{0}' for the '{1}' target endpoint. + + + The identity check failed for the incoming message. The expected identity is '{0}' for the '{1}' target endpoint. + + + The Identity check failed for the incoming message. The remote endpoint did not provide a domain name system (DNS) claim and therefore did not satisfied DNS identity '{0}'. This may be caused by lack of DNS or CN name in the remote endpoint X.509 certificate's distinguished name. + + + The Identity check failed for the outgoing message. The remote endpoint did not provide a domain name system (DNS) claim and therefore did not satisfied DNS identity '{0}'. This may be caused by lack of DNS or CN name in the remote endpoint X.509 certificate's distinguished name. + + + Identity check failed for incoming message. The expected DNS identity of the remote endpoint was '{0}' but the remote endpoint provided DNS claim '{1}'. If this is a legitimate remote endpoint, you can fix the problem by explicitly specifying DNS identity '{1}' as the Identity property of EndpointAddress when creating channel proxy. + + + Identity check failed for outgoing message. The expected DNS identity of the remote endpoint was '{0}' but the remote endpoint provided DNS claim '{1}'. If this is a legitimate remote endpoint, you can fix the problem by explicitly specifying DNS identity '{1}' as the Identity property of EndpointAddress when creating channel proxy. + + + The serialized token version {0} is unsupported. + + + The RequestSecurityTokenResponseCollection does not contain an authenticator. + + + The negotiation RequestSecurityTokenResponse has a different context from the authenticator RequestSecurityTokenResponse. + + + The recipient did not provide its certificate. This certificate is required by the TLS protocol. Both parties must have access to their certificates. + + + The authenticator was not included in the final leg of negotiation. + + + The RequestSecurityTokenResponse CombinedHash is incorrect. + + + The certificate for the client has not been provided. The certificate can be set on the ClientCredentials or ServiceCredentials. + + + The client certificate is not provided. Specify a client certificate in ServiceCredentials. + + + The client certificate is not provided. Specify a client certificate in ClientCredentials. + + + The service certificate is not provided. Specify a service certificate in ServiceCredentials. + + + The service certificate is not provided for target '{0}'. Specify a service certificate in ClientCredentials. + + + The username is not provided. Specify username in ClientCredentials. + + + Object is read-only. + + + Element {0} cannot be empty. + + + XML child node {0} of type {1} is unexpected for element {2}. + + + The context-id={0} (generation-id={1}) is already registered with SecurityContextSecurityTokenAuthenticator. + + + The context-id={0} (no key generation-id) is already registered with SecurityContextSecurityTokenAuthenticator. + + + There is no SecurityContextSecurityToken with context-id={0} (generation-id={1}) registered with SecurityContextSecurityTokenAuthenticator. + + + There is no SecurityContextSecurityToken with context-id={0} (no key generation-id) registered with SecurityContextSecurityTokenAuthenticator. + + + The SecurityContextSecurityToken has an invalid Cookie. The following error occurred when processing the Cookie: '{0}'. + + + The SecurityContextSecurityToken with context-id={0} (key generation-id={1}) is not registered. + + + The SecurityContextSecurityToken with context-id={0} (key generation-id={1}) has expired. + + + The SecurityContextSecurityToken with context-id={0} (no key generation-id) has expired. + + + The SecurityContextSecurityToken does not have a context-id. + + + For sending a message on server side composite duplex channels, the message must have either the 'Via' property or the 'To' header set. + + + The 'Via' property on the message is set to Anonymous Uri '{0}'. Please set the 'Via' property to a non-anonymous address as message cannot be addressed to anonymous Uri on server side composite duplex channels. + + + The 'To' header on the message is set to Anonymous Uri '{0}'. Please set the 'To' header to a non-anonymous address as message cannot be addressed to anonymous Uri on server side composite duplex channels. + + + This SecurityProtocol instance was not set up to process outgoing messages. + + + This SecurityProtocol instance was not set up to process incoming messages. + + + The token provider cannot get tokens for target '{0}'. + + + Key derivation algorithm '{0}' is not supported. + + + Cannot find the correlation state for applying security to reply at the responder. + + + The reply was not signed with the required signing token. + + + Encryption not expected for this message. + + + A signature is not expected for this message. + + + The QName is invalid. + + + The ICrypto implementation '{0}' is not supported. + + + On DuplexSecurityProtocolFactory, the same protocol factory cannot be set for the forward and reverse directions. + + + The algorithm '{0}' is not accepted for operation '{1}' by algorithm suite {2}. + + + '{0}' does not support '{1}' creation. + + + Cannot create an ICrypto interface from the '{0}' token for signature verification. + + + Message security verification failed. + + + Transport secured messages should have the 'To' header specified. + + + The message received over Transport security was missing the 'To' header. + + + The message received over Transport security has unsigned 'To' header. + + + More than one 'To' header specified in a message secured by Transport Security. + + + Received security header contains unexpected token '{0}'. + + + Cannot find the X.509 certificate using the following search criteria: StoreName '{0}', StoreLocation '{1}', FindType '{2}', FindValue '{3}'. + + + Cannot find The X.509 certificate using the following search criteria: StoreName '{0}', StoreLocation '{1}', FindType '{2}', FindValue '{3}' for target '{4}'. + + + Found multiple X.509 certificates using the following search criteria: StoreName '{0}', StoreLocation '{1}', FindType '{2}', FindValue '{3}'. Provide a more specific find value. + + + Found multiple X.509 certificates using the following search criteria: StoreName '{0}', StoreLocation '{1}', FindType '{2}', FindValue '{3}' for target '{4}'. Provide a more specific find value. + + + The KeyInfo clause is missing or empty in EncryptedKey. + + + The EncryptedKey clause was not wrapped with the required encryption token '{0}'. + + + The message was not encrypted with the required encryption token. + + + The timestamp must occur first in this security header layout. + + + The timestamp must occur last in this security header layout. + + + Only one primary signature is allowed in a security header. + + + The signing token {0} has no keys. The security token is used in a context that requires it to perform cryptographic operations, but the token contains no cryptographic keys. Either the token type does not support cryptographic operations, or the particular token instance does not contain cryptographic keys. Check your configuration to ensure that cryptographically disabled token types (for example, UserNameSecurityToken) are not specified in a context that requires cryptographic operations (for example, an endorsing supporting token). + + + The signing token {0} has no key that supports the algorithm suite {1}. + + + Delayed security application has already been completed. + + + Cannot resolve KeyInfo in derived key token for resolving source token: KeyInfoClause '{0}'. + + + KeyInfo clause '{0}' resolved to token '{1}', which does not contain a Symmetric key that can be used for derivation. + + + Cannot resolve KeyInfo for verifying signature: KeyInfo '{0}', available tokens '{1}'. + + + Cannot resolve KeyInfo for unwrapping key: KeyInfo '{0}', available tokens '{1}'. + + + Cannot resolve KeyInfo for decryption: KeyInfo '{0}', available tokens '{1}'. + + + An empty value was found for the required base-64 attribute name '{0}', namespace '{1}'. + + + The security header element '{0}' with the '{1}' id must be signed. + + + The '{0}' security token with the '{1}' attachment mode must be signed. + + + The '{0}' security token with the '{1}' attachment mode must be encrypted. + + + Operation '{0}' is not valid in message body state '{1}'. + + + EncryptedKey with ReferenceList is not allowed according to the current settings. + + + Cannot find a token authenticator for the '{0}' token type. Tokens of that type cannot be accepted according to current security settings. + + + No signature was created because not part of the message matched the supplied message part specification. + + + Supporting SecurityToken cannot be written without encryption. + + + The '{0}' id occurred twice in the message that is supplied for verification. + + + Canonicalization algorithm '{0}' is not supported. + + + The KeyInfo value was not found in the encrypted item to find the decrypting token. + + + No KeyInfo in signature to find verification token. + + + Security header is empty. + + + The encryption method is missing in encrypted data. + + + The Encrypted Header and the Security Header '{0}' attribute did not match. Encrypted Header: {1}. Security Header: {2}. + + + At most one reference list is supported with default policy check. + + + At most one signature is supported with default policy check. + + + Unexpected encrypted element in security header. + + + Id is missing in encrypted item in security header. + + + The supplied token manager cannot create a token reference. + + + The timestamp element added to security header to sign has no id. + + + An encrypted header must have an id. + + + The data reference '{0}' could not be resolved in the received message. + + + A timestamp element has already been set for this security header. + + + More than one Timestamp element was present in security header. + + + The incoming message was signed with a token which was different from what used to encrypt the body. This was not expected. + + + Cannot create the '{0}' symmetric algorithm from the token. + + + Unrecognized encoding occurred while reading the binary security token. + + + Cannot resolve reference URI '{0}' in signature to compute digest. + + + No timestamp is available in the security header to do replay detection. + + + No signature is available in the security header to provide the nonce for replay detection. + + + There is no namespace binding for prefix '{0}' in scope. + + + Derived Key Token cannot derive key from the secret. + + + Both offset and generation cannot be specified for Derived Key Token. + + + Either offset or generation must be specified for Derived Key Token. + + + DerivedKeyToken requires a reference to a token. + + + DerivedKey length ({0}) exceeds the allowed settings ({1}). + + + The Implicit derived key clause '{0}' specifies a derivation key length ({1}) which exceeds the allowed maximum length ({2}). + + + The received derived key token has a invalid offset value specified. Value: {0}. The value should be greater than or equal to zero. + + + The received derived key token has a invalid generation value specified. Value: {0}. The value should be greater than or equal to zero. + + + The XML element {0} does not have a child of type {1}. + + + RequestedSecurityToken not specified in RequestSecurityTokenResponse. + + + Binary encoding {0} is not supported. + + + Invalid key encryption algorithm {0}. + + + The asynchronous result object used to end this operation was not the object that was returned when the operation was initiated. + + + Unable to create token reference. + + + null + + + The specified nonce is too short. The minimum required nonce length is 4 bytes. + + + There is no binary negotiation to send to the other party. + + + Security negotiation failure because an incorrect Context attribute specified in RequestSecurityToken/RequestSecurityTokenResponse from the other party. + + + No binary negotiation was received from the other party. + + + The proof token was not wrapped correctly in the RequestSecurityTokenResponse. + + + Final RSTR from other party does not contain a service token. + + + The Security Support Provider Interface (SSPI) negotiation failed. + + + Cannot authenticate the other party. + + + Incoming binary negotiation has invalid ValueType {0}. + + + The channel is not open. + + + Security negotiation failed because the remote party did not send back a reply in a timely manner. This may be because the underlying transport connection was aborted. + + + SecurityVersion must be WsSecurity10 or WsSecurity11. + + + Creation time must be before expiration time. + + + Negotiation state already exists for context '{0}'. + + + Cannot find the negotiation state for the context '{0}'. + + + Send cannot be called when the session does not expect output. + + + The session was closed before message transfer was complete. + + + The item cannot be added. The maximum cache size is ({0} items). + + + The server's X509SecurityTokenProvider cannot be null. + + + Expected binary secret of type {0} but got secret of type {1}. + + + The '{0}' username token has an unsupported password type. + + + Unrecognized identity property type: '{0}'. + + + There was no channel that could accept the message with action '{0}'. + + + There was no endpoint listening at {0} that could accept the message. This is often caused by an incorrect address or SOAP action. See InnerException, if present, for more details. + + + This factory buffers messages, so the message sizes must be in the range of an integer value. + + + For TransferMode.Buffered, MaxReceivedMessageSize and MaxBufferSize must be the same value. + + + MaxBufferSize must not exceed MaxReceivedMessageSize. + + + This Factory buffers messages, so the message sizes must be in the range of a int value. + + + URI {0} could not be set because its size ({1}) exceeds the max supported size ({2}). + + + Expecting first char - c - to be in set [Char.IsLetter(c) && c == '_', found '{0}'. + + + Expecting all chars - c - of id to be in set [Char.IsLetter(c), Char.IsNumber(c), '.', '_', '-'], found '{0}'. + + + HTTP could not register URL {0}. Another application has already registered this URL with HTTP.SYS. + + + HTTP could not register URL {0}. Your process does not have access rights to this namespace (see http://go.microsoft.com/fwlink/?LinkId=70353 for details). + + + HTTP could not register URL {0} because TCP port {1} is being used by another application. + + + HTTP could not register URL {0} because the MaxEndpoints quota has been exceeded. To correct this, either close other HTTP-based services, or increase your MaxEndpoints registry key setting (see http://go.microsoft.com/fwlink/?LinkId=70352 for details). + + + The remote server returned an unexpected response: ({0}) {1}. + + + The number of bytes available is inconsistent with the HTTP Content-Length header. There may have been a network error or the client may be sending invalid requests. + + + A response was received from a one-way send over the underlying IRequestChannel. Make sure the remote endpoint has a compatible binding at its endpoint (one that contains OneWayBindingElement). + + + The receiver returned an error indicating that the content type was missing on the request to {0}. See the inner exception for more information. + + + Duplex channel to {0} was aborted during the open process. + + + Operation was aborted while establishing a connection to {0}. + + + The incoming message contains a SOAP header representing the WS-Addressing '{0}', yet the HTTP transport is configured with AddressingVersion.None. As a result, the message is being dropped. If this is not desired, then update your HTTP binding to support a different AddressingVersion. + + + There is a problem with the XML that was received from the network. See inner exception for more details. + + + An IPv4 address was specified ({0}), but IPv4 is not enabled on this machine. + + + An IPv6 address was specified ({0}), but IPv6 is not enabled on this machine. + + + Cannot find a unique port number that is available for both IPv4 and IPv6. + + + There is already a listener on IP endpoint {0}. This could happen if there is another application already listening on this endpoint or if you have multiple service endpoints in your service host with the same IP endpoint but with incompatible binding configurations. + + + Insufficient winsock resources available to complete socket connection initiation. + + + Insufficient memory avaliable to complete the operation. + + + Could not connect to {0}. TCP error code {1}: {2}. + + + Could not connect to {0}. The connection attempt lasted for a time span of {3}. TCP error code {1}: {2}. + + + A TCP error ({0}: {1}) occurred while listening on IP Endpoint={2}. + + + A TCP error ({0}: {1}) occurred while transmitting data. + + + A TCP error ({0}: {1}) occurred while transmitting data. The local IP address and port is {2}. The remote IP address and port is {3}. + + + The socket connection was aborted by your local machine. This could be caused by a channel Abort(), or a transmission error from another thread using this socket. + + + The HTTP request context was aborted while writing the response. As a result, the response may not have been completely written to the network. This can be remedied by gracefully closing the request context rather than aborting it. + + + The socket connection was aborted. This could be caused by an error processing your message or a receive timeout being exceeded by the remote host, or an underlying network resource issue. Local socket timeout was '{0}'. + + + The socket connection was aborted. This could be caused by an error processing your message or a receive timeout being exceeded by the remote host, or an underlying network resource issue. Local socket timeout was '{0}'. The local IP address and port is {1}. The remote IP address and port is {2}. + + + The socket transfer timed out after {0}. You have exceeded the timeout set on your binding. The time allotted to this operation may have been a portion of a longer timeout. + + + The socket transfer timed out after {0}. You have exceeded the timeout set on your binding. The time allotted to this operation may have been a portion of a longer timeout. The local IP address and port is {1}. The remote IP address and port is {2}. + + + The socket connection has been disposed. + + + The socket listener has been disposed. + + + The socket listener is not listening. + + + No duplex session listener was listening at {0}. This could be due to an incorrect via set on the client or a binding mismatch. + + + The entry found in AuthenticationManager's CustomTargetNameDictionary for {0} does not match the requested identity of {1}. + + + An HTTP Content-Type header is required for SOAP messaging and none was found. + + + Content Type {0} was sent to a service expecting {1}. The client and service bindings may be mismatched. + + + The content type {0} of the response message does not match the content type of the binding ({1}). If using a custom encoder, be sure that the IsContentTypeSupported method is implemented properly. The first {2} bytes of the response were: '{3}'. + + + The content type {0} of the message is not supported by the encoder. + + + The binding specified requires that the to and via URIs must match because the Addressing Version is set to None. The to URI specified was '{0}'. The via URI specified was '{1}'. + + + The server challenged this request and streamed requests cannot be resubmitted. To enable HTTP server challenges, set your TransferMode to Buffered or StreamedResponse. + + + Content Type {0} was not supported by service {1}. The client and service bindings may be mismatched. + + + Server faulted with code '{0}'. + + + Content type '{0}' is too long to be processed by the remote host. See the server logs for more details. + + + Via '{0}' is too long to be processed by the remote host. See the server logs for more details. + + + The .Net Framing mode being used is not supported by '{0}'. See the server logs for more details. + + + The .Net Framing version being used is not supported by '{0}'. See the server logs for more details. + + + The requested upgrade is not supported by '{0}'. This could be due to mismatched bindings (for example security enabled on the client and not on the server). + + + Server '{0}' sent back a fault indicating it is too busy to process the request. Please retry later. Please see the inner exception for fault details. + + + Server '{0}' sent back a fault indicating it is in the process of shutting down. Please see the inner exception for fault details. + + + Server '{0}' is too busy to process this request. Try again later. + + + Protocol Type {0} was sent to a service that does not support that type of upgrade. + + + .Net Framing upgrade request for {0} was sent to a service that is not setup to receive upgrades. + + + You have tried to create a channel to a service that does not support .Net Framing. + + + You have tried to create a channel to a service that does not support .Net Framing. It is possible that you are encountering an HTTP endpoint. + + + An error occurred while transmitting data. + + + The server rejected the upgrade request. + + + The server at {0} rejected the session-establishment request. + + + Cannot resolve the host name of URI \"{0}\" using DNS. + + + The '{0}' authentication scheme has been specified on the HTTP factory. However, the factory only supports specification of exactly one authentication scheme. Valid authentication schemes are Digest, Negotiate, NTLM, Basic, or Anonymous. + + + The value specified for the AuthenticationScheme property on the HttpTransportBindingElement ('{0}') is not allowed when building a ChannelFactory. If you used a standard binding, ensure the ClientCredentialType is not set to HttpClientCredentialType.InheritedFromHost, a value which is invalid on a client. If you set the value to '{0}' directly on the HttpTransportBindingElement, please set it to Digest, Negotiate, NTLM, Basic, or Anonymous. + + + The '{0}' authentication scheme has been specified for the proxy on the HTTP factory. However, the factory only supports specification of exactly one authentication scheme. Valid authentication schemes are Digest, Negotiate, NTLM, Basic, or Anonymous. + + + The remote HTTP server did not satisfy the mutual authentication requirement. + + + The HTTP request is unauthorized with client authentication scheme '{0}'. The authentication header received from the server was '{1}'. + + + The HTTP request with client authentication scheme '{0}' failed with '{1}' status. + + + The HTTP request was forbidden with client authentication scheme '{0}'. + + + The provided URI scheme '{0}' is invalid; expected '{1}'. + + + The HTTPS listener factory was configured to require a client certificate and the '{0}' authentication scheme. However, only one form of client authentication can be required at once. + + + Could not find an appropriate transport manager for listen URI '{0}'. + + + The specified channel listener at '{0}' is not registered with this transport manager. + + + The HTTPS channel factory does not support explicit specification of an identity in the EndpointAddress unless the authentication scheme is NTLM or Negotiate. + + + The endpoint identity specified when creating the HTTPS channel to '{0}' contains multiple server certificates. However, the HTTPS transport only supports the specification of a single server certificate. In order to create an HTTPS channel, please specify no more than one server certificate in the endpoint identity. + + + The server certificate with name '{0}' failed identity verification because its thumbprint ('{1}') does not match the one specified in the endpoint identity ('{2}'). As a result, the current HTTPS request has failed. Please update the endpoint identity used on the client or the certificate used by the server. + + + A registration already exists for URI '{0}'. + + + Could not establish secure channel for SSL/TLS with authority '{0}'. + + + Could not establish trust relationship for the SSL/TLS secure channel with authority '{0}'. + + + Could not find a compatible transport manager for URI '{0}'. + + + The SPN for the responding server at URI '{0}' could not be determined. + + + The remote server did not satisfy the mutual authentication requirement. + + + Transfer mode {0} is not supported by {1}. + + + The token provider of type '{0}' did not return a token of type '{1}'. Check the credential configuration. + + + The required UserNameSecurityToken was not provided. + + + The following remote identity failed verification: '{0}'. + + + You cannot specify an explicit Proxy Address as well as UseDefaultWebProxy=true in your HTTP Transport Binding Element. + + + The HTTP proxy authentication credential specified an impersonation level restriction ({0}) that is stricter than the restriction for target server authentication ({1}). + + + The HTTP proxy authentication credential specified an mutual authentication requirement ({0}) that is stricter than the requirement for target server authentication ({1}). + + + The NTLM authentication scheme was specified, but the target credential does not allow NTLM. + + + The impersonation level '{0}' was specified, yet HTTP Digest authentication can only support 'Impersonation' level when used with an explicit credential. + + + The scheme parameter must not be empty. + + + The protection level '{0}' was specified, yet SSL transport security only supports EncryptAndSign. + + + {0}. This often indicates that a service that HTTP.SYS depends upon (such as httpfilter) is not started. + + + {0}. This often indicates that the HTTP client has prematurely closed the underlying TCP connection. + + + Opening the channel timed out after {0}. The time allotted to this operation may have been a portion of a longer timeout. + + + Opening the {0} channel timed out after {1}. The time allotted to this operation may have been a portion of a longer timeout. + + + TimeSpan must be greater than TimeSpan.Zero. + + + TimeSpan cannot be less than TimeSpan.Zero. + + + The value of this argument must be non-negative. + + + The value of this argument must be positive. + + + The value of this argument must be greater than 0. + + + The value of this argument must fall within the range {0} to {1}. + + + The specified offset exceeds the upper bound of the buffer ({0}). + + + The specified offset exceeds the buffer size ({0} bytes). + + + The specified size exceeds the remaining buffer space ({0} bytes). + + + The space needed for encoding ({0} bytes) exceeds the message frame offset. + + + {0} returned true from OnTryCreateFaultMessage, but did not return a fault message. + + + {0} returned false from OnTryCreateFaultMessage, but returned a non-null fault message. + + + {0} returned true from OnTryCreateException, but did not return an Exception. + + + {0} returned false from OnTryCreateException, but returned a non-null Exception (See InnerException for details). + + + Policy chain contains self issued URI or a managed issuer in the wrong position. + + + The Binding with name {0} failed validation because it contains a BindingElement with type {1} which is not supported in partial trust. Consider using BasicHttpBinding or WSHttpBinding, or hosting your application in a full-trust environment. + + + The WSHttpBinding with name {0} failed validation because it contains a BindingElement with type {1} which is not supported in partial trust. Consider disabling the message security and reliable session options, using BasicHttpBinding, or hosting your application in a full-trust environment. + + + The Binding with name {0} failed validation because the Binding type {1} is not supported in partial trust. Consider using BasicHttpBinding or WSHttpBinding, or hosting your application in a full-trust environment. + + + The Service with name '{0}' could not be constructed because the application does not have permission to construct the type: both the Type and its default parameter-less constructor must be public. + + + The Method with name '{1}' in Type '{0}' could not be invoked because the application does not have permission to invoke the method: both the Method and its containing Type must be public. + + + Access to performance counters is denied. Application may be running in partial trust. Either disable performance counters or configure the application to run in full trust. + + + Performance counter instance names may not be unique. See "http://go.microsoft.com/fwlink/?LinkId=524462" for details. + + + Access to windows management instrumentation (WMI) is denied. Application may be running in partial trust. Either disable WMI or configure the application to run in full trust. + + + Unable to log messages. Application may be running in partial trust. Either disable message logging or configure the application to run in full trust. + + + The 'scopeName' argument to the InstanceKey constructor must be a non-empty string which indicates the scope of uniqueness for the key. Durable services use the service namespace and name as the scope of uniqueness. + + + The 'provider' argument to the InstanceKey constructor must be a non-empty string which identifies the source of the key data. The 'provider' argument can be null, in which case the default correlation provider name is used. + + + The 'Name' property cannot be set on an invalid InstanceKey. + + + The type {0} is not a supported result type. + + + The result cannot be represented as a nodeset. Only results of type XPathResultType.NodeSet can be represented as nodesets. + + + Message with id {0} was not in a locked state. + + + Validity of message with id {0} has expired. + + + The StreamUpgradeInitiator specified ({0}) is not supported by this IStreamUpgradeChannelBindingProvider implementation. The most likely cause of this is passing a StreamUpgradeInitiator that was not created by the StreamUpgradeProvider associated with the current IStreamUpgradeChannelBindingProvider implementation. + + + The StreamUpgradeAcceptor specified ({0}) is not supported by this IStreamUpgradeChannelBindingProvider implementation. The most likely cause of this is passing a StreamUpgradeAcceptor that was not created by the StreamUpgradeProvider associated with this IStreamUpgradeChannelBindingProvider implementation. + + + The StreamUpgradeProvider {0} does not support the specified ChannelBindingKind ({1}). + + + Extended protection is not supported on this platform. Please install the appropriate patch or change the ExtendedProtectionPolicy on the Binding or BindingElement to a value with a PolicyEnforcement value of "Never" or "WhenSupported". + + + The Authentication Scheme "Basic" does not support Extended Protection. Please use a different authentication scheme or disable the ExtendedProtectionPolicy on the Binding or BindingElement by creating a new ExtendedProtectionPolicy with a PolicyEnforcement value of "Never". + + + CustomChannelBindings are not supported. Please remove the CustomChannelBinding from the ExtendedProtectionPolicy". + + + ClientCredentialType '{0}' can only be used on the server side, not the client side. Please use one of the following values instead 'None, Basic, Client, Digest, Ntlm, Windows'. + + + When authentication schemes 'Basic' and also '{0}' are enabled, the value of IncludeWindowsGroups for Windows ('{1}') and UserName authentication ('{2}') must match. Please consider using the same value in both places. + + + The authentication schemes cannot be inherited from the host for binding '{0}'. No AuthenticationScheme was specified on the ServiceHost or in the virtual application in IIS. This may be resolved by enabling at least one authentication scheme for this virtual application in IIS, through the ServiceHost.Authentication.AuthenticationSchemes property or in the configuration at the <serviceAuthenticationManager> element. + + + The authentication schemes configured on the host ('{0}') do not allow those configured on the binding '{1}' ('{2}'). Please ensure that the SecurityMode is set to Transport or TransportCredentialOnly. Additionally, this may be resolved by changing the authentication schemes for this application through the IIS management tool, through the ServiceHost.Authentication.AuthenticationSchemes property, in the application configuration file at the <serviceAuthenticationManager> element, by updating the ClientCredentialType property on the binding, or by adjusting the AuthenticationScheme property on the HttpTransportBindingElement. + + + Object type must be an enum with the flag attribute. '{0}' is not an enum - or the flag attribute is not set. Please use an enum type with the flag attribute instead. + + + Object type must be an enum with the flag attribute and may only contain powers of two for the flags enum values or a combination of such values. Please use an enum type according to these rules. + + + There is no pending asynchronous write on this stream. Ensure that there is pending write on the stream or verify that the implementation does not try to complete the same operation multiple times. + + + Cannot write to a buffer which is currently being flushed. + + + An asynchronous write was called on the stream without a free buffer. + + + The transport configured on this binding does not appear to support the CompressionFormat specified ({0}) on the message encoder. To resolve this issue, set the CompressionFormat on the {1} to '{2}' or use a different transport. + + + The value '{1}' is not supported in this context for the binding security property '{0}'. + + + The value '{1}' is not supported in this context for the binding property '{0}'. + + + The value of MaxPendingAccepts should not be larger than {0}. + + + The initialization process of the request message timed out after {0}. To increase this quota, use the '{1}' property on the '{2}'. + + + The value '{1}' for the '{0}' property is not supported in Windows Store apps. + + + The remote endpoint requested an address for acknowledgements that is not the same as the address for application messages. The channel could not be opened because this is not supported. Ensure the endpoint address used to create the channel is identical to the one the remote endpoint was set up with. + + + The address for acknowledgements must be the same as the address for application messages. Verify that your endpoint is configured to use the same URI for these two addresses. + + + The {0}:{1} assertion is not supported. + + + An unexpected error occurred while attempting to close the output half of the duplex reliable session. + + + The remote endpoint sent conflicting requests to create a reliable session. The conflicting requests have inconsistent filter criteria such as address or action. The reliable session has been faulted. + + + The remote endpoint sent conflicting requests to create a reliable session. The remote endpoint requested both a one way and a two way session. The reliable session has been faulted. + + + A message with action {0} could not be parsed. + + + The request to create a reliable session has been refused by the RM Destination. {0} The channel could not be opened. + + + The endpoint processing requests to create a reliable session only supports sessions in which the AcksTo Uri and the Endpoint Uri are the same. + + + The endpoint processing requests to create a reliable session only supports sessions in which the AcksTo Uri and the ReplyTo Uri are the same. + + + The endpoint at {0} processes duplex sessions. The create sequence request must contain an offer for a return sequence. This is likely caused by a binding mismatch. + + + The endpoint at {0} processes input sessions. The create sequence request must not contain an offer for a return sequence. This is likely caused by a binding mismatch. + + + The request to create a reliable session contains an invalid wsrm:IncompleteSequenceBehavior value. This is a WS-ReliableMessaging protocol violation. + + + The request to create a reliable session contains the wsse:SecurityTokenReference but does not carry a wsrm:UsesSequenceSTR header. This is a WS-ReliableMessaging protocol violation. The session could not be created. + + + The endpoint at {0} processes reply sessions. The create sequence request must contain an offer for a return sequence. This is likely caused by a binding mismatch. + + + The RM Destination requires the WS-SecureConversation protocol in the binding. This is likely caused by a binding mismatch. + + + The endpoint processing requests to create a reliable session does not support sessions that use SSL. This is likely caused by a binding mismatch. The session could not be created. + + + The request to create a reliable session carries a wsrm:UsesSequenceSTR header, but does not contain the wsse:SecurityTokenReference. This is a WS-ReliableMessaging protocol violation. The session could not be created. + + + The message is not a valid SOAP message. The body contains more than 1 root element. + + + The remote endpoint replied to a request for a two way session with an offer for a one way session. This is likely caused by a binding mismatch. The channel could not be opened. + + + The client requested creation of a two way session. A one way session was created. The session cannot continue without as a one way session. This is likely caused by a binding mismatch. + + + The response to the request to create a reliable session contains an invalid wsrm:IncompleteSequenceBehavior value. This is a WS-ReliableMessaging protocol violation. + + + The remote endpoint replied to a request for a one way session with an offer for a two way session. This is a WS-ReliableMessaging protocol violation. The channel could not be opened. + + + A return sequence was not offered by the create sequence request. The create sequence response cannot accept a return sequence. + + + The remote endpoint replied to a request for a two way session with an offer for a one way session. This is a WS-ReliableMessaging protocol violation. The channel could not be opened. + + + A return sequence was offered by the create sequence request but the create sequence response did not accept this sequence. + + + The WS-RM policy under the namespace {0} requires the wsrmp:ExactlyOnce, wsrmp:AtLeastOnce, or wsrmp:AtMostOnce assertion. Nothing was found. + + + The WS-RM policy under the namespace {0} requires the wsrmp:ExactlyOnce, wsrmp:AtLeastOnce, or wsrmp:AtMostOnce assertion. The {1} element under the {2} namespace was found. + + + The remote endpoint sent a TerminateSequence protocol message before fully acknowledging all messages in the reply sequence. This is a violation of the reliable request reply protocol. The reliable session was faulted. + + + The remote endpoint has closed the underlying secure session before the reliable session fully completed. The reliable session was faulted. + + + The underlying secure session has faulted before the reliable session fully completed. The reliable session was faulted. + + + The remote endpoint has errantly sent a TerminateSequence protocol message before the sequence finished. + + + The {0}:{1} element requires a {2}:{3} child element but has the {4} child element under the {5} namespace. + + + The {0}:{1} element requires a {2}:{3} child element but has no child elements. + + + The remote endpoint specified two different last message numbers. The reliable session is in an inconsistent state since it cannot determine the actual last message. The reliable session was faulted. + + + The SequenceAcknowledgement violates the cumulative acknowledgement invariant. + + + A violation of acknowledgement protocol has been detected. An InvalidAcknowledgement fault was sent to the remote endpoint and the reliable session was faulted. + + + An acknowledgement was received indicating the remaining buffer space on the remote endpoint is {0}. This number cannot be less than zero. The reliable session was faulted. + + + A message was received with a sequence number of {0}. Sequence numbers cannot be less than 1. The reliable session was faulted. + + + An acknowledgement range starting at {0} and ending at {1} was received. This is an invalid acknowledgement range. The reliable session was faulted. + + + The remote endpoint responded to the {0} request with a response with action {1}. The response must be a {0}Response with action {2}. The channel could not be opened. + + + The remote endpoint responded to the {0} request with a response with action {1}. The response must be a {0}Response with action {2}. The channel was faulted. + + + The {0} request's response was a message with action {1}. The response must be a {0}Response with action {2}. The reliable session cannot continue. + + + A message was received with a sequence number higher than the sequence number of the last message in this sequence. This is a violation of the sequence number protocol. The reliable session was faulted. + + + The value for wsrm:MessageNumber exceeds the value of the MessageNumber accompanying a LastMessage element in this Sequence. + + + Binding validation failed because the TransportBindingElement's ManualAddressing property was set to true on a binding that is configured to create reliable sessions. This combination is not supported and the channel factory or service host was not opened. + + + The maximum retry count has been exceeded with no response from the remote endpoint. The reliable session was faulted. This is often an indication that the remote endpoint is no longer available. + + + A problem occurred while reading a message. See inner exception for details. + + + The maximum message number for this sequence has been exceeded. The reliable session was faulted. + + + The maximum value for wsrm:MessageNumber has been exceeded. + + + The {0} assertion's Milliseconds attribute does not fall within the range this binding uses. The ReliableSessionBindingElement could not be created. + + + The remote endpoint did not include a final acknowledgement in the reply to the close sequence request message. This is a violation of the WS-ReliableMessaging protocol. The reliable session was faulted. + + + The wsa:MessageId header must be present on a wsrm:{0} message. + + + The returned wsrm:{0}Response message was missing the required wsa:RelatesTo header. This is a violation of the WS-Addressing request reply protocol. The reliable session was faulted. + + + The wsa:ReplyTo header must be present on a wsrm:{0} message. + + + More than one version of the {0} assertion was found. The ReliableSessionBindingElement could not be created. + + + The endpoint only processes messages using the WS-ReliableMessaging protocol. The message sent to the endpoint does not have an action or any headers used by the protocol and cannot be processed. + + + A message with action {0} is an empty message. This message cannot be processed because the body of this WS-ReliableMessaging protocol message must carry information pertaining to a reliable session. + + + The action {0} is not supported by this endpoint. Only WS-ReliableMessaging February 2005 messages are processed by this endpoint. + + + The remote endpoint closed the session before acknowledging all responses. All replies could not be delivered. The reliable session was faulted. + + + The remote endpoint returned a {0}Response when the {0} request had not been sent. This is a WS-ReliableMessaging protocol violation. The reliable session was faulted. + + + The {0}Response was received when the {0} request had not been sent. This is a WS-ReliableMessaging protocol violation. The reliable session cannot continue. + + + The remote endpoint failed to include a required SequenceAcknowledgement header on a reliable reply message. The reliable session was faulted. + + + Due to a request context abort call, the reliable reply session channel potentially has a gap in its reply sequence. The ExactlyOnce assurance can no longer be satisfied. The reliable session was faulted. + + + The required {0} attribute is missing from the {1} element in the {2} assertion. The ReliableSessionBindingElement could not be created. + + + The {0} assertion's required Milliseconds attribute is not schema compliant. Milliseconds must be convertible to an unsigned long. The ReliableSessionBindingElement could not be created. + + + The endpoint at {0} has stopped accepting wsrm sessions. + + + The Sequence is closed and cannot accept new messages. + + + The RM Source could not transfer the last message within the timeout the user specified. + + + The server received a TerminateSequence message before all reply sequence messages were acknowledged. This is a violation of the reply sequence acknowledgement protocol. + + + The wsrm:TerminateSequence protocol message was transmitted before the sequence was successfully completed. + + + The inactivity timeout of ({0}) has been exceeded. + + + Two different wsrm:LastMsgNumber values were specified. Because of this the reliable session cannot complete. + + + The user specified maximum retry count for a particular message has been exceeded. Because of this the reliable session cannot continue. + + + The CloseSequence request's reply message must carry a final acknowledgement. This is a violation of the WS-ReliableMessaging protocol. The reliable session cannot continue. + + + Due to a user abort the reliable session cannot continue. + + + The necessary size to buffer a sequence message has exceeded the configured buffer quota. Because of this the reliable session cannot continue. + + + The session has stopped waiting for a particular reply. Because of this the reliable session cannot continue. + + + A reply message was received with no acknowledgement. + + + All of the reply sequence's messages must be acknowledged prior to closing the request sequence. This is a violation of the reply sequence's delivery guarantee. The session cannot continue. + + + The user of the remote endpoint's reliable session expects no more messages and a new message arrived. Due to this the reliable session cannot continue. + + + The wsrm:LastMsgNumber value is too small. A message with a larger sequence number has already been received. + + + The RM destination received an acknowledgement message. The RM destination does not process acknowledgement messages. + + + The RM source received an AckRequested message. The RM source does not process AckRequested messages. + + + The RM source received an CloseSequence message. The RM source does not process CloseSequence messages. + + + The RM destination received an CloseSequenceResponse message. The RM destination does not process CloseSequenceResponse messages. + + + The RM source received a CreateSequence request. The RM source does not process CreateSequence requests. + + + The RM destination received multiple CreateSequence requests with different OfferId values over the same session. + + + The RM destination received a CreateSequenceResponse message. The RM destination does not process CreateSequenceResponse messages. + + + The RM source received multiple CreateSequenceResponse messages with different sequence identifiers over the same session. + + + The RM source received a TerminateSequence message. The RM source does not process TerminateSequence messages. + + + The RM destination received a TerminateSequenceResponse message. The RM destination does not process TerminateSequenceResponse messages. + + + The RM source does not support an RM destination initiated close since messages can be lost. The reliable session cannot continue. + + + The RM source does not support an RM destination initiated termination since messages can be lost. The reliable session cannot continue. + + + An unknown error occurred while trying to add a sequence message to the window. + + + The remote endpoint specified a last message number that is smaller than a sequence number that has already been seen. The reliable session is in an inconsistent state since it cannot determine the actual last message. The reliable session was faulted. + + + The message could not be transferred within the allotted timeout of {0}. There was no space available in the reliable channel's transfer window. The time allotted to this operation may have been a portion of a longer timeout. + + + The close operation did not complete within the allotted timeout of {0}. The time allotted to this operation may have been a portion of a longer timeout. + + + The open operation did not complete within the allotted timeout of {0}. The time allotted to this operation may have been a portion of a longer timeout. + + + The operation did not complete within the allotted timeout of {0}. The time allotted to this operation may have been a portion of a longer timeout. + + + The request operation did not complete within the allotted timeout of {0}. The time allotted to this operation may have been a portion of a longer timeout. + + + The send operation did not complete within the allotted timeout of {0}. The time allotted to this operation may have been a portion of a longer timeout. + + + The remote endpoint sent an unexpected ack. Simplex servers do not process acks. + + + The remote endpoint sent an unexpected request for an ack. Simplex clients do not send acks and do not process requests for acks. + + + The remote endpoint sent an unexpected close sequence message. Simplex clients do not process this message. + + + The remote endpoint sent an unexpected close sequence response message. Simplex servers do not process this message. + + + The remote endpoint sent an unexpected request to create a sequence. Clients do not process requests for a sequence. + + + The remote endpoint sent an unexpected create sequence response. Servers do not process this message. + + + The remote endpoint sent inconsistent requests to create the same sequence. The OfferId values are not identical. + + + The remote endpoint sent inconsistent responses to the same create sequence request. The sequence identifiers are not identical. + + + The remote endpoint sent an unexpected terminate sequence message. Simplex clients do not process this message. + + + The remote endpoint sent an unexpected terminate sequence response message. Simplex servers do not process this message. + + + The remote endpoint replied to the request for a sequence with a response that could not be parsed. See inner exception for details. The channel could not be opened. + + + The value of wsrm:Identifier is not a known Sequence identifier. + + + The remote endpoint no longer recognizes this sequence. This is most likely due to an abort on the remote endpoint. {0} The reliable session was faulted. + + + The remote endpoint has sent a message containing an unrecognized sequence identifier. The reliable session was faulted. + + + The remote endpoint has sent an unrecognized fault with namespace, {0}, name {1}, and reason {2}. The reliable session was faulted. + + + The remote endpoint has sent an unrecognized fault with namespace, {0}, name {1}, and reason {2}. The channel could not be opened. + + + The remote endpoint closed the sequence before message transfer was complete. This is not supported since all messages could not be transferred. The reliable session was faulted. + + + The remote endpoint terminated the sequence before message transfer was complete. This is not supported since all messages could not be transferred. The reliable session was faulted. + + + The remote endpoint has sent an fault message with an unexpected sequence identifier over a session. The fault may be intended for a different session. The fault reason is: {0} The reliable session was faulted. + + + Binding validation failed because the WSHttpBinding does not support reliable sessions over transport security (HTTPS). The channel factory or service host could not be opened. Use message security for secure reliable messaging over HTTP. + + + The sequence has been terminated by the remote endpoint. {0} The reliable session was faulted. + + + An error occurred while processing a message. {0} + + + The returned {0}Response was carrying the a wsa:RelatesTo header that does not correlate with the wsa:MessageId header on the {0} request. This is a violation of the WS-Addressing request reply protocol. The reliable session cannot continue. + + + The remote endpoint has responded to a {0} request message with an invalid reply. The reply has a wsa:RelatesTo header with an unexpected identifier. The reliable session cannot continue. + + + The remote endpoint sent a wsrm:{0} request message with a wsa:ReplyTo address containing a URI which is not equivalent to the remote address. This is not supported. The reliable session was faulted. + + + The wsrm:{0} request message's wsa:ReplyTo address containing a URI which is not equivalent to the remote address. This is not supported. The reliable session was faulted. + + + The incoming message is not a WS-ReliableMessaging 1.1 message and could not be processed. + + + The RM server requires the use of WS-ReliableMessaging 1.1 protocol. This is likely caused by a binding mismatch. + + + The operations {0} and {1} have the same action ({2}). Every operation must have a unique action value. + + + Cannot create a typed message due to action mismatch, expecting {0} encountered {1} + + + Part {1} in message {0} cannot be exported with RPC or encoded since its type is anonymous. + + + The IAsyncResult returned from Begin and the IAsyncResult supplied to the Callback are on different objects. These are required to be the same object. + + + Binding name cannot be null or empty. + + + Value '{0}' provided for {1} property is an invalid URI. + + + Parameter value '{0}' is an invalid URI. + + + Header name cannot be null or empty. + + + Could not find a base address that matches scheme {0} for the endpoint with binding {1}. Registered base address schemes are [{2}]. + + + The scheme '{0}' used by binding {1} does not match the required scheme '{2}'. + + + Only a '{0}' using '{1}' or '{2}' is supported in this scenario. + + + MessageVersion '{0}' is not supported in this scenario. Only MessageVersion '{1}' is supported. + + + The binding associated with ServiceMetadataBehavior or ServiceDebugBehavior is not supported. The inner binding elements used by this binding must support IReplyChannel. Verify that HttpGetBinding/HttpsGetBinding (on ServiceMetadataBehavior) and HttpHelpPageBinding/HttpsHelpPageBinding (on ServiceDebugBehavior) are supported. + + + Method '{0}' in class '{1}' has bad parameter metadata: a pass-by-reference parameter is marked with the 'in' but not the 'out' parameter mode. + + + Method '{0}' in class '{1}' has bad parameter metadata: a pass-by-value parameter is marked with the 'out' parameter mode. + + + When calling the CreateFromPolicy method, the policy argument must be an XmlElement instance with LocalName '{1}' and NamespaceUri '{0}'. This XmlElement has LocalName '{3}' and NamespaceUri '{2}'. + + + The URI supplied to ServiceMetadataBehavior via the ExternalMetadataLocation property or the externalMetadataLocation attribute in the serviceMetadata section in config must be a relative URI or an absolute URI with an http or https scheme. '{0}' was specified, which is a absolute URI with {1} scheme. + + + The URL supplied to ServiceMetadataBehavior via the ExternalMetadataLocation property or the externalMetadataLocation attribute in the serviceMetadata section in config was a relative URL and there is no base address with which to resolve it. '{0}' was specified. + + + There was a problem reading the MetadataSet argument: a MetadataSection instance with identifier '{0}' and dialect '{1}' has a Metadata property whose type does not match the dialect. The expected Metadata type for this dialect is '{2}' but was found to be '{3}'. + + + Metadata contains a reference that cannot be resolved: '{0}'. + + + The MaximumResolvedReferences property of MetadataExchangeClient must be greater than or equal to one. '{0}' was specified. + + + The MetadataExchangeClient was not supplied with a MetadataReference or MetadataLocation from which to get metadata. You must supply one to the constructor, to the GetMetadata method, or to the BeginGetMetadata method. + + + The MetadataExchangeClient could not create an IChannelFactory for: address='{0}', dialect='{1}', and identifier='{2}'. + + + The MetadataExchangeClient could not create an HttpWebRequest for: address='{0}', dialect='{1}', and identifier='{2}'. + + + The MetadataExchangeClient instance could not be initialized because no Binding is available for scheme '{0}'. You can supply a Binding in the constructor, or specify a configurationName. + + + The TransactionProtocol setting was not understood. A supported protocol must be specified. + + + The MetadataResolver cannot recieve an empty contracts argument to the Resolve or BeginResolve methods. You must supply at least one ContractDescription. + + + The ContractDescriptions in contracts must all have unique Name and Namespace pairs. More than one ContractDescription had the pair Name='{0}' and Namespace='{1}'. + + + The contracts argument to the Resolve or BeginResolve methods cannot contain a null ContractDescription. + + + The binding specified to do metadata exchange does not contain a TransportBindingElement. + + + The binding (Name={0}, Namespace={1}) does not contain a TransportBindingElement. + + + Body object cannot be null in message {0} + + + Type {0} cannot inherit from any class other than object to be used as body object in RPC style. + + + Type {0} implements interface {1} which is not supported for body object in RPC style. + + + CallbackBehaviorAttribute can only be run as a behavior on an endpoint with a duplex contract. Contract '{0}' is not duplex, as it contains no callback operations. + + + This operation would deadlock because the reply cannot be received until the current Message completes processing. If you want to allow out-of-order message processing, specify ConcurrencyMode of Reentrant or Multiple on {0}. + + + In order to use the contract '{0}' with DuplexChannelFactory, the contract must specify a valid callback contract. If your contract does not have a callback contract, consider using ChannelFactory instead of DuplexChannelFactory. + + + The dispatch instance for duplex callbacks cannot be activated - you must provide an instance. + + + ServiceHostBase's AddBaseAddress method cannot be called after the InitializeDescription method has completed. + + + Cannot make a call on this channel because a call to Open() is in progress. + + + The MetadataExchangeClient can only get metadata from absolute addresses. It cannot get metadata from '{0}'. + + + The MetadataExchangeClient can only get metadata from http or https addresses when using MetadataExchangeClientMode HttpGet. It cannot get metadata from '{0}'. + + + The MetadataExchangeClient can only get metadata from http and https MetadataLocations. It cannot get metadata from '{0}'. + + + The configured policy specifies more than one TransactionProtocol across the operations. A single TransactionProtocol for each endpoint must be specified. + + + Generating message contract since the operation {0} is neither RPC nor document wrapped. + + + Generating message contract since the wrapper namespace ({1}) of message {0} does not match the default value ({2}) + + + Generating message contract since the wrapper name ({1}) of message {0} does not match the default value ({2}) + + + Generating message contract since element name {0} from namespace {1} is not marked nillable + + + Generating message contract since message {0} requires protection. + + + Headers are not supported in RPC encoded format. Headers are ignored in message {0}. + + + Generating message contract since message {0} has headers + + + Generating message contract since the operation {0} has untyped Message as argument or return type + + + Generating message contract since message part namespace ({0}) does not match the default value ({1}) + + + There are two contracts listening on the same binding ({2}) and address with conflicting settings. Specifically, the contract '{0}' specifies SessionMode.NotAllowed while the contract '{1}' specifies SessionMode.Required. You should either change one of the SessionMode values or specify a different address (or ListenUri) for each endpoint. + + + This collection does not support setting extensions by index. Please consider using the InsertItem or RemoveItem methods. + + + This ChannelDispatcher is not currently attached to the provided ServiceHost. + + + Cannot add a ChannelDispatcher to more than one ServiceHost. + + + Cannot open ChannelDispatcher because it is not attached to a ServiceHost. + + + Cannot open ChannelDispatcher because it is does not have a MessageVersion set. + + + The ChannelDispatcher at '{0}' is unable to open its IChannelListener as there are no endpoints for the ChannelDispatcher. + + + The ChannelDispatcher at '{0}' with contract(s) '{1}' is unable to open its IChannelListener. + + + The type argument passed to the generic ChannelFactory class must be an interface type. + + + ApplyConfiguration requires that the Endpoint property be initialized. Either provide a valid ServiceEndpoint in the CreateDescription method or override the ApplyConfiguration method to provide an alternative implementation. + + + CreateFactory requires that the Endpoint property be initialized. Either provide a valid ServiceEndpoint in the CreateDescription method or override the CreateFactory method to provide an alternative implementation. + + + An operation marked as IsTerminating has already been invoked on this channel, causing the channel's connection to terminate. No more operations may be invoked on this channel. Please re-create the channel to continue communication. + + + This channel can no longer be used to send messages as the output session was auto-closed due to a server-initiated shutdown. Either disable auto-close by setting the DispatchRuntime.AutomaticInputSessionShutdown to false, or consider modifying the shutdown protocol with the remote server. + + + Array of type {0} is not supported. + + + Can only store into ArgBuilder or LocalBuilder. Got: {0}. + + + Expecting End {0}. + + + {0} is not assignable from {1}. + + + No conversion possible to {0}. + + + CODEGEN: {0} + + + Internal Error: Unrecognized constant type {0}. + + + This collection does not support setting items by index. + + + This operation is not supported because the collection is read-only. + + + The collection of type {0} does not support values of type {1}. + + + Top level XML element with name {0} in namespace {1} cannot reference {2} type because it already references a different type ({3}). Use a different operation name or MessageBodyMemberAttribute to specify a different name for the Message or Message parts. + + + Duplicate top level XML Schema type with name {0} in namespace {1}. + + + The value of OperationContext.Current is not the OperationContext value installed by this OperationContextScope. + + + ContractDescription's Name must be a non-empty string. + + + ContractDescription '{0}' has zero operations; a contract must have at least one operation. + + + ContractDescription '{0}' has zero IsInitiating=true operations; a contract must have at least one IsInitiating=true operation. + + + The service class of type {0} both defines a ServiceContract and inherits a ServiceContract from type {1}. Contract inheritance can only be used among interface types. If a class is marked with ServiceContractAttribute, it must be the only type in the hierarchy with ServiceContractAttribute. Consider moving the ServiceContractAttribute on type {1} to a separate interface that type {1} implements. + + + The service class of type {0} both defines a ServiceContract and inherits a ServiceContract from type {1}. Contract inheritance can only be used among interface types. If a class is marked with ServiceContractAttribute, then another service class cannot derive from it. + + + SynchronizedReadOnlyCollection's CopyTo only works if the underlying list implements ICollection. + + + The callback contract of contract {0} either does not exist or does not define any operations. If this is not a duplex contract, consider using ChannelFactory instead of DuplexChannelFactory. + + + This CreateChannel overload cannot be called on this instance of DuplexChannelFactory, as the DuplexChannelFactory was not initialized with an InstanceContext. Please call the CreateChannel overload that takes an InstanceContext. + + + This CreateChannel overload cannot be called on this instance of DuplexChannelFactory, as the DuplexChannelFactory was initialized with a Type and no valid InstanceContext was provided. Please call the CreateChannel overload that takes an InstanceContext. + + + This CreateChannel overload cannot be called on this instance of DuplexChannelFactory, as the InstanceContext provided to the DuplexChannelFactory does not contain a valid UserObject. + + + The InstanceContext provided to the ChannelFactory contains a UserObject that does not implement the CallbackContractType '{0}'. + + + ChannelFactory does not support the contract {0} as it defines a callback contract with one or more operations. Please consider using DuplexChannelFactory instead of ChannelFactory. + + + The CustomBinding on the ServiceEndpoint with contract '{0}' lacks a TransportBindingElement. Every binding must have at least one binding element that derives from TransportBindingElement. + + + The Scheme cannot be computed for this binding because this CustomBinding lacks a TransportBindingElement. Every binding must have at least one binding element that derives from TransportBindingElement. + + + The formatter threw an exception while trying to deserialize the message: {0} + + + This operation is not possible since the dictionary is empty. + + + The type or member named '{0}' could not be loaded because it has two incompatible attributes: '{1}' and '{2}'. To fix the problem, remove one of the attributes from the type or member. + + + The endpoint's address is not specified. + + + The endpoint's contract is not specified. + + + The endpoint's binding is not specified. + + + The channel is configured to use interactive initializer '{0}', but the channel was Opened without calling DisplayInitializationUI. Call DisplayInitializationUI before calling Open or other methods on this channel. + + + AllowInitializationUI was set to false for this channel, but the channel is configured to use the '{0}' as an interactive initializer. + + + This is a Windows&#169; Communication Foundation service.<BR/><BR/><B>Metadata publishing for this service is currently disabled.</B><BR/><BR/>If you have access to the service, you can enable metadata publishing by completing the following steps to modify your web or application configuration file:<BR/><BR/>1. Create the following service behavior configuration, or add the &lt;serviceMetadata&gt; element to an existing service behavior configuration: + + + 2. Add the behavior configuration to the service: + + + Note: the service name must match the configuration name for the service implementation.<BR/><BR/>3. Add the following endpoint to your service configuration: + + + Note: your service must have an http base address to add this endpoint.<BR/><BR/>The following is an example service configuration file with metadata publishing enabled: + + + For more information on publishing metadata please see the following documentation: <a href="http://go.microsoft.com/fwlink/?LinkId=65455">http://go.microsoft.com/fwlink/?LinkId=65455</a>. + + + Note: the service name must match the configuration name for the service implementation. + + + Add the following endpoint. + + + Note: your service must have an http base address to add this endpoint. + + + Add the following element to your service behavior configuration. + + + <P class='intro'><B>C#</B></P> + + + <P class='intro'><B>Visual Basic</B></P> + + + Service + + + {0} Service + + + You have created a service.<P class='intro'>To test this service, you will need to create a client and use it to call the service. You can do this using the svcutil.exe tool from the command line with the following syntax:</P> + + + You have created a service.<P class='intro'>To test this service, you will need to create a client and use it to call the service; however, metadata publishing via ?WSDL is currently disabled. This can be enabled via the service's configuration file. </P> + + + This will generate a configuration file and a code file that contains the client class. Add the two files to your client application and use the generated client class to call the Service. For example:<BR/> + + + Use the 'client' variable to call operations on the service. + + + Always close the client. + + + The service encountered an error. + + + Operation '{0}' could not be loaded as it uses an unsupported combination of Use and Style settings: Document with Encoded. To fix the problem, change the Use setting to Literal or change the Style setting to Rpc. + + + Fault could not be loaded as the Use setting is Encoded and it references a schema definition using Element attribute. To fix the problem, change the Use setting to Literal. + + + Message part {0} in namespace {1} appears more than once in Message. + + + This service has multiple endpoints listening at '{0}' which share the same initiating action '{1}'. As a result, messages with this action would be dropped since the dispatcher would not be able to determine the correct endpoint for handling the message. Please consider hosting these Endpoints at separate ListenUris. + + + The IEndpointBehavior '{0}' cannot be used on the server side; this behavior can only be applied to clients. + + + Cannot add EndpointDispatcher to more than one ChannelDispatcher. + + + This EndpointDispatcher is not currently attached to the provided ChannelDispatcher. + + + Error creating a reader for the MTOM message + + + Error in deserializing body of request message for operation '{0}'. + + + Error in deserializing body of request message for operation '{0}'. {1} + + + Error in deserializing body of reply message for operation '{0}'. + + + Error in deserializing body of reply message for operation '{0}'. {1} + + + There was an error in serializing body of message {0}: '{1}'. Please see InnerException for more details. + + + There was an error in deserializing one of the headers in message {0}. Please see InnerException for more details. + + + There was an error in serializing one of the headers in message {0}: '{1}'. Please see InnerException for more details. + + + Server returned an invalid SOAP Fault. Please see InnerException for more details. + + + An error occurred while loading attribute '{0}' on type '{1}'. Please see InnerException for more details. + + + An error occurred while loading attribute '{0}' on method '{1}' in type '{2}'. Please see InnerException for more details. + + + An error occurred while loading attribute '{0}' on parameter {1} of method '{2}' in type '{3}'. Please see InnerException for more details. + + + An error occurred while loading attribute '{0}'. Please see InnerException for more details. + + + --- End of inner ExceptionDetail stack trace --- + + + An ExceptionDetail, likely created by IncludeExceptionDetailInFaults=true, whose value is: + + + Internal Error: Message must be a valid IMethodCallMessage. + + + The specified ContractDescription could not be exported to WSDL because the Type property of the MessagePartDescription with name '{1}' in the OperationDescription with name '{0}' is not set. The Type property must be set in order to create WSDL. + + + Fault named {0} in operation {1} cannot be imported. {2} + + + In operation {0}, more than one fault is declared with detail type {1} + + + In operation {0}, more than one fault is declared with element name {1} in namespace {2} + + + {0}: {1} (Fault Detail is equal to {2}). + + + The creator of this fault did not specify a Reason. + + + In operation {0}, the schema type corresponding to the fault detail type {1} is anonymous. Please set Fault name explicitly to export anonymous types. + + + Header name mismatch in member {1} of type {0}. The header name found in the description is {2}. The element name deduced by the formatter is {3}. This mismatch can happen if the ElementName specified in XmlElementAttribute or XmlArrayAttribute does not match the name specified in the MessageHeaderAttribute or MessageHeaderArrayAttribute or the member name. + + + Header name mismatch in operation {0} from contract {1}:{2}. The header name found in the description is {3}. The element name deduced by the formatter is {4}. This mismatch can happen if the ElementName specified in XmlElementAttribute or XmlArrayAttribute does not match the name specified in the MessageHeaderAttribute or MessageHeaderArrayAttribute or the member name. + + + Header namespace mismatch in member {1} of type {0}. The header namespace found in the description is {2}. The element namespace deduced by the formatter is {3}. This mismatch can happen if the Namespace specified in XmlElementAttribute or XmlArrayAttribute does not match the namespace specified in the MessageHeaderAttribute or MessageHeaderArrayAttribute or the contract namespace. + + + Header namespace mismatch in operation {0} from contract {1}:{2}. The header namespace found in the description is {3}. The element namespace deduced by the formatter is {4}. This mismatch can happen if the Namespace specified in XmlElementAttribute or XmlArrayAttribute does not match the namespace specified in the MessageHeaderAttribute or MessageHeaderArrayAttribute or the contract namespace. + + + The header '{0}' from the namespace '{1}' was not understood by the recipient of this message, causing the message to not be processed. This error typically indicates that the sender of this message has enabled a communication protocol that the receiver cannot process. Please ensure that the configuration of the client's binding is consistent with the service's binding. + + + Message {0} must not have headers to be used in RPC encoded style. + + + This value cannot be changed after the ServiceHost has opened. + + + This value cannot be changed after the ChannelFactory has opened. + + + This value cannot be changed after the first ClientBase of type '{0}' has been created. + + + {0} cannot be changed after the ServiceHost has opened. + + + Operation {0} binding {1} has extra part {2} that is not present in other bindings + + + Style {1} on header {0} does not match expected style {2}. + + + All parts of message in operation {0} must either contain type or element. + + + Style {1} inferred from messages in operation {0} does not match expected style {2} specified via bindings. + + + Bindings for operation {0} cannot specify different use and style values. Binding {1} specifies use {2} and style {3} while binding {4} specifies use {5} and style {6}. + + + Extensions for operation {0} in binding {1} cannot specify different use values. + + + Message bindings for operation {0} in binding {1} cannot specify different use values. + + + Fault bindings for operation {0} in binding {1} cannot specify different use values. + + + Service implementation object invoked with wrong number of input parameters, operation expects {0} parameters but was called with {1} parameters. + + + Service implementation object invoked with null input parameters, but operation expects {0} parameters. + + + The InstanceContext has no provider for creating Service implementation objects. + + + This OperationContextScope is being disposed out of order. + + + The server was unable to process the request due to an internal error. For more information about the error, either turn on IncludeExceptionDetailInFaults (either from ServiceBehaviorAttribute or from the <serviceDebug> configuration behavior) on the server in order to send the exception information back to the client, or turn on tracing as per the Microsoft .NET Framework SDK documentation and inspect the server trace logs. + + + The client was unable to process the callback request due to an internal error. For more information about the error, either turn on IncludeExceptionDetailInFaults (either from CallbackBehaviorAttribute or from the <clientDebug> configuration behavior) on the client in order to send the exception information back to the server, or turn on tracing as per the Microsoft .NET Framework SDK documentation and inspect the client trace logs. + + + IAsyncResult's State must be the state argument passed to your Begin call. + + + IAsyncResult not provided or of wrong type. + + + The CallbackContract {0} is invalid because it is not an interface type. + + + Invalid IContextChannel passed to OperationContext. Must be either a server dispatching channel or a client proxy channel. + + + This OperationContextScope is being disposed on a different thread than it was created. + + + OperationFormatter encountered an invalid Message body. Expected to find node type 'Element' with name '{0}' and namespace '{1}'. Found node type '{2}' with name '{3}' and namespace '{4}' + + + The OperationFormatter could not deserialize any information from the Message because the Message is empty (IsEmpty = true). + + + There was an error while trying to serialize parameter {0}:{1}. The InnerException message was '{2}'. Please see InnerException for more details. + + + There was an error while trying to deserialize parameter {0}:{1}. Please see InnerException for more details. + + + There was an error while trying to deserialize parameter {0}:{1}. The InnerException message was '{2}'. Please see InnerException for more details. + + + The operation {0} either has a parameter or a return type that is attributed with MessageContractAttribute. In order to represent the request message using a Message Contract, the operation must have a single parameter attributed with MessageContractAttribute. In order to represent the response message using a Message Contract, the operation's return value must be a type that is attributed with MessageContractAttribute and the operation may not have any out or ref parameters. + + + MessageHeaderArrayAttribute found on member {0} is not a single dimensional array. + + + Outgoing request message for operation '{0}' specified Action='{1}', but contract for that operation specifies Action='{2}'. The Action specified in the Message must match the Action in the contract, or the operation contract must specify Action='*'. + + + Outgoing reply message for operation '{0}' specified Action='{1}', but contract for that operation specifies ReplyAction='{2}'. The Action specified in the Message must match the ReplyAction in the contract, or the operation contract must specify ReplyAction='*'. + + + In order to use Streams with the MessageContract programming model, the type {0} must have a single member with MessageBodyMember attribute and the member type must be Stream. + + + For request in operation {0} to be a stream the operation must have a single parameter whose type is Stream. + + + For response in operation {0} to be a stream the operation must have a single out parameter or return value whose type is Stream. + + + Buffer size must be at least {0} bytes. + + + The PrimitiveOperationFormatter was given a parameter or return type which it does not support. + + + The static CreateChannel method cannot be used with the contract {0} because that contract defines a callback contract. Please try using one of the static CreateChannel overloads on DuplexChannelFactory<TChannel>. + + + XmlSerializer attribute {0} is not valid in {1}. Only SoapElement attribute is supported. + + + XmlSerializer attribute {0} is not valid in {1}. Only XmlElement, XmlArray, XmlArrayItem and XmlAnyElement attributes are supported in MessageContract when IsWrapped is false. + + + XmlSerializer attribute {0} is not valid in {1}. Only XmlElement, XmlArray, XmlArrayItem, XmlAnyAttribute and XmlAnyElement attributes are supported when IsWrapped is true. + + + {0} must contain either a single ServiceKnownTypeAttribute that refers to a method or a set of ServiceKnownTypeAttributes, each specifying a valid type + + + The return type of method {1} in type {2} must be IEnumerable<Type> to be used by ServiceKnownTypeAttribute in {0} + + + ServiceKnownTypeAttribute in {0} refers to a method {1} that does not exist in type {2} + + + KnownType cannot be null in operation {0} + + + The type {1} defines a MessageContract but also derives from a type {0} that does not define a MessageContract. All of the objects in the inheritance hierarchy of {1} must defines a MessageContract. + + + The message cannot be deserialized into MessageContract type {0} since it does not have a default (parameterless) constructor. + + + MessageOperationFormatter cannot serialize faults. + + + The value '{0}' is not valid for the Location property. The Location property must be a valid absolute or relative URI. + + + Method {0} is not supported on this proxy, this can happen if the method is not marked with OperationContractAttribute or if the interface type is not marked with ServiceContractAttribute. + + + Callback method {0} is not supported, this can happen if the method is not marked with OperationContractAttribute or if its interface type is not the target of the ServiceContractAttribute's CallbackContract. + + + ServiceHost implementation type {0} does not implement ServiceContract {1}. + + + A DispatchOperation (or ClientOperation) can only be added to its parent DispatchRuntime (or ClientRuntime). + + + No Action header was found with namespace '{0}' for the given message. + + + Calling Post() on '{0}' resulted in multiple callbacks. This indicates a problem in '{0}'. + + + The callback passed to operation '{0}' was called more than once. This indicates an internal error in the implementation of that operation. + + + Method {0} in type {1} has more than one header part of type array of XmlElement. + + + A ServiceContract has more the one operation with an Action of "*". A ServiceContract can have at most one operation an Action = "*". + + + The Service contains multiple ServiceEndpoints with different ContractDescriptions which each have Name='{0}' and Namespace='{1}'. Either provide ContractDescriptions with unique Name and Namespaces, or ensure the ServiceEndpoints have the same ContractDescription instance. + + + Part {1}:{0} is repeating and is not supported in Soap Encoding. + + + The Name property must be a non-empty string. + + + The ConfigurationName property must be a non-empty string. + + + Cannot handle invocation of {0} on interface {1} because the OperationSelector on ClientRuntime is null. + + + The service type provided could not be loaded as a service because it does not have a default (parameter-less) constructor. To fix the problem, add a default constructor to the type, or pass an instance of the type to the host. + + + The contract specified by type '{0}' is ambiguous. The type derives from at least two different types that each define its own service contract. For this type to be used as a contract type, exactly one of its inherited contracts must be more derived than any of the others. + + + Extension {0} prevented call to operation '{1}' from replying by setting the reply to null. + + + Formatter {0} returned a null reply message for call to operation '{1}'. + + + The operation '{0}' could not be completed because the sessionful channel timed out waiting to receive a message. To increase the timeout, either set the receiveTimeout property on the binding in your configuration file, or set the ReceiveTimeout property on the Binding directly. + + + {0} must be a relative URI or an absolute URI with scheme '{1}'. '{2}' is an absolute URI with scheme '{3}'. + + + The HttpGetEnabled property of ServiceMetadataBehavior is set to true and the HttpGetUrl property is a relative address, but there is no http base address. Either supply an http base address or set HttpGetUrl to an absolute address. + + + The HttpsGetEnabled property of ServiceMetadataBehavior is set to true and the HttpsGetUrl property is a relative address, but there is no https base address. Either supply an https base address or set HttpsGetUrl to an absolute address. + + + The ChannelDispatcher with ListenUri '{0}' has endpoints with the following contracts: {1}. Metadata endpoints cannot share ListenUris. The conflicting endpoints were either specified in AddServiceEndpoint() calls, in a config file, or a combination of AddServiceEndpoint() and config. + + + Service implementation type is an interface or abstract class and no implementation object was provided. + + + This property sets EnableFaults on the client. To set EnableFaults on the server, use ChannelDispatcher's EnableFaults. + + + This property sets ManualAddressing on the client. To set ManualAddressing on the server, use ChannelDispatcher's ManualAddressing. + + + TransactedBatchingBehavior validation failed. Service or client cannot be started. Transacted batching is not supported for session contracts. Remove transacted batching behavior from the endpoint or define a non-sessionful contract. + + + TransactedBatchingBehavior validation failed. Service cannot be started. Transacted batching requires ServiceBehavior.ReleaseServiceInstanceOnTransactionComplete to be false. + + + The service implementation object was not initialized or is not available. + + + The WS-Addressing "none" value is not valid for the August 2004 version of WS-Addressing. + + + An object that is not an exception was thrown. + + + The operation '{0}' cannot be the first operation to be called because IsInitiating is false. + + + There was no CLR type specified for parameter {0}, preventing the operation from being generated. + + + The one-way operation '{1}' on ServiceContract '{0}' is configured for transaction flow. Transactions cannot be flowed over one-way operations. + + + The incoming message with action could not be processed because it is targeted at a request-reply operation, but cannot be replied to as the MessageId property is not set. + + + OperationBehaviorAttribute can only go on the service class, it cannot be put on the ServiceContract interface. Method '{0}' on type '{1}' violates this. + + + The ReleaseInstanceMode property on OperationBehaviorAttribute can only be set on non-callback operations. Method '{0}' violates this. + + + Method '{0}' has OperationContractAttribute, but enclosing type '{1}' does not have ServiceContractAttribute. OperationContractAttribute can only be used on methods in ServiceContractAttribute types or on their CallbackContract types. + + + Method '{1}' has {0}, but enclosing type '{2}' does not have ServiceContractAttribute. {0} can only be used on methods in ServiceContractAttribute types. + + + OperationDescription's Name must be a non-empty string. + + + All parameter names used in operations that make up a service contract must not be null. + + + OperationDescription '{0}' is invalid because its Messages property contains an invalid number of MessageDescription instances. Each OperationDescription must have one or two messages. + + + There was a mismatch between the number of supplied arguments and the number of expected arguments. Specifically, the argument '{0}' has '{1}' elements while the argument '{2}' has '{3}' elements. + + + The 'parameters' argument must be an array that contains a single Message object. + + + The 'parameters' argument must be either null or an empty array. + + + The 'parameters' argument must be an array of one element. + + + Message part name {0} is not unique in an RPC Message. + + + The contract '{0}' has at least one operation annotated with '{1}', but the binding used for the contract endpoint at address '{2}' does not support required binding property '{3}'. Please ensure that the binding used for the contract supports the ReceiveContext capability. + + + Required message property '{0}' is missing from the IncomingProperties collections of the received message. Ensure that when the receive context is enabled on the binding, the created channel ensures that '{0}' is present on all received messages. + + + The request message has ReplyTo='{0}' but IContextChannel.LocalAddress is '{1}'. When ManualAddressing is false, these values must be the same, null, or EndpointAddress.AnonymousAddress. Enable ManualAddressing or avoid setting ReplyTo on the message. + + + The request message has FaultTo='{0}' but IContextChannel.LocalAddress is '{1}'. When ManualAddressing is false, these values must be the same, null, or EndpointAddress.AnonymousAddress. Enable ManualAddressing or avoid setting FaultTo on the message. + + + The request message has From='{0}' but IContextChannel.LocalAddress is '{1}'. When ManualAddressing is false, these values must be the same, null, or EndpointAddress.AnonymousAddress. Enable ManualAddressing or avoid setting From on the message. + + + The request message has ReplyTo='{0}' but IContextChannel.RemoteAddress is '{1}'. When ManualAddressing is false, these values must be the same, null, or EndpointAddress.AnonymousAddress because sending a reply to a different address than the original sender can create a security risk. If you want to process such messages, enable ManualAddressing. + + + The request message has FaultTo='{0}' but IContextChannel.RemoteAddress is '{1}'. When ManualAddressing is false, these values must be the same, null, or EndpointAddress.AnonymousAddress because sending a reply to a different address than the original sender can create a security risk. If you want to process such messages, enable ManualAddressing. + + + The request message has From='{0}' but IContextChannel.RemoteAddress is '{1}'. When ManualAddressing is false, these values must be the same, null, or EndpointAddress.AnonymousAddress because sending a reply to a different address than the original sender can create a security risk. If you want to process such messages, enable ManualAddressing. + + + A message was received with a WS-Addressing ReplyTo or FaultTo header targeted at the "None" address. These values are not valid for request-reply operations. Please consider using a one-way operation or enabling ManualAddressing if you need to support ReplyTo or FaultTo values of "None." + + + This request operation did not receive a reply within the configured timeout ({0}). The time allotted to this operation may have been a portion of a longer timeout. This may be because the service is still processing the operation or because the service was unable to send a reply message. Please consider increasing the operation timeout (by casting the channel/proxy to IContextChannel and setting the OperationTimeout property) and ensure that the service is able to connect to the client. + + + This request operation sent to {0} did not receive a reply within the configured timeout ({1}). The time allotted to this operation may have been a portion of a longer timeout. This may be because the service is still processing the operation or because the service was unable to send a reply message. Please consider increasing the operation timeout (by casting the channel/proxy to IContextChannel and setting the OperationTimeout property) and ensure that the service is able to connect to the client. + + + A reply message was received for operation '{0}' with action '{1}'. However, your client code requires action '{2}'. + + + Required runtime property '{0}' is not initialized on DispatchRuntime. Do not remove ServiceBehaviorAttribute from ServiceDescription.Behaviors or ensure that you include a third-party service behavior that supplies this value. + + + The MetadataExchangeClient has resolved more than MaximumResolvedReferences. + + + The 'result' argument must be of type Message. + + + Could not revert impersonation on current thread. Continuing would compromise system security. Terminating process. + + + RPC Message {1} in operation {0} has an invalid body name {2}. It must be {3} + + + RPC Message {1} in operation {0} must have a single MessageBodyMember. + + + There was a problem loading the XSD documents provided: a reference to a schema element with name '{0}' and namespace '{1}' could not be resolved because the element definition could not be found in the schema for targetNamespace '{1}'. Please check the XSD documents provided and try again. + + + There was a problem loading the XSD documents provided: a reference to a schema type with name '{0}' and namespace '{1}' could not be resolved because the type definition could not be found in the schema for targetNamespace '{1}'. Please check the XSD documents provided and try again. + + + Service description message '{1}' from target namespace '{2}' does not contain part named '{0}'. + + + Schema with target namespace '{0}' could not be found. + + + SecurityContextProperty is missing from the request Message, this may indicate security is configured incorrectly. + + + The server did not provide a meaningful reply; this might be caused by a contract mismatch, a premature session shutdown or an internal server error. + + + Endpoints cannot be added after the ServiceHost has been opened/faulted/aborted/closed. + + + Endpoints cannot be added before the Description property has been initialized. + + + ApplyConfiguration requires that the Description property be initialized. Either provide a valid ServiceDescription in the CreateDescription method or override the ApplyConfiguration method to provide an alternative implementation. + + + LoadConfigurationSection requires that the Description property be initialized. Provide a valid ServiceDescription in the CreateDescription method. + + + InitializeRuntime requires that the Description property be initialized. Either provide a valid ServiceDescription in the CreateDescription method or override the InitializeRuntime method to provide an alternative implementation. + + + InitializeDescription must be called with a serviceType or singletonInstance parameter. + + + Header properties cannot be set in MessageHeaderAttribute of {0} as its type is MessageHeader<T>. + + + An exception has been thrown when reading the stream. + + + The message containing this stream has been closed. Note that request streams cannot be accessed after the service operation returns. + + + The message containing this stream has been closed. + + + This channel cannot send any more messages because IsTerminating operation '{0}' has already been called. + + + Throttle limit must be greater than zero. To disable, set to Int32.MaxValue. + + + The timeout value provided was not of a recognized format. Please see InnerException for more details. + + + Timeout must be greater than or equal to TimeSpan.Zero. To disable timeout, specify TimeSpan.MaxValue. + + + Timeouts larger than Int32.MaxValue TotalMilliseconds (approximately 24 days) cannot be honored. To disable timeout, specify TimeSpan.MaxValue. + + + Cannot create a unique part name for {0}. + + + An unrecognized element was encountered in the XML during deserialization which was ignored. + + + TransactedBatchingBehavior validation failed. The service endpoint cannot be started. TransactedBatchingBehavior requires a binding that contains a binding element ITransactedBindingElement that returns true for ITransactedBindingElement.TransactedReceiveEnabled. If you are using NetMsmqBinding or MsmqIntegrationBinding make sure that ExactlyOnce is set to true. + + + TThe operation '{1}' on contract '{0}' is configured with TransactionAutoComplete set to false and with ConcurrencyMode not set to Single. TransactionAutoComplete set to false requires ConcurrencyMode.Single. + + + The '{0}' service is configured with ReleaseServiceInstanceOnTransactionComplete set to true, but the ConcurrencyMode is not set to Single. The ReleaseServiceInstanceOnTransactionComplete requires the use of ConcurrencyMode.Single. + + + The '{0}' service is configured with EnsureOrderedDispatch set to true, but the ConcurrencyMode is not set to Single. EnsureOrderedDispatch requires the use of ConcurrencyMode.Single. + + + The DispatchRuntime.EnsureOrderedDispatch property is set to true, but the DispatchRuntime.ConcurrencyMode is not set to Single. EnsureOrderedDispatch requires the use of ConcurrencyMode.Single. + + + The service does not support concurrent transactions. + + + The transaction under which this method call was executing was asynchronously aborted. + + + The SetTransactionComplete method was called in the operation '{0}' on contract '{1}' when TransactionAutoComplete was set to true. The SetTransactionComplete method can only be called when TransactionAutoComplete is set to false. This is an invalid scenario and the current transaction was aborted. + + + The SetTransactionComplete method was wrongly called more than once in the operation '{0}' on contract '{1}'. The SetTransactionComplete method can only be called once. This is an invalid scenario and the current transaction was aborted. + + + The binding for the endpoint at address '{0}' is configured with both the MsmqTransportBindingElement and the TransactionFlowBindingElement. These two elements cannot be used together. + + + The operation '{1}' on contract '{0}' is configured with TransactionAutoComplete set to false and the InstanceContextMode is not set to PerSession. TransactionAutoComplete set to false requires the use of InstanceContextMode.PerSession. + + + The operation '{0}' on callback contract '{1}' is configured with TransactionAutoComplete set to false. TransactionAutoComplete set to false cannot be used with operations on callback contracts. + + + The operation '{1}' on contract '{0}' is configured with TransactionAutoComplete set to false but SessionMode is not set to Required. TransactionAutoComplete set to false requires SessionMode.Required. + + + The service '{0}' is configured with TransactionAutoCompleteOnSessionClose set to true and with an InstanceContextMode not set to PerSession. TransactionAutoCompleteOnSessionClose set to true requires an instancing mode that uses sessions. + + + The service '{0}' is configured with a TransactionTimeout but no operations are configured with TransactionScopeRequired set to true. TransactionTimeout requires at least one operation with TransactionScopeRequired set to true. + + + The service '{0}' is configured with a TransactionIsolationLevel but no operations are configured with TransactionScopeRequired set to true. TransactionIsolationLevel requires at least one operation with TransactionScopeRequired set to true. + + + The service '{0}' is configured with ReleaseServiceInstanceOnTransactionComplete but no operations are configured with TransactionScopeRequired set to true. The ReleaseServiceInstanceOnTransactionComplete property requires at least one operation with TransactionScopeRequired set to true. Remove the ReleaseServiceInstanceOnTransactionComplete property from the service if this is the case. + + + The service '{0}' is configured with TransactionAutoCompleteOnSessionClose, but no operations are configured with TransactionScopeRequired set to true. The TransactionAutoCompleteOnSessionClose property requires at least one operation with TransactionScopeRequired set to true. Remove the TransactionAutoCompleteOnSessionClose property from the service if this is the case. + + + The service operation requires a transaction to be flowed. + + + The flowed transaction could not be unmarshaled. The following exception occurred: {0} + + + The incoming transaction cannot be deserialized. The transaction header in the message was either malformed or in an unrecognized format. The client and the service must be configured to use the same protocol and protocol version. The following exception occurred: {0} + + + The transaction header '{0}' within the namespace '{1}' was not understood by the service. The client and the service must be configured to use the same protocol and protocol version ('{2}'). + + + An attempt was made to add more than one transaction to a message. At most one transaction can be added. + + + Internal Error: The instance of the MessageContract cannot be null in {0}. + + + The operation '{0}' could not be loaded because it specifies "rpc-style" in "literal" mode, but uses message contract types or the CoreWCF.Channels.Message. This combination is disallowed -- specify a different value for style or use parameters other than message contract types or System.ServiceModel.Channels.Message. + + + The operation '{0}' could not be loaded because it has a parameter or return type of type CoreWCF.Channels.Message or a type that has MessageContractAttribute and other parameters of different types. When using System.ServiceModel.Channels.Message or types with MessageContractAttribute, the method must not use any other types of parameters. + + + When using the rpc-encoded style, message contract types or the CoreWCF.Channels.Message type cannot be used if the operation has no parameters or has a void return value. Add a blank message contract type as a parameter or return type to operation '{0}'. + + + This fault did not provide a matching translation: {0} + + + This fault did not provide a reason (MessageFault.Reason was null). + + + This fault did not provide a reason (MessageFault.Reason.Translations.Count was 0). + + + User operation '{0}.{1}' threw an exception that is unhandled in user code. This exception will be rethrown. If this is a recurring problem, it may indicate an error in the implementation of the '{0}.{1}' method. + + + Parameter '{0}' requires additional schema information that cannot be captured using the parameter mode. The specific attribute is '{1}'. + + + In order to use one of the ServiceHost constructors that takes a service instance, the InstanceContextMode of the service must be set to InstanceContextMode.Single. This can be configured via the ServiceBehaviorAttribute. Otherwise, please consider using the ServiceHost constructors that take a Type argument. + + + Cannot add outgoing headers to message as MessageVersion in OperationContext.Current '{0}' does not match with the header version of message being processed '{1}'. + + + When multiple endpoints on a service share the same ListenUri, those endpoints must all have the same Identity in their EndpointAddress. The endpoints at ListenUri '{0}' do not meet this criteria. + + + Wrapper element name cannot be empty. + + + Wrapper type for message {0} cannot be projected as a data contract type since it has multiple namespaces. Consider using the XmlSerializer + + + WSDL part {0} in message {1} from namespace {2} must have either an element or a type name + + + DataContractSerializer does not support collection specified on element '{0}' + + + Invalid OperationFormatUse specified in the OperationFormatStyle of operation {0}, DataContractSerializer supports only Literal. + + + XmlArrayAttribute cannot be used in repeating part {1}:{0}. + + + Could not find default endpoint element that references contract '{0}' in the ServiceModel client configuration section. This might be because no configuration file was found for your application, or because no endpoint element matching this contract could be found in the client element. + + + Could not find endpoint element with name '{0}' and contract '{1}' in the ServiceModel client configuration section. This might be because no configuration file was found for your application, or because no endpoint element matching this name could be found in the client element. + + + The Address property on ChannelFactory.Endpoint was null. The ChannelFactory's Endpoint must have a valid Address specified. + + + In order to generate configuration information using the GenerateServiceEndpoint method, the ServiceContractGenerator instance must have been initialized with a valid Configuration object. + + + The ServiceHost close operation timed out after {0}. This could be because a client failed to close a sessionful channel within the required time. The time allotted to this operation may have been a portion of a longer timeout. + + + Close process timed out waiting for service dispatch to complete. + + + The WSDL binding named {0} is not valid because no match for operation {1} was found in the corresponding portType definition. + + + The WSDL binding named {0} is not valid because an operation binding doesn't have a name specified. + + + The underlying channel factory could not be created because no binding information was found in the configuration file for endpoint with name '{0}'. Please check the endpoint configuration section with name '{0}' to ensure that binding information is present and correct. + + + The underlying channel factory could not be created because no Binding was passed to the ChannelFactory. Please supply a valid Binding instance via the ChannelFactory constructor. + + + The endpoint configuration section for contract '{0}' with name '{1}' could not be loaded because more than one endpoint configuration with the same name and contract were found. Please check your config and try again. + + + An endpoint configuration section for contract '{0}' could not be loaded because more than one endpoint configuration for that contract was found. Please indicate the preferred endpoint configuration section by name. + + + In operation '{0}', cannot pass null to methods that take Message as input parameter. + + + In operation '{0}', cannot return null from methods that return Message. + + + ServiceHost only supports class service types. + + + The contract name '{0}' could not be found in the list of contracts implemented by the service '{1}'. + + + In order to add an endpoint to the service '{0}', a non-empty contract name must be specified. + + + The contract name 'IMetadataExchange' could not be found in the list of contracts implemented by the service {0}. Add a ServiceMetadataBehavior to the configuration file or to the ServiceHost directly to enable support for this contract. + + + The contract type {0} is not attributed with ServiceContractAttribute. In order to define a valid contract, the specified type (either contract interface or service class) must be attributed with ServiceContractAttribute. + + + An endpoint for type '{0}' could not be added because the ServiceHost instance was not initialized properly. In order to add endpoints by Type, the CreateDescription method must be called. If you are using a class derived from ServiceHost, ensure that the class is properly calling base.CreateDescription. + + + Instance of MessagePartDescription Name='{0}' Namespace='{1}' cannot be used in this context: required 'Type' property was not set. + + + The wsdl operation input {0} in portType {1} does not reference a message. This is either because the message attribute is missing or empty. + + + The wsdl operation output {0} in portType {1} does not reference a message. This is either because the message attribute is missing or empty. + + + The wsdl operation {0} in portType {1} contains a fault that does not reference a message. This is either because the message attribute is missing or empty. + + + Cannot create a typed message from type '{0}'. The functionality only valid for types decorated with MessageContractAttribute. + + + A Channel/Service Endpoint is null. + + + A Channel/Service endpoint's Binding is null. + + + A Channel/Service endpoint's Contract is null. + + + A Channel/Service endpoint's Contract's name is null or empty. + + + A Channel/Service endpoint's Contract's namespace is null. + + + Service '{0}' has zero application (non-infrastructure) endpoints. This might be because no configuration file was found for your application, or because no service element matching the service name could be found in the configuration file, or because no endpoints were defined in the service element. + + + DeliveryRequirementsAttribute requires QueuedDelivery, but binding for the endpoint with contract '{0}' doesn't support it or isn't configured properly to support it. + + + DeliveryRequirementsAttribute disallows QueuedDelivery, but binding for the endpoint with contract '{0}' supports it. + + + The DeliveryRequirementsAttribute on contract '{0}' specifies that the binding must support ordered delivery (RequireOrderedDelivery). This condition could not be verified because the configured binding does not implement IBindingDeliveryCapabilities. The DeliveryRequirementsAttribute may only be used with bindings that implement the IBindingDeliveryCapabilities interface. + + + The DeliveryRequirementsAttribute on contract '{0}' specifies a QueuedDeliveryRequirements constraint. This condition could not be verified because the configured binding does not implement IBindingDeliveryCapabilities. The DeliveryRequirementsAttribute may only be used with bindings that implement the IBindingDeliveryCapabilities interface. + + + The DeliveryRequirementsAttribute on contract '{0}' specifies a QueuedDeliveryRequirements value of NotAllowed. However, the configured binding for this contract specifies that it does support queued delivery. A queued binding may not be used with this contract. + + + At least one operation on the '{0}' contract is configured with the TransactionFlowAttribute attribute set to Mandatory but the channel's binding '{1}' is not configured with a TransactionFlowBindingElement. The TransactionFlowAttribute attribute set to Mandatory cannot be used without a TransactionFlowBindingElement. + + + At least one operation on the '{0}' contract is configured with the TransactionFlowAttribute attribute set to Mandatory but the channel's binding '{1}' is not configured with a TransactionFlowBindingElement. The TransactionFlowAttribute attribute set to Mandatory cannot be used without a TransactionFlowBindingElement. + + + The message with Action '{0}' cannot be processed at the receiver, due to a ContractFilter mismatch at the EndpointDispatcher. This may be because of either a contract mismatch (mismatched Actions between sender and receiver) or a binding/security mismatch between the sender and the receiver. Check that sender and receiver have the same contract and the same binding (including security requirements, e.g. Message, Transport, None). + + + The message with To '{0}' cannot be processed at the receiver, due to an AddressFilter mismatch at the EndpointDispatcher. Check that the sender and receiver's EndpointAddresses agree. + + + The message with Action '{0}' cannot be processed at the receiver because this Action is reserved for the connection opening messages only and cannot be sent from client to server. To invoke this operation on the server, call the '{1}' method on the client proxy instead. + + + The operation '{0}' could not be invoked because the property '{1}' on the OperationContract is set to '{2}'. To invoke this operation on the server, call the '{3}' method on the client proxy instead. + + + When using the IAsyncResult design pattern, the End method cannot be decorated with OperationContractAttribute. Only the corresponding Begin method can be decorated with OperationContractAttribute; that attribute will apply to the Begin-End pair of methods. Method '{0}' in type '{1}' violates this. + + + The WS-AT messaging library failed to initialize. + + + A client-side channel to the WS-AT protocol service could not be created. + + + The DispatchOperation '{0}' requires Formatter, since DeserializeRequest and SerializeReply are not both false. + + + The ClientOperation '{0}' requires Formatter, since SerializeRequest and DeserializeReply are not both false. + + + DispatchOperation requires Invoker. + + + Channel requirements cannot be met by the ChannelFactory for Binding '{0}' since the contract requires support for one of these channel types '{1}' but the binding doesn't support any of them. + + + Channel type '{1}' was requested, but Binding '{0}' doesn't support it or isn't configured properly to support it. + + + ChannelDispatcher requirements cannot be met by the IChannelListener for Binding '{0}' since the contract requires support for one of these channel types '{1}' but the binding only supports these channel types '{2}'. + + + The listener at Uri '{0}' could not be initialized because it was created for an unrecognized channel type. + + + Contract requires Session, but Binding '{0}' doesn't support it or isn't configured properly to support it. + + + Contract does not allow Session, but Binding '{0}' does not support Datagram or is not configured properly to support it. + + + Contract requires OneWay, but Binding '{0}' doesn't support it or isn't configured properly to support it. + + + Contract requires TwoWay (either request-reply or duplex), but Binding '{0}' doesn't support it or isn't configured properly to support it. + + + Contract requires Request/Reply, but Binding '{0}' doesn't support it or isn't configured properly to support it. + + + Contract requires Duplex, but Binding '{0}' doesn't support it or isn't configured properly to support it. + + + Binding '{0}' doesn't support creating any channel types. This often indicates that the BindingElements in a CustomBinding have been stacked incorrectly or in the wrong order. A Transport is required at the bottom of the stack. The recommended order for BindingElements is: TransactionFlow, ReliableSession, Security, CompositeDuplex, OneWay, StreamSecurity, MessageEncoding, Transport. + + + The contract '{0}' is not self-consistent -- it has one or more IsTerminating or non-IsInitiating operations, but it does not have the SessionMode property set to SessionMode.Required. The IsInitiating and IsTerminating attributes can only be used in the context of a session. + + + The operation contract '{0}' is not self-consistent. When the '{1}' is set to '{2}', both '{3}' and '{4}' properties must be true, and the operation must not have any input parameters. + + + The ServiceHost must be configured with either a serviceType or a serviceInstance. Both of these values are currently null. + + + The ServiceMetadataExtension instance could not be added to the ServiceHost instance because it has already been added to another ServiceHost instance. + + + The ServiceMetadataExtension instance could not be removed from the ServiceHost instance because it has not been added to any ServiceHost instance. + + + The ServiceMetadataExtension instance could not be removed from the ServiceHost instance because it has already been added to a different ServiceHost instance. + + + A value of type '{0}' cannot be added to the generic collection, because the collection has been parameterized with a different type. + + + A null value cannot be added to the generic collection, because the collection has been parameterized with a value type. + + + Cannot add two items with the same key to SynchronizedKeyedCollection. + + + Item does not exist in SynchronizedKeyedCollection. + + + A reply message was received without a valid RelatesTo header. This may have been caused by a missing RelatesTo header or a RelatesTo header with an invalid WS-Addressing Relationship type. + + + Internal Error: The InnerChannel property is null. + + + The current channel does not support closing the output session as this channel does not implement ISessionChannel<IDuplexSession>. + + + The ServiceEndpoint with name '{0}' could not be exported to WSDL because the Binding property is null. To fix this, set the Binding property to a valid Binding instance. + + + A binding instance has already been associated to listen URI '{0}'. If two endpoints want to share the same ListenUri, they must also share the same binding object instance. The two conflicting endpoints were either specified in AddServiceEndpoint() calls, in a config file, or a combination of AddServiceEndpoint() and config. + + + The following Policy Assertions were not Imported:\r\n + + + XPath:{0}\r\n Assertions: + + + "XPath Unavailable" + + + A policy expression was ignored because another policy expression with that ID has already been read in this document.\r\nXPath:{0} + + + A policy document was ignored because a policy expression with that ID has already been imported.\r\nPolicy ID:{0} + + + A metadata section containing policy did not have an identifier so it cannot be referenced. + + + XPath:{0} + + + A policy reference was ignored because the policy with ID '{0}' could not be found. + + + A policy reference was ignored because the URI of the reference was empty. + + + A policy reference was ignored because the required {0} attribute was missing. + + + The policy expression was not fully imported because it exceeded the maximum allowable complexity. The import stopped at element '{0}' '{1}'. + + + The policy expression was not fully imported because its normalized form was too large. + + + Unrecognized policy element {0} in namespace {1}. + + + "{0}" is not a supported WS-Policy document root element. + + + The "{0}" namespace is not a recognized WS-Policy namespace. + + + Cannot find usable policy alternatives. + + + Unreachable policy detected.\r\nA WS-Policy element embedded in WSDL is missing a fragment identifier. This policy cannot be referenced by any WS-PolicyAttachment mechanisms.\r\nXPath:{0} + + + The processing of the WSDL parameter failed. Error: {0} + + + The optional WSDL extension element '{0}' from namespace '{1}' was not handled.\r\nXPath: {2} + + + The required WSDL extension element '{0}' from namespace '{1}' was not handled. + + + An unrecognized WSDL extension of Type '{0}' was not handled. + + + A previous call to this WsdlExporter left it in a faulted state. It is no longer usable. + + + A previous call to this WsdlImporter left it in a faulted state. It is no longer usable. + + + The ContractDescription argument to ImportEndpoints must be contained in the KnownContracts collection. + + + A previous attempt to import this {0} already failed. + + + The type {0} registered as a policy extension does not implement IPolicyImportExtension + + + The type {0} registered as a policy extension does not have a public default constructor. Policy extensions must have a public default constructor + + + An exception was thrown in a call to a policy import extension.\r\nExtension: {0}\r\nError: {1} + + + An exception was thrown in a call to a policy export extension.\r\nExtension: {0}\r\nError: {1} + + + Calling IWsdlExportExtension.ExportContract twice with the same ContractDescription is not supported. + + + Duplicate contract XmlQualifiedNames are not supported.\r\nAnother ContractDescription with the Name: {0} and Namespace: {1} has already been exported. + + + Similar ServiceEndpoints were exported. The WSDL export process was forced to suffix wsdl:binding names to avoid naming conflicts.\r\n Similar ServiceEndpoints means different binding instances having the Name: {0} and Namespace: {1} and either the same ContractDescription or at least the same contract Name: {2}. + + + An operation was skipped during export because it has a wildcard action. This is not supported in WSDL.\r\nContract Name:{0}\r\nContract Namespace:{1}\r\nOperation Name:{2} + + + An operation was skipped during export because the property '{0}' is set to '{1}'. This operation should be used for server only and should not be exposed from WSDL. \r\nContract Name:{2}\r\nContract Namespace:{3}\r\nOperation Name:{4} + + + The type {0} registered as a WSDL extension does not implement IWsdlImportExtension. + + + The type {0} registered as a WSDL extension does not have a public default constructor. WSDL extensions must have a public default constructor. + + + An exception was thrown in a call to a WSDL export extension: {0}\r\n contract: {1} + + + An exception was thrown in a call to a WSDL export extension: {0}\r\n Endpoint: {1} + + + A WSDL import extension threw an exception during the BeforeImport call: {0}\r\nError: {1} + + + An exception was thrown while running a WSDL import extension: {0}\r\nError: {1} + + + Cannot import {0}\r\nDetail: {2}\r\nXPath to Error Source: {1} + + + There was an error importing a {0} that the {1} is dependent on.\r\nXPath to {0}: {2} + + + The {0} binding element requires envelope version '{1}' It doesn't support '{2}'. + + + No value. + + + The '{0}' binding element does not support cloning. + + + WsdlImporter encountered unrecognized policy assertions in ServiceDescription '{0}': + + + The {0} declared on method '{1}' in type '{2}' is invalid. {0}s are only valid on methods that are declared in a type that has ServiceContractAttribute. Either add ServiceContractAttribute to type '{2}' or remove {0} from method '{1}'. + + + Too many attributes of type {0} on {1}. + + + Couldn't find required attribute of type {0} on {1}. + + + Attempted to get contract type for {0}, but that type is not a ServiceContract, nor does it inherit a ServiceContract. + + + OperationContract method '{0}' in type '{1}' does not properly implement the async pattern, as no corresponding method '{2}' could be found. Either provide a method called '{2}' or set the AsyncPattern property on method '{0}' to false. + + + OperationContract method '{0}' in type '{1}' does not properly implement the async pattern, as more than one corresponding method '{2}' was found. When using the async pattern, exactly one end method must be provided. Either remove or rename one or more of the '{2}' methods such that there is just one, or set the AsyncPattern property on method '{0}' to false. + + + Invalid async End method signature for method {0} in ServiceContract type {1}. Your end method must take an IAsyncResult as the last argument. + + + Invalid async Begin method signature for method {0} in ServiceContract type {1}. Your begin method must take an AsyncCallback and an object as the last two arguments and return an IAsyncResult. + + + Because base ServiceContract '{0}' has a CallbackContract '{1}', derived ServiceContract '{2}' must also specify either '{1}' or a derived type as its CallbackContract. + + + In a contract inheritance hierarchy, the ServiceContract's CallbackContract must be a subtype of the CallbackContracts of all of the CallbackContracts of the ServiceContracts inherited by the original ServiceContract, Types {0} and {1} violate this rule. + + + Cannot have two operations in the same contract with the same name, methods {0} and {1} in type {2} violate this rule. You can change the name of one of the operations by changing the method name or by using the Name property of OperationContractAttribute. + + + The {0}.{1} operation references a message element [{2}] that has already been exported from the {3}.{4} operation. You can change the name of one of the operations by changing the method name or using the Name property of OperationContractAttribute. Alternatively, you can control the element name in greater detail using the MessageContract programming model. + + + Cannot inherit two different operations with the same name, operation '{0}' from contracts '{1}' and '{2}' violate this rule. You can change the name of one of the operations by changing the method name or by using the Name property of OperationContractAttribute. + + + The synchronous OperationContract method '{0}' in type '{1}' was matched with the asynchronous OperationContract methods '{2}' and '{3}' because they have the same operation name '{4}'. When a synchronous OperationContract method is matched to a pair of asynchronous OperationContract methods, the two OperationContracts must define the same number and types of parameters. In this case, some of the arguments are different. To fix it, ensure that the OperationContracts define the same number and types of arguments, in the same order. Alternatively, changing the name of one of the methods will prevent matching. + + + The synchronous OperationContract method '{0}' in type '{1}' was matched with the task-based asynchronous OperationContract method '{2}' because they have the same operation name '{3}'. When a synchronous OperationContract method is matched to a task-based asynchronous OperationContract method, the two OperationContracts must define the same number and types of parameters. In this case, some of the arguments are different. To fix it, ensure that the OperationContracts define the same number and types of arguments, in the same order. Alternatively, changing the name of one of the methods will prevent matching. + + + The task-based asynchronous OperationContract method '{0}' in type '{1}' was matched with the asynchronous OperationContract methods '{2}' and '{3}' because they have the same operation name '{4}'. When a task-based asynchronous OperationContract method is matched to a pair of asynchronous OperationContract methods, the two OperationContracts must define the same number and types of parameters. In this case, some of the arguments are different. To fix it, ensure that the OperationContracts define the same number and types of arguments, in the same order. Alternatively, changing the name of one of the methods will prevent matching. + + + The synchronous OperationContract method '{0}' in type '{1}' was matched with the asynchronous OperationContract methods '{2}' and '{3}' because they have the same operation name '{4}'. When a synchronous OperationContract method is matched to a pair of asynchronous OperationContract methods, the two OperationContracts must define the same return type. In this case, the return types are different. To fix it, ensure that method '{0}' and method '{3}' have the same return type. Alternatively, changing the name of one of the methods will prevent matching. + + + The synchronous OperationContract method '{0}' in type '{1}' was matched with the task-based asynchronous OperationContract method '{2}' because they have the same operation name '{3}'. When a synchronous OperationContract method is matched to a task-based asynchronous OperationContract method, the two OperationContracts must define the same return type. In this case, the return types are different. To fix it, ensure that method '{0}' and method '{2}' have the same return type. Alternatively, changing the name of one of the methods will prevent matching. + + + The task-based asynchronous OperationContract method '{0}' in type '{1}' was matched with the asynchronous OperationContract methods '{2}' and '{3}' because they have the same operation name '{4}'. When a synchronous OperationContract method is matched to a pair of asynchronous OperationContract methods, the two OperationContracts must define the same return type. In this case, the return types are different. To fix it, ensure that method '{0}' and method '{3}' have the same return type. Alternatively, changing the name of one of the methods will prevent matching. + + + The synchronous OperationContract method '{0}' in type '{1}' was matched with the asynchronous OperationContract methods '{2}' and '{3}' because they have the same operation name '{4}'. When a synchronous OperationContract method is matched to a pair of asynchronous OperationContract methods, any additional attributes must be declared on the synchronous OperationContract method. In this case, the asynchronous OperationContract method '{2}' has one or more attributes of type '{5}'. To fix it, remove the '{5}' attribute or attributes from method '{2}'. Alternatively, changing the name of one of the methods will prevent matching. + + + The synchronous OperationContract method '{0}' in type '{1}' was matched with the task-based asynchronous OperationContract method '{2}' because they have the same operation name '{3}'. When a synchronous OperationContract method is matched to a task-based asynchronous OperationContract method, any additional attributes must be declared on the synchronous OperationContract method. In this case, the task-based asynchronous OperationContract method '{2}' has one or more attributes of type '{4}'. To fix it, remove the '{4}' attribute or attributes from method '{2}'. Alternatively, changing the name of one of the methods will prevent matching. + + + The task-based asynchronous OperationContract method '{0}' in type '{1}' was matched with the asynchronous OperationContract methods '{2}' and '{3}' because they have the same operation name '{4}'. When a task-based asynchronous OperationContract method is matched to a pair of asynchronous OperationContract methods, any additional attributes must be declared on the task-based asynchronous OperationContract method. In this case, the asynchronous OperationContract method '{2}' has one or more attributes of type '{5}'. To fix it, remove the '{5}' attribute or attributes from method '{2}'. Alternatively, changing the name of one of the methods will prevent matching. + + + The synchronous OperationContract method '{0}' in type '{1}' was matched with the asynchronous OperationContract methods '{2}' and '{3}' because they have the same operation name '{4}'. When a synchronous OperationContract method is matched to a pair of asynchronous OperationContract methods, the two OperationContracts must have the same value for the '{5}' property. In this case, the values are different. To fix it, change the '{5} property of one of the OperationContracts to match the other. Alternatively, changing the name of one of the methods will prevent matching. + + + The synchronous OperationContract method '{0}' in type '{1}' was matched with the task-based asynchronous OperationContract method '{2}' because they have the same operation name '{3}'. When a synchronous OperationContract method is matched to a task-based asynchronous OperationContract method, the two OperationContracts must have the same value for the '{4}' property. In this case, the values are different. To fix it, change the '{4} property of one of the OperationContracts to match the other. Alternatively, changing the name of one of the methods will prevent matching. + + + The task-based asynchronous OperationContract method '{0}' in type '{1}' was matched with the asynchronous OperationContract methods '{2}' and '{3}' because they have the same operation name '{4}'. When a task-based asynchronous OperationContract method is matched to a pair of asynchronous OperationContract methods, the two OperationContracts must have the same value for the '{5}' property. In this case, the values are different. To fix it, change the '{5} property of one of the OperationContracts to match the other. Alternatively, changing the name of one of the methods will prevent matching. + + + Operations marked with IsOneWay=true must not declare output parameters, by-reference parameters or return values. + + + One way operation {0} cannot not specify a reply action. + + + The method '{1}' in type '{0}' is marked IsOneWay=true and declares one or more FaultContractAttributes. One-way methods cannot declare FaultContractAttributes. To fix it, change IsOneWay to false or remove the FaultContractAttributes. + + + Only malformed Messages are supported. + + + Cannot locate operation {0} in Contract {1}. + + + Unsupported WSDL, only one message part is supported for fault messages. This fault message references zero or more than one message part. If you have edit access to the WSDL file, you can fix the problem by removing the extra message parts such that fault message references just one part. + + + Unsupported WSDL, the fault message part must reference an element. This fault message does not reference an element. If you have edit access to the WSDL document, you can fix the problem by referencing a schema element using the 'element' attribute. + + + Async End called on wrong channel. + + + Async End called with an IAsyncResult from a different Begin method. + + + The received transaction has an isolation level of '{0}' but the service is configured with a TransactionIsolationLevel of '{1}'. The isolation level for received transactions and the service must be the same. + + + The value of the addressHeaders argument is invalid because the collection contains null values. Null is not a valid value for the AddressHeaderCollection. + + + The array passed does not have enough space to hold all the properties contained by this collection. + + + The value could not be added to the collection, as the collection already contains an item of the same type: '{0}'. This collection only supports one instance of each type. + + + Cannot create channel for a contract that requires request/reply and a binding that requires manual addressing but only supports duplex communication. + + + Missing required '{0}' attribute. + + + Ignoring invalid SOAP header extension in wsdl:operation name='{0}' from targetNamespace='{1}'. Reason: {2} + + + Ignoring invalid SOAP fault extension in wsdl:operation name='{0}' from targetNamespace='{1}'. Reason: {2} + + + Ignoring invalid part in wsdl:message name='{0}' from targetNamespace='{1}'. Reason: {2} + + + PrivacyNotice element must have a Version attribute. + + + PrivacyNotice element Version attribute must have an integer value. + + + Binding validation failed. The client cannot send messages. A conflict in the binding properties caused this failure. The UseActiveDirectory is set to true and QueueTransferProtocol is set to Native. To resolve the conflict, correct one of the properties. + + + Binding validation failed because the binding's ReceiveErrorHandlig property is set to Move or Reject while the version of MSMQ installed on this system is not 4.0 or higher. The channel listener cannot be opened. Resolve the conflict by setting the ReceiveErrorHandling property to Drop or Fault, or by upgrading to MSMQ v4.0. + + + The Ambient transaction used to Complete the ReceiveContext Operation is not in an active state. + + + Binding validation failed because the binding's MsmqAuthenticationMode property is set to Certificate while the MsmqProtectionLevel property is not set to Sign or EncryptAndSign. The channel factory or service host cannot be opened. Resolve the conflict by correcting one of the properties. + + + Binding validation failed. The service or the client cannot be started. A conflict in the binding properties caused this failure. The MsmqAuthenticationMode is set to None and MsmqProtectionLevel is not set to None. To resolve to conflict, correct one of the properties. + + + Binding validation failed because the binding's MsmqAuthenticationMode property is set to WindowsDomain while the MsmqProtectionLevel property is not set to Sign or EncryptAndSign. The channel factory or service host cannot be opened. Resolve the conflict by correcting one of the properties. + + + Creation of a message security context failed because the attached sender certificate was invalid or cannot be validated. The message cannot be received. Ensure that a valid certificate is attached to the message and that the certificate is present in the receiver's certificate store. + + + The content type of an incoming message is unknown or not supported. The message cannot be received. Ensure that the sender was configured to use the same message encoder as the receiver. + + + An incoming MSMQ message contained invalid or unexpected .NET Message Framing information in its body. The message cannot be received. Ensure that the sender is using a compatible service contract with a matching SessionMode. + + + An XML error was encountered while reading a WCF message. The message cannot be received. Ensure the message was sent by a WCF client which used an identical message encoder. + + + TransactedBatchingBehavior validation failed because none of the service operations had the TransactionScopeRequired property set to true on their OperationBehavior attribute. The service host cannot be started. Ensure this requirement is met if you wish to use this behavior. + + + A mismatch was detected between the serialization format specified in the MsmqIntegrationMessageProperty and the body of the MSMQ message. The message cannot be sent. The serialization format ByteArray requires the body of the MSMQ message to be of type byte[]. + + + An error occurred while deserializing an MSMQ message's ActiveX body. The message cannot be received. The specified variant type for the body does not match the actual MSMQ message body. + + + An error occurred while deserializing an MSMQ message's XML body. The message cannot be received. Ensure that the service contract is decorated with appropriate [ServiceKnownType] attributes or the TargetSerializationTypes property is set on the MsmqIntegrationBindingElement. + + + The properties of the message are mismatched. The message cannot be sent. The BodyType message property cannot be specified if the ActiveX serialization format is used. + + + The sender's X.509 certificate was not found. The message cannot be sent. Ensure the certificate is available in the sender's certificate store. + + + Binding validation failed. The client cannot send the message. The DeadLetterQueue is set to Custom, but the CustomDeadLetterQueue is not specified. Specify the URI of the dead letter queue for each application in the CustomDeadLetterQueue property. + + + An error was encountered while deserializing the message. The message cannot be received. + + + Binding validation failed because the endpoint listen URI does not represent an MSMQ direct format name. The service host cannot be opened. Make sure you use a direct format name for the endpoint's listen URI. + + + The host in the CustomDeadLetterQueue URI is not "localhost" or the local machine name. A custom DLQ must reside on the sender's machine. + + + Binding validation failed. The client cannot send a message. The specified dead letter queue does not exist or cannot be written. Ensure the queue exists with the proper authorization to write to it. + + + Binding validation failed because the binding's MsmqProtectionLevel property is set to EncryptAndSign while the UseActiveDirectory is not set to true. The channel factory or the service host cannot be opened. Resolve the conflict by correcting one of the properties. + + + Binding validation failed. The service or the client cannot be started. The ExactlyOnce property is set to false and ReceiveContext is enabled. This is not supported. To resolve the conflict, either set ExactlyOnce to true or disable ReceiveContext. + + + The version check failed with the error: '{0}'. The version of MSMQ cannot be detected All operations that are on the queued channel will fail. Ensure that MSMQ is installed and is available. + + + The message ID '{0}' is not in the right format. + + + The specified addressing scheme is invalid for this binding. The NetMsmqBinding scheme must be net.msmq. The MsmqIntegrationBinding scheme must be msmq.formatname. + + + The MsmqIntegrationBinding validation failed. The service cannot be started. The {0} binding does not support the method signature for the service operation {1} in the {2} contract. Correct the service operation to use the MsmqIntegrationBinding. + + + The ActiveX serialization failed because the serialization format cannot be recognized. The message cannot be received. + + + The variant type is not recognized. The ActiveX serialization failed. The message cannot be sent. The specified variant type is not supported. + + + {0} ({1}, 0x{2}) + + + The message cannot be sent because it's missing an MsmqIntegrationMessageProperty. All messages sent over MSMQ integration channels must carry the MsmqIntegrationMessageProperty. + + + Binding validation failed. The service or the client cannot be started. The ExactlyOnce property is set to true and the Durable property is set to false. This is not supported. To resolve the conflict, correct one of these properties. + + + Argument must be a positive number or zero. + + + A mismatch between the binding and MSMQ queue configuration was detected. The service cannot be started. The ExactlyOnce property is set to false and the queue to read messages from is a transactional queue, Correct the error by setting the ExactlyOnce property to true or create a non-transactional binding. + + + Binding validation failed because the URI represents a subqueue and the ReceiveErrorHandling parameter is set to Move. The service host or channel listener cannot be opened. Resolve this conflict by setting the ReceiveErrorHandling to Fault, Drop or Reject. + + + Creation of a message security context failed because the sender's SID was not found in the message. The message cannot be received. The WindowsDomain MsmqAuthenticationMode requires the sender's SID. + + + An error occurred while opening the queue:{0}. The message cannot be sent or received from the queue. Ensure that MSMQ is installed and running. Also ensure that the queue is available to open with the required access mode and authorization. + + + An error occurred when converting the '{0}' queue path name to the format name: {1}. All operations on the queued channel failed. Ensure that the queue address is valid. MSMQ must be installed with Active Directory integration enabled and access to it is available. + + + Binding validation failed. The client cannot send messages. The CustomDeadLetterQueue property is set, but the DeadLetterQueue property is not set to Custom. Set the DeadLetterQueue property to Custom. + + + Binding validation failed. The client cannot send messages. A conflict in the binding properties is causing the failure. To use the custom dead letter queue, ExactlyOnce must be set to true to resolve to conflict. + + + A mismatch between the binding and MSMQ configuration was detected. The client cannot send messages. To use the custom dead letter queue, you must have MSMQ version 4.0 or higher. If you do not have MSMQ version 4.0 or higher set the DeadLetterQueue property to System or None. + + + The transport channel detected a poison message. This occurred because the message exceeded the maximum number of delivery attempts or because the channel detected a fundamental problem with the message. The inner exception may contain additional information. + + + There was an error opening the queue. Ensure that MSMQ is installed and running, the queue exists and has proper authorization to be read from. The inner exception may contain additional information. + + + The ReceiveContext delete operation failed because the message with Id '{0}' could not be received from the lock subqueue. + + + The ReceiveContext unlock operation failed because the message with Id '{0}' could not be moved from the lock subqueue to the main queue. + + + The queue could not be opened because the ReceiveContext feature is not supported on subqueues. Specify a different queue to receive from, or disable ReceiveContext. + + + An error occurred while receiving a message from the queue: {0}. Ensure that MSMQ is installed and running. Make sure the queue is available to receive from. + + + A transaction error occurred for this session. The session channel is faulted. Messages in the session cannot be sent or received. A queued session cannot be associated with more than one transaction. Ensure that all messages in the session are sent or received using a single transaction. + + + An error occurred while sending to the queue: {0}.Ensure that MSMQ is installed and running. If you are sending to a local queue, ensure the queue exists with the required access mode and authorization. + + + A serialization error occurred. The message cannot be sent or received. The MSMQ integration channel is able to serialize no more than {0} types. + + + The transaction associated with this session channel has been rolled back because Abort was called on the session channel before the transaction committed. + + + Session channels must not have pending messages when the transactions associated with these channels are committed. Pending messages are either messages that have not been received from the session channel or messages that have been received but Complete has not been called for them. The channel has faulted and the transaction was rolled back. + + + Session channels must be closed before the transaction is committed. The channel has faulted and the transaction was rolled back. + + + The total size of messages sent in this session exceeded the maximum value of Int32. The messages in this session cannot be sent. + + + An attempt made to close the session channel while there are still messages pending in the session. Current transaction will be rolled back and the session channel will be faulted. Messages in a session must be consumed all at once. + + + An attempt was made to close the session channel while there are still messages pending in the session. The sessiongram will be rolled back to the queue and the session channel will be faulted. + + + A serialization error occurred because of a mismatch between the value of the SerializationFormat property and the type of the body. The message cannot be sent. Ensure the type of the body is Stream or use a different SerializationFormat. + + + The message time to live (TTL) is too large. The message cannot be sent. The message TTL cannot exceed the Int32 maximum value. + + + A client X.509 certificate was not specified through the channel factory's Credentials property, but one is required when the binding's MsmqAuthenticationMode property is set to Certificate. The message cannot be sent. + + + The current transaction is not active. Messages in this session cannot be sent or received and the session channel will be faulted. All messages in a session must be sent or received using a single transaction. + + + Binding validation failed because the binding's ExactlyOnce property is set to true while the destination queue is non-transactional. The service host cannot be opened. Resolve this conflict by setting the ExactlyOnce property to false or creating a transactional queue for this binding. + + + A transaction was not found in Transaction.Current but one is required for this operation. The channel cannot be opened. Ensure this operation is being called within a transaction scope. + + + A transaction is required but is not available. Messages cannot be sent or received. Ensure that the transaction scope is specified to send or receive messages. + + + A mismatch occurred between the binding and the MSMQ configuration. Messages cannot be sent. The custom dead letter queue specified in the binding must be a transactional queue. Ensure that the custom dead letter queue address is correct and the queue is a transactional queue. + + + The net.msmq scheme does not support port numbers. To correct this, remove the port number from the URI. + + + Unrecognized error {0} (0x{1}) + + + The serialization failed because the serialization format '{0}' is not supported. The message cannot be sent or received. + + + Binding validation failed because the binding's MsmqAuthenticationMode property is set to WindowsDomain but MSMQ is installed with Active Directory integration disabled. The channel factory or service host cannot be opened. + + + The URL in invalid. The URL for the queue cannot contain the '$' character. Use the syntax in net.msmq://machine/private/queueName to address a private queue. + + + The URI is invalid because it is missing a host. + + + Failed to reacquire lock for message. + + + Cannot find '{0}' value in dictionary string. + + + WMI GetObject Query: {0} + + + WMI PutInstance Class: {0} + + + Cannot dequeue a '{0}' object while in the Created state. + + + The binding (Name={0}, Namespace={1}) cannot be used to create a ChannelFactory or a ChannelListener because it appears to be missing a TransportBindingElement. Every binding must have at least one binding element that derives from TransportBindingElement. + + + The TransportBindingElement of type '{0}' in this CustomBinding returned a null or empty string for the Scheme. TransportBindingElement's Scheme must be a non-empty string. + + + Binding '{0}' lacks a TransportBindingElement. Every binding must have a binding element that derives from TransportBindingElement. This binding element must appear last in the BindingElementCollection. + + + In Binding '{0}', TransportBindingElement '{1}' does not appear last in the BindingElementCollection. Please change the order of elements such that the TransportBindingElement is last. + + + None of the binding elements in binding '{0}' define a message version. At least one binding element must define a message version and return it from the GetProperty<MessageVersion> method. + + + Some of the binding elements in this binding were not used when building the ChannelFactory / ChannelListener. This may be have been caused by the binding elements being misordered. The recommended order for binding elements is: TransactionFlow, ReliableSession, Security, CompositeDuplex, OneWay, StreamSecurity, MessageEncoding, Transport. Note that the TransportBindingElement must be last. The following binding elements were not built: {0}. + + + More than one MessageEncodingBindingElement was found in the BindingParameters of the BindingContext. This usually is caused by having multiple MessageEncodingBindingElements in a CustomBinding. Remove all but one of these elements. + + + More than one IStreamUpgradeProviderElement was found in the BindingParameters of the BindingContext. This usually is caused by having multiple IStreamUpgradeProviderElements in a CustomBinding. Remove all but one of these elements. + + + More than one PeerResolverBindingElement was found in the BindingParameters of the BindingContext. This usually is caused by having multiple PeerResolverBindingElements in a CustomBinding. Remove all but one of these elements. + + + More than one PeerCustomResolverBindingElement was found in the BindingParameters of the BindingContext. This usually is caused by having multiple PeerCustomResolverBindingElement in a CustomBinding. Remove all but one of these elements. + + + The security capabilities of binding '{0}' do not match those of the generated runtime object. Most likely this means the binding contains a StreamSecurityBindingElement, but lacks a TransportBindingElement that supports Stream Security (such as TCP or Named Pipes). Either remove the unused StreamSecurityBindingElement or use a transport that supports this element. + + + Only an absolute Uri can be used as a base address. + + + This collection already contains an address with scheme {0}. There can be at most one address per scheme in this collection. If your service is being hosted in IIS you can fix the problem by setting 'system.serviceModel/serviceHostingEnvironment/multipleSiteBindingsEnabled' to true or specifying 'system.serviceModel/serviceHostingEnvironment/baseAddressPrefixFilters'. + + + A base address cannot contain a Uri user info section. + + + The binding does not contain a TransportBindingElement. + + + The binding does not contain a ChannelDemuxerBindingElement. + + + A base address cannot contain a Uri query string. + + + A base address cannot contain a Uri fragment. + + + The given URI must be absolute. + + + The binding for scheme '{0}' specified in the protocol mapping does not exist and must be created. + + + The binding on the service endpoint cannot be configured. + + + Configuration binding extension '{0}' could not be found. Verify that this binding extension is properly registered in system.serviceModel/extensions/bindingExtensions and that it is spelled correctly. + + + A binding reference cycle was detected in your configuration. The following reference cycle must be removed: {0}. + + + The binding specified cannot be null or an empty string. Please specify a valid binding. Valid binding values can be found in the system.serviceModel/extensions/bindingExtensions collection. + + + Cannot parse type '{0}' into a CoreWCF.Dispatcher.XPathMessageFilter. + + + Configuration endpoint extension '{0}' could not be found. Verify that this endpoint extension is properly registered in system.serviceModel/extensions/endpointExtensions and that it is spelled correctly. + + + An endpoint reference cycle was detected in your configuration. The following reference cycle must be removed: {0}. + + + The endpoint specified cannot be null or an empty string. Please specify a valid endpoint. Valid endpoint values can be found in the system.serviceModel/extensions/endpointExtensions collection. + + + Filter element body must not be empty. + + + An extension named {0} already appears in the {1}. Extension names must be unique. + + + An extension of name '{0}' already appears in extension collection. Extension names must be unique. + + + An extension of type '{0}' already appears in extension collection. Extension types must be unique. + + + A child element with the element name '{0}' already exists. Child elements can only be added once. + + + A child element named '{0}' with same key already exists at the same configuration scope. Collection elements must be unique within the same configuration scope (e.g. the same application.config file). Duplicate key value: '{1}'. + + + The '{0}' configuration element key cannot be null. + + + At least one of the configuration element keys '{0}' must not be null. + + + Extension element '{0}' cannot be added to this element. Verify that the extension is registered in the extension collection at system.serviceModel/extensions/{1}. + + + Extension collection '{0}' not found. + + + The extension of type '{0}' is not registered in the extension collection '{1}'. + + + Invalid value for serviceAuthenticationManagerType. The serviceAuthenticationManagerType '{0}' does not derive from '{1}'. + + + Invalid value in policyType. The policyType '{0}' does not implement from '{1}'. + + + The {1} binding does not have a configured binding named '{0}'. + + + The binding at {1} does not have a configured binding named '{0}'. This is an invalid value for {2}. + + + Cannot add the behavior extension '{0}' to the common endpoint behavior because it does not implement '{1}'. + + + Cannot add the behavior extension '{0}' to the common service behavior because it does not implement '{1}'. + + + Invalid value for the certificate validator type. The type '{0}' does not derive from the appropriate base class '{1}'. + + + Invalid value for the client credentials type. The type '{0}' does not derive from the appropriate base class '{1}'. + + + The value '{0}' is not a valid instance of type '{1}'. + + + The instance is not a valid configurable value of type '{0}'. + + + {0} is not a valid encoding string for System.Text.Encoding.GetEncoding(string). + + + There is no endpoint behavior named '{0}'. + + + Cannot add the '{0}' behavior extension to '{1}' endpoint behavior because the underlying behavior type does not implement the IEndpointBehavior interface. + + + The endpoint at {1} does not have a configured endpoint named '{0}'. This is an invalid value for {2}. + + + The attribute '{0}' cannot be specified on element '{1}' when attribute '{2}' is not specified. + + + The CreateServiceEndpoint method in type '{0}' returned null instead of an instance of type '{1}'. + + + Invalid element in configuration. The extension '{0}' does not derive from correct extension base type '{1}'. + + + Invalid element in configuration. The extension name '{0}' is not registered in the collection at system.serviceModel/extensions/{1}. + + + The '{0}' type must derive from {1} to be used in the {2} collection. + + + The element {0} requires a key of type '{1}'. Type of the key passed in: '{2}'. + + + '{0}' is not a valid reliable messaging version. Valid values are 'WSReliableMessagingFebruary2005' and 'WSReliableMessaging11'. + + + Invalid value for the saml serializer type. The type '{0}' does not derive from the appropriate base class: '{1}'. + + + Invalid binding path. There is no binding registered with the configuration path '{0}'. + + + Invalid value for the service credentials type. The type '{0}' does not derive from the appropriate base class '{1}'. + + + Invalid value for the security state encoder type. The type '{0}' does not derive from the appropriate base class '{1}'. + + + Invalid value for the username password validator type. The type '{0}' does not derive from the appropriate base class '{1}'. + + + Invalid value for serviceAuthorizationManagerType. The serviceAuthorizationManagerType '{0}' does not derive from '{1}'. + + + There is no service behavior named '{0}'. + + + Cannot add the behavior extension '{0}' to the service behavior named '{1}' because the underlying behavior type does not implement the IServiceBehavior interface. + + + Start must be between 0 and {0}. Value passed in is {1}. + + + '{0}' is not a valid transaction protocol. Valid values are 'OleTransactions', 'WSAtomicTransactionOctober2004', and 'WSAtomicTransaction11'. + + + The type '{0}' registered for extension '{1}' could not be loaded. + + + Invalid binding type for binding extension configuration object. This binding extension manages configuration of binding type '{0}' and cannot act upon type '{1}'. + + + Invalid binding element type for binding element extension configuration object. This binding element extension manages configuration of binding element type '{0}' and cannot act upon type '{1}'. + + + Invalid endpoint type for endpoint extension configuration object. This endpoint extension manages configuration of endpoint type '{0}' and cannot act upon type '{1}'. + + + No elements matching the key '{0}' were found in the configuration element collection. + + + The key does not match the indexer key. When setting the value of a specific index, the key of the desired value must match the index at which it is being set. Key on element (expected value): {0}. Key provided to indexer: {1}. + + + Cannot add the message encoding element '{0}'. Another message encoding element already exists in the binding '{1}'. There can only be one message encoding element for each binding. + + + Cannot find the extension collection associated with extension of type '{0}'. + + + Federated issuer address cannot be null when specifying an issuer binding. + + + The configuration is read only. + + + The '{0}' configuration section cannot be created. The machine.config file is missing information. Verify that this configuration section is properly registered and that you have correctly spelled the section name. For Windows Communication Foundation sections, run ServiceModelReg.exe -i to fix this error. + + + Cannot add stream upgrade element '{0}'. Another stream upgrade element already exists in the binding '{1}'. There can only be one stream update element per binding. + + + Cannot add the transport element '{0}'. Another transport element already exists in the binding '{1}'. There can only be one transport element for each binding. + + + The XmlElement must contain XML content. + + + The XPathFilter for an XPathFilterElement cannot be null. + + + Namespace prefix '{0}' referenced in XPath expression was not found. + + + (Default) + + + MTAWorkerThread exception + + + An unexpected error has occurred. + + + The CLSID specified in the service file is not configured in the specified application. (The CLSID is {0}, the AppID is {1}.) + + + The CLSID specified in the service file does not have a service element in a configuration file. (The CLSID is {0}.) + + + An endpoint configured for the COM+ CLSID {0} is not a configured interface on the class. (The contract type is {1}.) + + + The COM+ string in the .svc file was formatted incorrectly. (The string is "{0}".) + + + The contract type name in the configuration file was not in the form of an interface identifier. (The string is "{0}".) + + + The configured application was not found. (The Application ID was {0}.) + + + A transaction vote request was completed, but there was no outstanding vote request. + + + Failed to convert type library to assembly + + + Incorrect Interface version in registry + + + Failed to load type library + + + An attempt to load the native type library '{0}' was made. Native type libraries cannot be loaded. + + + Could not find interface in the Assembly + + + The '{0}' user-defined type could not be found. Ensure that the correct type and type library are registered and specified. + + + Could not find keyword {0}. + + + Invalid serializer specified. The only valid values are 'xml' and 'datacontract'. + + + The keyword '{0}' has no equal sign following it. Ensure that each keyword is followed by an equal sign and a value. + + + No value found for a keyword. + + + Badly terminated value {0}. + + + Missing Quote in value {0}. + + + Repeated moniker keyword. + + + Interface {0} not found in configuration. + + + Interface {0} has a null namespace or name. + + + Method {0} given in config was not found on interface {1}. + + + Only one type of server identity can be specified. + + + Address not specified. + + + Mex binding section name attribute not specified. + + + Mex address not specified. + + + Contract not specified. + + + Binding not specified. + + + Binding namespace not specified. + + + Failed to do mex retrieval:{0}. + + + None of the contract in metadata matched the contract specified. + + + The contract does not have an endpoint supporting the binding specified. + + + Moniker Missing Colon + + + Multiple server identity keywords were specified. Ensure that at most one identity keyword is specified. + + + The object does not support the interface '{0}'. + + + Could not duplicate the token (error=0x{0:X}). + + + Could not perform an AccessCheck (error=0x{0:X}). + + + Could not impersonate the anonymous user (error=0x{0:X}). + + + The provided SafeArray parameter was passed by value. SafeArray parameters must be passed by reference. + + + Multi-dimensional SafeArray parameters cannot be used. + + + The elements of the SafeArray must be of the type VARIANT. + + + The lower bound of the SafeArray was not zero. SafeArrays with a lower bound other than zero cannot be used. + + + Could not open the thread token (error=0x{0:X}). + + + Could not open the process token (error=0x{0:X}). + + + The isolation level for component {0} is invalid. (The value was {1}.) + + + The conversion between the client parameter type '{0}' to the required server parameter type '{1}' cannot be performed. + + + The required outer proxy could not be created. Ensure that the service moniker is correctly installed and registered. + + + Cannot load library {0}. Ensure that WCF is properly installed. + + + Interface Not Registered + + + Bad Interface Registration + + + The argument passed to SetObject is not a COM object. + + + No type library available for interface + + + Cannot find CLSID {0} in COM+ application {1}. + + + Cannot create an instance of the specified service: access is denied. + + + An internal error occurred attempting to create an instance of the specified service. + + + No services are configured for the application. + + + Access is denied. The message was not authenticated with a valid windows identity. + + + The session requirements of the contracts are inconsistent. All COM contracts in a service must have the same session requirement. + + + Access is denied. + + + Parameter at index {0} is null. + + + Unable to retrieve IUnknown for object. + + + QueryInterface succeeded but the persistable type wrapper was null. + + + Unexpected threading model. WCF/COM+ integration only supports STA and MTA threading models. + + + None of the methods were found for interface {0}. + + + The {0} operation on the service {1} could not be found in the catalog. + + + The interface with IID {0} cannot be exposed as a web service + + + The parameter named {0} of type {1} on method {2} of interface {3} cannot be serialized. + + + The return value of type {0} on method {1} of interface {2} cannot be serialized. + + + The COM+ Integration service '{0}' specified in configuration is not in a supported format and could not be started. Ensure that the configuration is correctly specified. + + + The method '{0}' could not be found. Ensure that the correct method name is specified. + + + The Dispatch ID '{0}' could not be found or is invalid. + + + At least one operation is asynchronous. Asynchronous operations are not allowed. + + + There are duplicate operations, which is invalid. Remove the duplicates. + + + The number of parameters in the request did not match the number supported by the method. Ensure that the correct number of parameters are specified. + + + Binding type {0} instance {1} not found in config. + + + The required address keyword was not specified. + + + The required binding keyword was not specified or is not valid. + + + A VARIANT parameter was passed by value. VARIANT parameters must be passed by reference. + + + The type for the '{0}' parameter in '{1}' within the namespace '{2}' cannot not be resolved. + + + The operation cannot be performed after the communications channel has been created. + + + The interface with IID {0} has no methods configured in the COM+ catalog and cannot be exposed as a web service. + + + The interface with IID {0} is not configured in the COM+ catalog and cannot be exposed as a web service. + + + The channeloption intrinsic object cannot be created because the channel builder is not initialized. + + + There is no transaction in the context of the operation. + + + The service does not accept issued tokens. + + + There was an error verifying some XML Schemas generated during export:\r\n{0} + + + There was a validation error on a schema generated during export:\r\n Source: {0}\r\n Line: {1} Column: {2}\r\n Validation Error: {3} + + + The Address, Binding and Contract keywords are required. + + + Type load for contract interface ID {0} failed with Error:{1}. + + + Fail to load binding {0} from config. Error:{1}. + + + Application {0} is marked Pooled. Pooled applications are not supported under COM+ hosting. + + + Application {0} has recycling enabled. Recycling of applications is not supported under COM+ hosting. + + + The client token at least needs to have the SecurityImpersonationLevel of at least Impersonation for Out of process Webhost activations. + + + This InstanceContext requires a valid Message to obtain the instance. + + + From: {0}\nAppId: {1}\nClsId: {2}\nIncoming TransactionId: {3}\nRequesting Identity: {4} + + + From: {0}\nAppId: {1}\nClsId: {2}\nIid: {3}\nAction: {4}\nInstance Id: {5}\nManaged Thread Id: {6}\nUnmanaged Thread Id: {7}\nRequesting Identity: {8} + + + AppId: {0}\nClsId: {1}\n + + + AppId: {0} + + + Iid: {0}\nType Library ID: {1} + + + A Windows hotfix or later service pack is required on Windows XP and Windows Server 2003 to use WS-AtomicTransaction and COM+ Integration Web service transaction functionality. See the Microsoft .NET Framework release notes for instructions on installing the required hotfix. + + + Generating manifest file {0} failed with {1}. + + + Directory {0} not found. + + + Cannot access directory {0}. + + + The object with CLSID '{0}' does not support the required IPersistStream interface. + + + CLSID of type {0} does not match the CLSID on PersistStreamTypeWrapper which is {1}. + + + Target object does not support IPersistStream. + + + Target type is an interface but corresponding type is not PersistStreamTypeWrapper. + + + CLSID {0} is not allowed. + + + Transferring to ComPlus logical thread {0}. + + + The cNamedArgs parameter is not supported and must be 0. + + + Binding '{0}' was not found in config. The config file must be present and contain a binding matching the one specified in the moniker. + + + The claimType cannot be an empty string. + + + X509Chain does not have any valid certificates. + + + X509CertificateValidationMode.Custom requires a CustomCertificateValidator. Specify the CustomCertificateValidator property. + + + UserNamePasswordValidationMode.MembershipProvider requires a MembershipProvider. Specify the MembershipProvider property. + + + UserNamePasswordValidationMode.Custom requires a CustomUserNamePasswordValidator. Specify the CustomUserNamePasswordValidator property. + + + The Security Support Provider Interface does not support Impersonation level 'None'. Specify Identification, Impersonation or Delegation level. + + + The public key is not an RSA key. + + + The '{0}' dynamic link library (dll) failed to load. + + + Writing audit messages to the Security log is not supported by the current platform. You must write audit messages to the Application log. + + + No custom principal is specified in the authorization context. + + + Access is denied. + + + SecurityAuditBehavior is not supported on the channel factory. + + + The Infocard token created during channel intialization has expired. Please create a new channel to reacquire token. + + + No Infocard token was found in the ChannelParameters. Infocard requires that the security token be created during channel intialization. + + + Message with action {0} received from a neighbor is missing a via Header. + + + The LinkUtility message received from a neighbor has invalid values for usefull '{0}' and total '{1}'. + + + Internal Error: Peer Neighbor state change from {0} to {1} is invalid. + + + The MaxReceivedMessageSize of the associated listener ({0}) is greater than the MaxReceivedMessageSize of the PeerNode ({1}) with the meshid ({2}), ensure that all ChannelFactories and Endpoints for this mesh have the same configuration for MaxRecievedMessageSize. + + + Binding settings conflict with an existing instance that is using the same mesh name. Check the value of the property {0}. + + + value must be >= {0} and <= {1}. + + + Invalid message: the peer channel via ({0}) has a size of ({1}) it exceeds the maximum via size of ({2}). + + + The PeerNode cannot be opened because it has been Aborted. + + + PNRP is not available. Please refer to the documentation with your system for details on how to install and enable PNRP. + + + The PNRP service is not installed on this machine. Please refer to the documentation with your system for details on how to install and enable PNRP. + + + A PeerResolverBindingElement is required in the {0} binding. The default resolver (PNRP) is not available. + + + Resolver must be specified. The default resolver (PNRP) is not available. Please refer to the documentation with your system for details on how to install and enable PNRP. + + + The specified ResolverType: {0} cannot be loaded. Please ensure that the type name specified refers to a type that can be loaded. + + + Specified resolver settings are not enough to create a valid resolver. Please ensure that a ResolverType and an Address is specified for the custom resolver. + + + The ListenIPAddress {0} is invalid. + + + Internal Error. PeerFlooder instance is already disposed. It cannot be used to send messages. + + + Internal Error. Address of the Service cannot be registered with PNRP. + + + The registrationId {0} is invalid. + + + Application message contains a header that conflicts with a PeerChannel specific header. Name = {0} and Namespace = {1}. + + + PNRP could not find any clouds that match the current operation. + + + "Specified addresses can not be registered with PNRP because either PNRP is not enabled or the specified addresses do not have corresponding clouds. Please refer to the documentation with your system for details on how to install and enable PNRP." + + + The binding's PeerTransportSecuritySettings can not be supported under the current system security configuration. + + + Credentials specified are not sufficient to carry requested operation. Please specify a valid value for {0}. + + + Connection was not accepted because the SecurityContext contained tokens that do not match the current security settings. + + + Addresses specified in the registration exceed PNRP's per registration address limit. + + + Provided information is Insufficient to create a valid connection to the resolver service. + + + Specified PeerResolverMode value {0} is invalid. Please specify either PeerResolveMode.Auto, Default, or Pnrp. + + + concrete PeerResolver implementation must override Initialize to accept metadata about resolver service. + + + The operation: {0} is not valid while the object is in open state. + + + The operation: {0} is not valid while the object is in closed state. + + + Registration info can not be null. Please ensure that the Register operation is invoked with a valid RegistrationInfo object. + + + Resolve info can not be null. Please ensure that the Resolve operation is invoked with a valid ResolveInfo object. + + + Refresh info can not be null. Please ensure that the Refresh operation is invoked with a valid RefreshInfo object. + + + MessageBody does not contain a valid {0} message. Please ensure that the message is well formed. + + + A peer registration with the service address {0} already exists. + + + MeshId: {0}, Node Id: {1}, Online: {2}, Open: {3}, Port: {4} + + + The MessagePropagationFilter threw an exception. Please refer to InnerException. + + + An event notification threw an exception. Please refer to InnerException. + + + The Peer resolver threw an exception. Please refer to InnerException. + + + One of the addresses specified doesn't match any PNRP cloud for registration.{0} + + + Specified cloud {0} could not be used for the specified operation because it is disabled. + + + Specified cloud {0} is configured for Resolve operations only. + + + Requested PNRP operation {0} could not be performed because the port is blocked possibly by a firewall. + + + Specified mesh name {0} cannot be used because a name can only be registered once per process. + + + Invalid RefreshInterval value of {0}; it must be greater than zero + + + Invalid CleanupInterval value of {0}; it must be greater than zero + + + Multiple link-local only interfaces detected. Please specifiy the interface you require by using the ListenIpAddress attribute in the PeerTransportBindingElement + + + Registration with zero addresses detected. Please call Register with more than zero addresses. + + + Certificate generation has failed. Please see the inner exception for more information. + + + Throttle on the mesh {0} waiting. + + + Attempting to prune the slow neighbor for the mesh {0}. + + + Maintainer is starting for the mesh {0}. + + + Maintainer is attempting a connection to Peer {0} for the mesh {1}. + + + Maintainer encountered exception when attempting a connection to Peer {0} for the mesh {1}. Exception is {2}. + + + Mantainer's InitialConnect is running for the mesh {0}. + + + Mantainer is attempting to prune connections for the mesh {0}. + + + Mantainer is attempting to establish additional connections for the mesh {0}. + + + BasicHttpContextBinding {0}:{1} requires that AllowCookies property is set to true. + + + The message contains a callback context header with an endpoint reference for AddressingVersion '{0}'. Callback context can only be transmitted when the AddressingVersion is configured with 'WSAddressing10'. + + + The callback address already has a context header in it. + + + The callback address contains multiple context headers. There can be at most one context header in a callback address. + + + The incoming message with action '{0}' contains a callback context header with name '{1}' and namespace '{2}'. Callback context headers are not expected in incoming messages at the client. + + + The message contains a callback context message property. Callback context can be transmitted only when the ContextBindingElement is configured with ContextExchangeMechanism of ContextSoapHeader. + + + ContextBindingElement cannot provide channel factory for the requested channel shape {0}. + + + ContextBindingElement cannot provide channel listener for the requested channel shape {0}. + + + Value '{0}' specified for 'name' attribute of ContextMessageProperty is either null or has invalid character(s). Please ensure value of 'name' is within the allowed value space. + + + Context protocol was unable to parse the context header. Nodes disallowed by the context header schema were found inside the context header. + + + The outgoing message with action '{0}' contains a callback context message property. Callback context cannot be transmitted in outgoing messages at the server. + + + Channel context management cannot be enabled or disabled after the channel is opened. + + + Context cached at the channel cannot be set or retrieved when the context management is disabled at the channel layer. Ensure context channel property 'IContextManager.Enabled' is set to true. + + + Context cached at the channel layer cannot be changed after the channel is opened. + + + Cannot specify 'ContextMessageProperty' in message when using context channel with context management enabled. Ensure the message does not have 'ContextMessageProperty' or disable context management by setting channel property 'IContextManager.Enabled' to false. + + + Context channel received a message with context which does not match the current context cached at the channel. Ensure service does not change context after it was originally set or disable context management by setting channel property 'IContextManager.Enabled' to false. + + + Service behavior {0} requires that the binding associated with endpoint {1} listening on {2} supports the context protocol, because the contract associated with this endpoint may require a session. Currently configured binding for this endpoint does not support the context protocol. Please modify the binding to add support for the context protocol or modify the SessionMode on the contract to NotAllowed. + + + Binding {1}:{2} is configured with ContextExchangeMechanism.HttpCookie which is not compatible with the transport type {0}. Please modify the ContextExchangeMechanism or use HTTP or HTTPS transport. + + + ContextBindingElement of binding {0}:{1} is configured with ContextExchangeMode.HttpCookie but the configuration of this binding's HttpTransportBindingElement prevents upper channel layers from managing cookies. Please set the HttpTransportBindingElement.AllowCookies property to false or change the ContextExchangeMechanism of ContextBindingElement to SoapHeader. + + + ContextBindingElementImporter cannot import policy because PolicyImportContext.BindingElements collection is null. + + + EndpointAddress: {0}, Via:{1} + + + Context protocol was unable to parse the context header. + + + Context protocol was unable to parse the callback context header. + + + The OLE Transactions header was invalid or corrupt. + + + The WS-AtomicTransaction header was invalid or corrupt. + + + The issued token accompanying the WS-AtomicTransaction coordination context was invalid or corrupt. + + + The OLE Transactions propagation token received in the message could not be used to unmarshal a transaction. It may be invalid or corrupt. + + + The WS-AtomicTransaction extended information included in the OLE Transactions propagation token was invalid or corrupt. + + + An error occurred communicating with the distributed transaction manager. + + + The WS-AtomicTransaction protocol service could not unmarshal the flowed transaction. The following exception occured: {0} + + + The transaction identifier element in the registration header is invalid + + + The context identifier element in the registration header is invalid. + + + The token identifier element in the registration header is invalid. + + + The transaction identifier element in the coordination context is invalid. + + + The WS-AtomicTransaction transaction formatter could not read the registry value '{0}'. + + + The MSDTC transaction manager's WS-AtomicTransaction protocol service '{0}' is disabled and cannot unmarshal incoming transactions. + + + The MSDTC transaction manager has disabled incoming transactions. + + + The incoming transaction cannot be unmarshaled because the source MSDTC transaction manager has either disabled outbound transactions or disabled its WS-AtomicTransaction protocol service. + + + A registration service address could not be created from MSDTC whereabouts information. + + + The MSDTC whereabouts information could not be deserialized. + + + The standard whereabouts signature was missing from the MSDTC whereabouts information. + + + The MSDTC whereabouts information's protocol count was invalid. + + + The MSDTC whereabouts information's host name byte count was invalid. + + + The MSDTC whereabouts information's host name was invalid. + + + The MSDTC whereabouts information did not contain a host name. + + + The specified WSAT protocol version is invalid. + + + The parameter cannot be empty. + + + The requested resouce has not changed and should be taken from cache. + + + The requested resource has moved to one of the following locations:\n{0} + + + The requested resource must be accessed through one of the following intermediary service locations:\n{0} + + + The requested resource has been moved. + + + At least one RedirectionLocation must be provided for this RedirectionType. + + + RedirectionType 'Cache' does not allow any RedirectionLocation objects be passed into the constructor. + + + {0} ({1}) + + + {0} + + + The requested resource is available. + + + Executing user callback. + + + Close '{0}'. + + + Construct ChannelFactory. Contract type: '{0}'. + + + Construct ServiceHost '{0}'. + + + Execute '{0}.{1}'. + + + Execute Async: Begin: '{0}.{1}'; End: '{2}.{3}'. + + + Close ChannelFactory. Contract type: '{0}'. + + + Close ClientBase. Contract type: '{0}'. + + + Close ServiceHost '{0}'. + + + Listen at '{0}'. + + + Open '{0}'. + + + Open ServiceHost '{0}'. + + + Open ChannelFactory. Contract type: '{0}'. + + + Open ClientBase. Contract type: '{0}'. + + + Process action '{0}'. + + + Processing message {0}. + + + Receive bytes on connection '{0}'. + + + Set up Secure Session. + + + Renew Secure Session. + + + Close Security Session. + + + Shared listener connection: '{0}'. + + + Socket connection: '{0}'. + + + Reading data from connection on '{0}'. + + + Receiving data at via '{0}'. + + + Begin method execution. + + + Created: {0} + + + Disposed: {0} + + + Sent a message over a channel + + + Prepared message for sending over a channel + + + ComPlus:channel created. + + + ComPlus:Dispatch method details. + + + ComPlus:DllHost initializer:Adding host. + + + ComPlus:Started DllHost initializer. + + + ComPlus:Starting DllHost initializer. + + + ComPlus:Stopped DllHost initializer. + + + ComPlus:Stopping DllHost initializer. + + + ComPlus:Entering COM+ activity. + + + ComPlus:Executing COM call. + + + ComPlus:Received instance creation request. + + + ComPlus:Created instance. + + + ComPlus:Released instance. + + + ComPlus:Invoked method. + + + ComPlus:Invoking method. + + + Complus:Invoking method with transaction in COM+ context. + + + Complus:Invoking method with new incoming transaction. + + + ComPlus:Left COM+ activity. + + + Complus:Mex channel loader loaded. + + + Complus:Metadata exchange completed successfully. + + + ComPlus:Created service contract. + + + ComPlus:Created service endpoint. + + + ComPlus:Started service. + + + ComPlus:Started service:details. + + + ComPlus:Starting service. + + + ComPlus:Stopped service. + + + ComPlus:Stopping service. + + + ComPlus:Service moniker parsed. + + + ComPlus:Type library converter event. + + + ComPlus:Finished type library import. + + + ComPlus:Type library import: using assembly. + + + ComPlus:Type library import: using type library. + + + ComPlus:Starting type library import. + + + ComPlus:Transaction aborted by COM+ context. + + + ComPlus:Transaction aborted by Transaction Manager. + + + ComPlus:Transaction committed. + + + ComPlus:Typed channel builder loaded. + + + ComPlus:WSDL channel builder loaded. + + + Aborted '{0}'. + + + Failed to abort {0} + + + Failed to close {0} + + + Closed {0} + + + Created {0} + + + Closing {0} + + + Disposing {0} + + + CommunicationObject faulted due to exception. + + + Faulted {0} + + + Failed to open {0} + + + Opened {0} + + + Opening {0} + + + The configuration is read-only. + + + Extension type is not configured. + + + The connection has been abandoned. + + + Connection information. + + + An exception occurred while closing the connections in this connection pool. + + + A connection has exceeded the idle timeout of this connection pool ({0}) and been closed. + + + A connection has exceeded the connection lease timeout of this connection pool ({0}) and been closed. + + + MaxOutboundConnectionsPerEndpoint quota ({0}) has been reached, so connection was closed and not stored in this connection pool. + + + MaxOutboundConnectionsPerEndpoint quota ({0}) has been reached, so the connection was closed and not reused by the listener. + + + No matching <service> tag was found. Default endpoints added. + + + Failed to trace a message + + + Did not understand message header. + + + A response message was received, but there are no outstanding requests waiting for this message. The message is being dropped. + + + The given schema cannot be imported in this format. + + + The type of the element does not match the configuration type. + + + End method execution. + + + Endpoint listener closed. + + + Endpoint listener opened. + + + Error invoking user code + + + Configuration evaluation context not found. + + + Starting Security ExportChannelBinding + + + Finished Security ExportChannelBinding + + + The extension collection does not exist. + + + The extension collection is empty. + + + Extension element not associated with an extension collection. + + + The extension element already exists in the collection. + + + Extension type not found. + + + Failed to set an activity id header on an outgoing message + + + Failed to read an activity id header on a message + + + Evaluating message logging filter against the message exceeded the node quota set on the filter. + + + Get BehaviorElement. + + + Get ChannelEndpointElement. + + + Get machine.config common behaviors. + + + Get configuration section. + + + Get configured binding. + + + Get default configured binding. + + + Get configured endpoint. + + + Get default configured endpoint. + + + Get ServiceElement. + + + Authentication failed for HTTP(S) connection + + + The HTTP SOAPAction header and the wsa:Action SOAP header did not match. + + + Failed to lookup a channel to receive an incoming message. Either the endpoint or the SOAP action was not found. + + + Failed to send request message over HTTP + + + Failed to send response message over HTTP + + + Received bad HTTP response + + + HTTP response was received + + + The HTTP concurrent receive quota was reached. + + + Client certificate is invalid. + + + Client certificate is invalid with native error code {0} (see http://go.microsoft.com/fwlink/?LinkId=187517 for details). + + + Client certificate is required. No certificate was found in the request. This might be because the client certificate could not be successfully validated by the operating system or IIS. For information on how to bypass those validations and use a custom X509CertificateValidator in WCF please see http://go.microsoft.com/fwlink/?LinkId=208540. + + + Starting Security ImportChannelBinding + + + Finished Security ImportChannelBinding + + + An existing incompatible transport manager was found for the specified URI. + + + Initiating Named Pipe connection. + + + Initiating TCP connection. + + + The IssuanceTokenProvider has started a new security negotiation. + + + The IssuanceTokenProvider has completed the security negotiation. + + + The IssuanceTokenProvider applied a redirection header. + + + The IssuanceTokenProvider removed the expired service token. + + + IssuanceTokenProvider pruned service token cache. + + + The IssuanceTokenProvider used the cached service token. + + + Listener created + + + Listener disposed + + + Maximum number of pending connections has been reached. + + + Maximum number of inbound session channel has been reached. + + + A message was closed + + + A message was closed again + + + A message was copied + + + Reached the limit of messages to log. Message logging is stopping. + + + Message not logged because its size exceeds configured quota + + + A message was read + + + Sent a message over a channel. + + + Received a message over a channel. + + + A message was written + + + Switched threads while processing a message. + + + MsmqActivation service cannot peek on the queue. + + + MsmqActivation service cannot discover queues. + + + MSMQ datagram message received. + + + MSMQ datagram message sent. + + + MSMQ detected successfully. + + + Entered batching mode. + + + Expected exception caught. + + + Hosting environment found the base address for the service. + + + Left batching mode. + + + MsmqActivation service found application matching queue. + + + Cannot move or delete message because it is still locked under the transaction. + + + Message was dropped. + + + Message was rejected. + + + Cannot move or delete message. + + + Poison message moved to the poison subqueue. + + + Poison message moved to the retry subqueue. + + + Poison message rejected. + + + Pool of the native MSMQ messages is full. This may affect performance. + + + Transaction which received this message was aborted at least once. + + + MSMQ queue closed. + + + MSMQ queue opened. + + + Cannot detect if the queue is transactional. + + + MsmqActivation service started scan for queues. + + + MSMQ transport session received. + + + MSMQ transport session sent. + + + MSMQ Activation service started application. + + + Hosting environment started service. + + + Unexpected acknowledgment value. + + + Failed to receive a message over a named pipe channel. + + + Received a message over a named pipe channel. + + + NegotiationTokenAuthenticator was attached. + + + NegotiationTokenProvider was attached. + + + No existing transport manager was found for the specified URI. + + + Transport is listening at base URI. + + + The configuration system has detected a duplicate key in a different configuration scope and is overriding with the more recent value. + + + A message was received by a peer channel. + + + A message was sent on a peer channel. + + + A PeerNode received a message that did not match any local channels. + + + A PeerNode received a flooded message that was not propagated further. + + + A PeerNode received a flooded message. + + + Received message could not be forwarded to other neighbors since it exceeded the quota set for the peer node. + + + A Peer Neighbor close has failed. + + + A Peer Neighbor closing has failed. + + + A Peer Neighbor Manager is offline. + + + A Peer Neighbor Manager is online. + + + A message was received by a Peer Neighbor. + + + A Peer Neighbor was not accepted. + + + A Peer Neighbor was not found. + + + A Peer Neighbor open has failed. + + + A Peer Neighbor state change has failed. + + + A Peer Neighbor state has changed. + + + A PeerNode address has changed. + + + A neighbor connection could not be established due to insufficient or wrong credentials. + + + A neighbor security handshake as timed out. + + + A PeerNode was closed. + + + A PeerNode is closing. + + + Peer node open failed. + + + A PeerNode was opened. + + + A PeerNode is opening. + + + Message source could not be authenticated. + + + PeerService Opened and listening at '{0}'. + + + A performance counter failed to load. Some performance counters will not be available. + + + Failed to load the performance counter '{0}'. Some performance counters will not be available + + + There was an error while updating the performance counter '{0}'. This performance counter will be disabled. + + + Loading performance counters for the service failed. Performance counters will not be available for this service. + + + Unloading the performance counters failed. + + + Registered addresses in PNRP. + + + Resolved addresses in PNRP. + + + Unexpected Exception during PNRP resolve operation. + + + Unregistered addresses in PNRP. + + + A null Message (signalling end of channel) was received from a datagram channel, but the channel is still in the Opened state. This indicates a bug in the datagram channel, and the demuxer receive loop has been prematurely stalled. + + + PeerMaintainer Activity. + + + A reliable channel has been opened. + + + Behavior type already exists in the collection + + + Received reply over request channel + + + A failure occured while performing a security related operation. + + + An active security session was removed by the server. + + + A failure occurred while writing to the security audit log. + + + The security audit log is written successfully. + + + The security protocol verified the incoming message. + + + The security protocol secured the outgoing message. + + + The security protocol cannot secure the outgoing message. + + + The security protocol cannot verify the incoming message. + + + The client security session renewed the session key. + + + A Close message was sent by the client security session. + + + Close response message was sent by client security session. + + + Close message was received by client security session.TraceCodeSecurityClientSessionKeyRenewed=Client security session renewed session key. + + + The client security session discarded the previous session key. + + + The SecurityContextSecurityToken cache is full. + + + Identity cannot be determined for an EndpointReference. + + + Identity was determined for an EndpointReference. + + + The HostName portion of an endpoint address cannot be normalized. + + + Identity verification failed. + + + Identity verification succeeded. + + + Security impersonation failed at the server. + + + Security Impersonation succeeded at the server. + + + An inactive security session was faulted by the server. + + + Service security negotiation processing failure. + + + A new security session key was issued by the server. + + + A pending security session was added to the server. + + + The pending security session was closed by the server. + + + A pending security session was activated by the server. + + + The server security session received a close message from the client. + + + Server security session received Close response message from client. + + + Server security session sent session aborted fault to client. + + + The security session key was updated by the server. + + + The server security session sent a key renewal fault to the client. + + + The server security session sent a close response to the client. + + + Server security session sent Close to client. + + + Client security session received session aborted fault from server. + + + Failure sending security session aborted fault to client. + + + The client security session received a closed reponse from the server. + + + A failure occurred when sending a security session Close response to the client. + + + Failure sending security session Close to client. + + + The client security session received a key renewal fault from the server. + + + The client security session was redirected. + + + A failure occurred when sending a renewal fault on the security session key to the client. + + + The client security session operation failed. + + + The security session operation completed successfully at the client. + + + A security session operation was started at the client. + + + The security session operation failed at the server. + + + The ServicePrincipalName could not be mapped to a SecurityIdentifier. + + + Security Token Authenticator was closed. + + + Security Token Authenticator was opened. + + + Security Token Provider was closed. + + + Security Token Provider was opened. + + + ServiceChannel information. + + + ServiceHost base addresses. + + + ServiceHost close operation timedout. + + + ServiceHost faulted. + + + ServiceHost error on calling ReleasePerformanceCounters. + + + The system hit the limit set for throttle '{0}'. Limit for this throttle was set to {1}. Throttle value can be changed by modifying attribute '{2}' in serviceThrottle element or by modifying '{0}' property on behavior ServiceThrottlingBehavior. + + + The system hit an internal throttle limit. Limit for this throttle was set to {0}. This throttle cannot be configured. + + + The system hit the limit set for the '{0}' throttle. Throttle value can be changed by modifying {0} property on {1}. + + + Switched threads while processing a message for Contract '{0}' at Address '{1}'. ConcurrencyMode for service is set to Single/Reentrant and the service is currently processing another message. + + + Switched threads while processing a message for Contract '{0}' at Address '{1}'. Cannot process more than one transaction at a time and the transaction associated with the previous message is not yet complete. Ensure that the caller has committed the transaction. + + + Switched threads while processing a message for Contract '{0}' at Address '{1}'. Waiting for the completion of ReceiveContext acknowledgement. If your service seems to be not processing the message ensure that the channel implementation of receive context completes the operation. + + + Switched threads while processing a message for Contract '{0}' at Address '{1}'. UseSynchronizationContext property on ServiceBehaviorAttribute is set to true, and SynchronizationContext.Current was non-null when opening ServiceHost. If your service seems to be not processing messages, consider setting UseSynchronizationContext to false. + + + Replying to an operation threw a exception. + + + The Request/Reply operation {0} has no Reply Message. + + + The Request/Reply operation {0} has no IRequestContext to use for the reply. + + + Service security negotiation completed. + + + The incoming message is not part of an existing security session. + + + Create ServiceHost. + + + The TransportManager was successfully closed. + + + A named pipe was successfully duplicated. + + + A socket was successfully duplicated. + + + The PROCESS_DUP_HANDLE access right has been granted to the {0} service's account with SID '{1}'. + + + The TransportManager is now successfully listening. + + + Behavior type is not of expected type + + + An attempt to reuse a pooled connection failed. Another attempt will be made with {0} remaining in the overall timeout. + + + An attempt to connect to the named pipe endpoint at '{1}' failed. Another attempt will be made with {0} remaining in the overall timeout. + + + The operating system's timer resolution was detected as {0} ticks, which is about {1} milliseconds. + + + RequestContext aborted + + + PipeConnection aborted + + + The shared memory for the endpoint of the service '{0}' does not exist. The service may not be started. + + + SocketConnection aborted + + + SocketConnection aborted under Close + + + SocketConnection close + + + SocketConnection create + + + SpnegoTokenProvider completed SSPI negotiation. + + + SpnegoTokenAuthenticator completed SSPI negotiation. + + + Client's outgoing SSPI negotiation. + + + Service's outgoing SSPI negotiation. + + + The remote SSL client failed to provide a required certificate. + + + The stream security upgrade was accepted successfully. + + + Failed to receive a message over TCP channel + + + Received a message over TCP channel + + + Understood message header. + + + No service available to handle this action + + + Unhandled exception in user operation '{0}.{1}'. + + + Webhost could not activate service + + + Webhost couldn't compile service + + + Setting a value via WMI. + + + A non-critical error or warning occurred during WSDL Export + + + A non-critical error or warning occurred in the MetadataExchangeClient during WSDL Import This could result in some endpoints not being imported. + + + An incoming channel was disposed because there was an error while attempting to open it. + + + Listen at '{0}'. + + + An invalid create sequence message was received. + + + An invalid WS-RM message was received. + + + An incoming create sequence request was rejected because the maximum pending channel count was reached. + + + A message in a WS-RM sequence has been dropped because it could not be buffered. + + + The reliable session infrastructure detected a system clock change. This will temporarily result in a less optimal message retry strategy. + + + WS-RM SequenceAcknowledgement received. + + + WS-RM Last Sequence message received. + + + WS-RM Sequence message received. + + + WS-RM SequenceAcknowledgement sent. + + + WS-RM Last Sequence message sent. + + + WS-RM Sequence message sent. + + + A WS-RM sequence has faulted. + + + Channel connection was dropped + + + An async callback threw an exception! + + + The MetadataExchangeClient is sending a request for metadata. + + + The MetadataExchangeClient received a reply. + + + The ServiceDebugBehavior Help Page is enabled at a relative address and cannot be created because there is no base address. + + + The TCP connect operation failed. + + + The transaction '{0}' was received for operation '{1}' from a transacted transport, such as MSMQ. + + + The transaction '{0}' was flowed to operation '{1}'. + + + The transaction '{0}' was received for operation '{1}' from an InstanceContext transaction. + + + Existing transaction '{0}' being used for operation '{1}'. + + + The transaction '{0}' for operation '{1}' was completed due to the TransactionAutoComplete OperationBehaviorAttribute member being set to true. + + + The transaction '{0}' for operation '{1}' was completed due to an unhandled execution exception. + + + The transaction '{0}' for operation '{1}' was completed due to a call to SetTransactionComplete. + + + The transaction '{0}' was completed when the session was closed due to the TransactionAutoCompleteOnSessionClose ServiceBehaviorAttribute member. + + + The transaction '{0}' for operation '{1}' was completed due to asynchronous abort. + + + The transaction '{0}' for operation '{1}' remains attached to the InstanceContext. + + + The transaction '{0}' was aborted because it was uncompleted when the session was closed and the TransactionAutoCompleteOnSessionClose OperationBehaviorAttribute was set to false. + + + The service instance was released on the completion of the transaction '{0}' because the ReleaseServiceInstanceOnTransactionComplete ServiceBehaviorAttribute was set to true. + + + The transaction '{0}' was asynchronously aborted. + + + The OleTransactions protocol negotiation failed for coordination context '{0}'. + + + The transaction '{0}' for operation '{1}' was newly created. + + + Activating message received. + + + InstanceContext cached for InstanceId {0}. + + + InstanceContext for InstanceId {0} removed from cache. + + + DurableInstance's InstanceContext refcount incremented. + + + DurableInstance's InstanceContext refcount decremented. + + + ContextChannel created. + + + A new ContextChannel was accepted. + + + Context added to Message. + + + Context retrieved from Message. + + + WorkflowServiceHost created. + + + ServiceDurableInstance '{0}' deleted from persistence store. + + + ServiceDurableInstance '{0}' disposed. + + + ServiceDurableInstance loaded from persistence store. + + + ServiceDurableInstance saved to persistence store. + + + WorkflowDurableInstance '{0}' loaded. + + + WorkflowDurableInstance '{0}' activated. + + + WorkflowDurableInstance aborted. + + + Work item enqueued. + + + Reply sent for InstanceId {0}. + + + Fault Sent for InstanceId {0}. + + + Sql execution started. + + + Sql execution complete. + + + SqlPersistenceProvider.Open() parameters. + + + SynchronizationContextWorkflowSchedulerService - Timer {0} cancelled. + + + SynchronizationContextWorkflowSchedulerService - Timer {0} created for InstanceId {1}. + + + Reading of a syndication feed started. + + + Reading of a syndication feed completed. + + + Reading of a syndication item started. + + + Reading of a syndication item completed. + + + Writing of a syndication feed started. + + + Writing of a syndication feed completed. + + + Writing of a syndication item started. + + + Writing of a syndication item completed. + + + Syndication element with name '{0}' and namespace '{1}' was not written. + + + Syndication element with name '{0}' and namespace '{1}' is invalid. + + + HTTP query string parameter with name '{0}' was ignored. + + + Incoming HTTP request with URI '{0}' matched operation '{1}'. + + + Incoming HTTP request with URI '{0}' does not match any operation. + + + Parameter 'baseAddress' must an absolute uri. + + + BaseAddress must an absolute uri. + + + Cannot change BaseAddress after calling MakeReadOnly. + + + There were multiple UriTemplateMatch results, but MatchSingle was called. + + + BaseAddress has not been set. Set the BaseAddress property before calling MakeReadOnly, Match, or MatchSingle. + + + KeyValuePairs must have at least one element. + + + UriTemplate '{0}' contains {1} path variables and {2} query variables but {3} values were passed to the BindByPosition method. The number of values passed to BindByPosition should be greater than or equal to the number of path variables in the template and cannot be greater than the total number of variables in the template. + + + baseAddress must an absolute Uri. + + + The UriTemplate '{0}' is not valid; each portion of the query string must be of the form 'name' or of the form 'name=value', where each name is unique. Note that the names are case-insensitive. See the documentation for UriTemplate for more details. + + + The UriTemplate '{0}' is not valid; the query string cannot end with '&amp;'. See the documentation for UriTemplate for more details. + + + The UriTemplate '{0}' is not valid; each portion of the query string must be of the form 'name' or of the form 'name=value'. See the documentation for UriTemplate for more details. + + + The UriTemplate '{0}' is not valid; the UriTemplate variable named '{1}' appears multiple times in the template. Note that UriTemplate variable names are case-insensitive. See the documentation for UriTemplate for more details. + + + UriTemplateTable does not support '{0}' and '{1}' since they are not equivalent, but cannot be disambiguated because they have equivalent paths and the same common literal values for the query string. See the documentation for UriTemplateTable for more detail. + + + UriTemplateTable does not support multiple templates that have equivalent path as template '{0}' but have different query strings, where the query strings cannot all be disambiguated via literal values. See the documentation for UriTemplateTable for more detail. + + + UriTemplateTable (with allowDuplicateEquivalentUriTemplates = false) does not support both '{0}' and '{1}', since they are equivalent. Call MakeReadOnly with allowDuplicateEquivalentUriTemplates = true to use both of these UriTemplates in the same table. See the documentation for UriTemplateTable for more detail. + + + UriTemplate does not support '{0}' as a valid format for a segment or a query part. + + + The path variable '{0}' in the UriTemplate must be bound to a non-empty string value. + + + UriTemplate '{0}' contains no variables; yet the BindByPosition method was called with {1} values. + + + UTCSR - Lookup was called before match + + + The UriTemplate '{0}' is not valid; UriTemplate does not support two adjacent variables with no literal in compound segments, such as in the segment '{1}'. + + + The UriTemplate '{0}' is not valid; each portion of the query string must be of the form 'name=value', when value cannot be a compound segment. See the documentation for UriTemplate for more details. + + + The UriTemplate '{0}' is not valid; each portion of the query string must be of the form 'name' or of the form 'name=value', where name is a simple literal. See the documentation for UriTemplate for more details. + + + Changing an inline default value with information from the additional default values is not supported; the default value to the variable '{0}' was already provided as part of the UriTemplate '{1}'. See the documentation for UriTemplate for more details. + + + The default values of UriTemplate are immutable; they cannot be modified after the construction of the UriTemplate instance. See the documentation of UriTemplate for more details. + + + The UriTemplate '{0}' is not valid; the UriTemplate compound path segment '{1}' provides a default value to variable '{2}'. Note that UriTemplate doesn't support default values to variables in compound segments. See the documentation for UriTemplate for more details. + + + The UriTemplate '{0}' is not valid; the UriTemplate variable declaration '{1}' provides a default value to query variable '{2}'. Note that UriTemplate doesn't support default values to query variables. See the documentation for UriTemplate for more details. + + + The UriTemplate '{0}' is not valid; the UriTemplate variable declaration '{1}' provides an empty default value to path variable '{2}'. Note that UriTemplate path variables cannot be bound to a null or empty value. See the documentation for UriTemplate for more details. + + + The UriTemplate '{0}' is not valid; the UriTemplate variable declaration '{1}' isn't a valid variable construct. Note that UriTemplate variable definitions are either a simple, non-empty, variable name or a 'name=value' format, where the name must not be empty and the value provides a default value to the variable. See the documentation for UriTemplate for more details. + + + The UriTemplate '{0}' is not valid; the wildcard ('{1}') cannot appear in a variable name or literal, unless as a construct for a wildcard segment. Note that a wildcard segment, either a literal or a variable, is valid only as the last path segment in the template; the wildcard can appear only once. See the documentation for UriTemplate for more details. + + + The UriTemplate '{0}' is not valid; the UriTemplate last path segment '{1}' provides a default value to final star variable '{2}'. Note that UriTemplate doesn't support default values to final star variable. See the documentation for UriTemplate for more details. + + + The UriTemplate '{0}' is not valid; the path variable '{1}', defined as part of a compound path segment has been provided with a default value as part of the additional defaults. Note that UriTemplate doesn't support default values to variables in compound segments. See the documentation for UriTemplate for more details. + + + The UriTemplate '{0}' is not valid; the query variable '{1}' has been provided a default value as part of the additional defaults. Note that UriTemplate doesn't support default values to query variables. See the documentation for UriTemplate for more details. + + + The UriTemplate '{0}' is not valid; the additional default value '{1}' has a null value as default value. Note that null default values must be only provided to concrete path variables. See the documentation for UriTemplate for more details. + + + The UriTemplate '{0}' is not valid; the UriTemplate path variable '{1}' has a null default value while following path variable '{2}' has no defaults or provides a non-null default value. Note that UriTemplate path variable with null default value must be followed only with other path variables with null defaulted values. See the documentation for UriTemplate for more details. + + + The UriTemplate '{0}' is not valid; the UriTemplate path variable '{1}' has a null default value while the following path segment '{2}' is not a variable segment with a null default value. Note that UriTemplate path variable with null default values must be followed only with other path variables with null defaulted value. See the documentation for UriTemplate for more details. + + + The UriTemplate '{0}' is not valid; the UriTemplate path variable '{1}' has a null default value while the template is finished with a wildcard. Note that UriTemplate path variable with null default values must be followed only with other path variables with null defaulted value. See the documentation for UriTemplate for more details. + + + The UriTemplate '{0}' is not valid; the UriTemplate final star variable '{1}' has been provides a default value as part of the additional defaults information. Note that UriTemplate doesn't support default values to final star variable. See the documentation for UriTemplate for more details. + + + An invalid template '{0}' was passed as the key in a pair of template and its associated object. UriTemplateTable Key-Value pairs must always contain a valid UriTemplate object as key; note that UriTemplateTable doesn't support templates that are ignoring the trailing slash in respect to matching. See the documentation for UriTemplateTable for more details. + + + A null UriTemplate was passed as the key in a pair of template and its associated object. UriTemplateTable Key-Value pairs must always contain a valid UriTemplate object as key. See the documentation for UriTemplateTable for more details. + + + The BindByName method of UriTemplate was called with an empty name in the collection of arguments for the bind. Note that the NameValueCollection or the Dictionary passed to BindByName cannot contain an empty (or null) name as a key. See the documentation of UriTemplate for more details. + + + The UriTemplate contains a literal value for query key '{0}', but that key also is present in the NameValueCollection. Either remove that key from the NameValueCollection, or else change the UriTemplate to not have a query literal for that key. + + + The name of the extension element must be specified. + + + The Rss20Serializer does not support RSS version '{0}'. + + + The Atom10 specification requires '{0}' to have one of these values: \"text\", \"html\", \"xhtml\", however this value is '{1}' in the document being deserialized. + + + Error in line {0} position {1}. + + + An error was encountered when parsing the feed's XML. Refer to the inner exception for more details. + + + An error was encountered when parsing the document's XML. Refer to the inner exception for more details. + + + An error was encountered when parsing the item's XML. Refer to the inner exception for more details. + + + An error was encountered when parsing a DateTime value in the XML. + + + The outer element name must be specified. + + + The element with name '{0}' and namespace '{1}' is not an allowed feed format. + + + The element with name '{0}' and namespace '{1}' is not an allowed document format. + + + The element with name '{0}' and namespace '{1}' is not an allowed item format. + + + The syndication feed formatter must be configured with a syndication feed. + + + The document formatter must be configured with a document. + + + The syndication item formatter must be configured with a syndication item. + + + A feed containing items that are not buffered (i.e. the items are not stored in an IList) cannot clone its items. Buffer the items in the feed before calling Clone on it or pass false to the Clone method. + + + The feed being deserialized has non-contiguous sets of items in it. This is not supported by '{0}'. + + + The feed created a null category. + + + The item created a null category. + + + The feed created a null person. + + + The item created a null person. + + + =The feed created a null item. + + + Reading of a syndication feed started. + + + Reading of a syndication feed completed. + + + Reading of a syndication item started. + + + Reading of a syndication item completed. + + + Writing of a syndication feed started. + + + Writing of a syndication feed completed. + + + Writing of a syndication item started. + + + Writing of a syndication item completed. + + + Syndication XML node of type '{0}' with name '{1}' and namespace '{2}' ignored on read. + + + Reading of a service document started. + + + Reading of a service document completed. + + + Writing of a service document started. + + + Writing of a service document completed. + + + Reading of a categories document started. + + + Reading of a categories document completed. + + + Writing of a categories document started. + + + Writing of a categories document completed. + + + The feed's authors were not serialized as part of serializing the feed in RSS 2.0 format. + + + The feed's contributors were not serialized as part of serializing the feed in RSS 2.0 format. + + + The feed's id was not serialized as part of serializing the feed in RSS 2.0 format. + + + The feed's links were not serialized as part of serializing the feed in RSS 2.0 format. + + + The item's authors were not serialized as part of serializing the feed in RSS 2.0 format. + + + The item's contributors were not serialized as part of serializing the feed in RSS 2.0 format. + + + The item's links were not serialized as part of serializing the feed in RSS 2.0 format. + + + The item's copyrights were not serialized as part of serializing the feed in RSS 2.0 format. + + + The item's content was not serialized as part of serializing the feed in RSS 2.0 format. + + + The item's last updated time was not serialized as part of serializing the feed in RSS 2.0 format. + + + The outer name of the element extension cannot be empty. + + + The Type of object passed as parameter '{0}' is not derived from {1}. Ensure that the type of object passed is either of type {1} or derived from {1}. + + + Failed to impersonate client identity during serialization of the response message. + + + Line {0}, position {1}. + + + end of file + + + element '{0}' from namespace '{1}' + + + end element '{0}' from namespace '{1}' + + + text '{0}' + + + cdata '{0}' + + + comment '{0}' + + + node {0} + + + Start element expected. Found {0}. + + + A single WSDL document could not be generated for this service. Multiple service contract namespaces were found ({0}). Ensure that all your service contracts have the same namespace. + + + You can also access the service description as a single file: + + + The use of '{0}' on the task-based asynchronous method is not supported. + + + Client side task-based asynchronous method must not have any out or ref parameters. Any data that would have been returned through an out or ref parameter should instead be returned as part of the TResult in the resulting task. + + + Generating message contract since the operation has multiple return values. + + + ID0020: The collection is empty. + + + ID2004: IAsyncResult must be the AsyncResult instance returned from the Begin call. The runtime is expecting '{0}', and the actual type is '{1}'. + + + ID3002: WSTrustServiceContract could not create a SecurityTokenService instance from WSTrustServiceContract.SecurityTokenServiceConfiguration. + + + ID3004: Cannot obtain the schema for namespace: '{0}'. + + + ID3022: The WSTrustServiceContract only supports receiving RequestSecurityToken messages. If you need to support more message types, override the WSTrustServiceContract.DispatchRequest method. + + + ID3023: The WSTrustServiceContract only supports receiving RequestSecurityToken messages asynchronously. If you need to support more message types, override the WSTrustServiceContract.BeginDispatchRequest and EndDispatchRequest. + + + ID3097: ServiceHost does not contain any valid Endpoints. Add at least one valid endpoint in the SecurityTokenServiceConfiguration.TrustEndpoints collection. + + + ID3112: Unrecognized RequestType '{0}' specified in the incoming request. + + + ID3113: The WSTrustServiceContract does not support receiving '{0}' messages with the '{1}' SOAP action. If you need to support this, override the ValidateDispatchContext method. + + + ID3114: The WSTrustServiceContract cannot deserialize the WS-Trust request. + + + ID3137: The TrustVersion '{0}', is not supported, only 'TrustVersion.WSTrust13' and 'TrustVersion.WSTrustFeb2005' is supported. + + + ID3138: The RequestSecurityTokenResponse that was received did not contain a SecurityToken. + + + ID3139: The WSTrustChannel cannot compute a proof key. The KeyType '{0}' is not supported. Valid proof key types supported by the WSTrustChannel are WSTrust13 and WSTrustFeb2005. + + + ID3140: Specify one or more BaseAddresses to enable metadata or set DisableWsdl to true in the SecurityTokenServiceConfiguration. + + + ID3141: The RequestType '{0}', is not supported. If you need to support this RequestType, override the corresponding virtual method in your SecurityTokenService derived class. + + + ID3144: The PortType '{0}' Operation '{1}' has Message '{2}' is expected to have only one part but contains '{3}'. + + + ID3146: WsdlEndpointConversionContext.WsdlPort cannot be null. + + + ID3147: WsdlEndpointConversionContext.WsdlPort.Service cannot be null. + + + ID3148: WsdlEndpointConversionContext.WsdlPort.Service.ServiceDescription cannot be null. + + + ID3149: Cannot find an input message type for PortType '({0}, {1})' for operation '{2}' in the given ServiceDescription. + + + ID3150: Cannot find an output message type for PortType '({0}, {1})' for operation '{2}' in the given ServiceDescription. + + + ID3190: The WSTrustChannel cannot compute a proof key without a valid SecurityToken set as the RequestSecurityToken.UseKey when the RequestSecurityToken.KeyType is '{0}'. + + + ID3191: The WSTrustChannel received a RequestedSecurityTokenResponse message containing an Entropy without a ComputedKeyAlgorithm. + + + ID3192: The WSTrustChannel cannot compute a proof key. The received RequestedSecurityTokenResponse does not contain a RequestedProofToken and the ComputedKeyAlgorithm specified in the response is not supported: '{0}'. + + + ID3193: The WSTrustChannel cannot compute a proof key. The received RequestedSecurityTokenResponse indicates that the proof key is computed using combined entropy. However, the response does not include an entropy. + + + ID3194: The WSTrustChannel cannot compute a proof key. The received RequestedSecurityTokenResponse indicates that the proof key is computed using combined entropy. However, the request does not include an entropy. + + + ID3269: Cannot determine the TrustVersion. It must either be specified explicitly, or a SecurityBindingElement must be present in the binding. + + + ID3270: The WSTrustChannel does not support multi-leg issuance protocols. The RSTR received from the STS must be enclosed in a RequestSecurityTokenResponseCollection element. + + + ID3285: The WS-Trust operation '{0}' is not valid or unsupported. + + + ID3286: The 'inner' parameter must implement the 'CoreWCF.Channels.IChannel' interface. + + + ID3287: WSTrustChannelFactory does not support changing the value of this property after a channel is created. + + + ID4008: '{0}' does not provide an implementation for '{1}'. + + + ID4039: A custom ServiceAuthorizationManager has been configured. Any custom ServiceAuthorizationManager must be derived from IdentityModelServiceAuthorizationManager. + + + ID4041: Cannot configure the ServiceHost '{0}'. The ServiceHost is in a bad state and cannot be configured. + + + ID4053: The token has WS-SecureConversation version '{0}'. Version '{1}' was expected. + + + ID4072: The SecurityTokenHandler '{0}' registered for TokenType '{1}' must derive from '{2}'. + + + ID4101: The token cannot be validated because it is not a SamlSecurityToken or a Saml2SecurityToken. Token type: '{0}' + + + ID4192: The reader is not positioned on a KeyInfo element that can be read. + + + ID4240: The tokenRequirement must derived from 'RecipientServiceModelSecurityTokenRequirement' for SecureConversationSecurityTokens. The tokenRequirement is of type '{0}'. + + + ID4244: Internal error: sessionAuthenticator must support IIssuanceSecurityTokenAuthenticator. + + + ID4245: Internal error: sessionAuthenticator must support ICommunicationObject. + + + ID4268: MergeClaims must have at least one identity that is not null. + + + ID4271: No IAuthorizationPolicy was found for the Transport security token '{0}'. + + + ID4274: The Configuration property of this SecurityTokenHandler is set to null. Tokens cannot be read or validated in this state. Set this property or add this SecurityTokenHandler to a SecurityTokenHandlerCollection with a valid Configuration property. + + + ID4285: Cannot replace SecurityToken with Id '{0}' in cache with new one. Token must exist in cache to be replaced. + + + ID4287: The SecurityTokenRequirement '{0}' doesn't contain a ListenUri. + + + ID5004: Unrecognized namespace: '{0}'. + + + Authorize + + + OnAuthorizeRequest Failed. + + + OnAuthorizeRequest Succeeded. + + + Authentication failed. + + + The IssuedSecurityTokenProvider cannot support the FederatedClientCredentialsParameters. The FederatedClientCredentialsParameters has already provided the '{0}' parameter. + + + The TrustVersion '{0}', is not supported, only 'TrustVersion.WSTrust13' and 'TrustVersion.WSTrustFeb2005' is supported. + + + The input {0} must be a '{1}' object. + + + The input handler list cannot be empty. + + + The '{0}' list is invalid because the property '{1}' of '{2}' is not null. + + + The '{0}' list created by the Func '{1}' is invalid because it contains one or more null items. + + + The config element '{0}' is invalid because the attribute '{1}' and the sub element '{2}' were both specified. These are mutually exclusive items and cannot be used simultaneouly. + + + This '{0}' object cannot be used to generate configuration because it was created with the constructor that takes a '{1}' as the paramter. This functionality is not supported through configuration files. Please use a different constructor if you wish to generate a configuration file. + + + Invalid type: '{0}'. It must inherit from base type '{1}', cannot be abstract, and must expose a public default constructor. + + + '{0}' cannot return a null '{1}' instance. Please ensure that '{0}' returns a valid '{1}' instance. + + + HTTP pipeline operation cancelled. + + + The message property '{0}' is missing in the HttpRequestMessage. Please make sure this property not removed or changed from the properties of the HttpRequestMessage. If you are creating a new HttpRequestMessage, please copy this property from the old message to the new one. + + + The message property '{0}' inside the HttpRequestMessage is not with expected type '{1}'. Please make sure this property not removed or changed from the properties of the HttpRequestMessage. If you are creating a new HttpRequestMessage, please copy this property from the old message to the new one. + + + The value '{0}' is not a valid content type. + + + The property '{0}' is not supported when building a ChannelFactory. The property value must be null when calling BuildChannelFactory. + + + Cound not load type '{0}' from the assemblies in current AppDomain. + + + The HTTP response message should not be null. Please ensure your '{0}' instance returns a non-null '{1}' object. + + + The subprotocol '{0}' was not requested by the client - no '{1}' header was included in the request. + + + The subprotocol '{0}' was not requested by the client. The client requested the following subprotocol(s): '{1}'. + + + The subprotocol '{0}' is invalid because it contains the invalid character '{1}'. + + + The value specified ('{0}') contains more than one subprotocol which is not supported. + + + Empty string is not a valid subprotocol value. Please use "null" to specify no value. + + + This method is not supported for this HTTP content. + + + Invalid value for the {0} type. The type '{1}' does not derive from the appropriate base class '{2}' or is abstract. + + + This service only supports WebSocket connections. + + + This service does not support WebSocket connections. + + + WebSocket upgrade request failed. Received response status code '{0} ({1})', expected: '{2} ({3})'. + + + WebSocket upgrade request failed. The header '{0}' is missing in the response. + + + WebSocket upgrade request failed. The value of header '{0}' is '{1}'. The expected value is '{2}'. + + + Unexpected response - the server accepted the upgrade request but specified the subprotocol '{0}' when no subprotocol was requested. + + + WebSocket object cannot be accessed directly. + + + A WebSocket error occurred. + + + Unexpected WebSocket close message received when receiving a message. + + + Cannot write to the stream because the end of the stream marker was already written. + + + HttpChannelFactory cannot create the channel with shape '{0}' when the {1} of {2} was set as '{3}'. + + + Maximum number of pending WebSocket connections ({0}) has been reached. Consider increasing the '{1}' quota on the '{2}' property of the transport. + + + The opening handshake properties associated with the current WebSocket connection are not available. The most likely cause is that the property '{0}' on the '{1}' object returned from the custom '{2}' is not set. + + + The operation to establish the WebSocket connection timed out. To increase this time limit, use the OpenTimeout property on the service endpoint's binding. + + + The task was cancelled. + + + An error occured when getting the WebSocketVersion from the WebSocket factory of type '{0}'. See the inner exception for details. + + + The WebSocketVersion returned by the WebSocket factory of type '{0}' is either null, empty or invalid. + + + An error occurred when creating the WebSocket with the factory of type '{0}'. See the inner exception for details. + + + WebSocket creation failed. The '{0}' returned a WebSocket that is either null or not opened. + + + The WebSocket returned by the factory of type '{0}' has the SubProtocol '{1}' that doesn't match the requested SubProtocol value '{2}'. + + + The '{0}' contains multiple '{1}' objects, which is invalid. At most one '{1}' should be specified. + + + The Send operation timed out after '{0}'. Increase the SendTimeout value on the Binding. The time allotted to this operation may have been a portion of a longer timeout. + + + The Receive operation timed out after '{0}'. For duplex sessionful channels, the receive timeout is also the idle timeout for the channel, so consider setting a suitably large value for the ReceiveTimeout value on the Binding. The time allotted to this operation may have been a portion of a longer timeout. + + + The '{0}' operation timed out after '{1}'. The time allotted to this operation may have been a portion of a longer timeout. + + + This platform does not support server side WebSockets. + + + This platform does not support client side WebSockets natively. Support for client side WebSockets can be enabled on this platform by providing an implementation of {0}. + + + WebSockets are not supported in the classic pipeline mode. Consider using the integrated pipeline mode for the application pool. + + + The WebSocketModule is not loaded. Check if the WebSocket feature is installed and the WebSocketModule is enabled in the list of IIS modules (see http://go.microsoft.com/fwlink/?LinkId=231398 for details). + + + The name of the policy being imported for contract '{0}:{1}' is invalid:'{2}'. It should be either '{3}', '{4}' or '{5}'. + + + The server didn't accept the connection request. It is possible that the WebSocket protocol version on your client doesn't match the one on the server('{0}'). + + + The server didn't accept the connection request. It is possible that the WebSocket subprotocol sent by your client is not supported by the server. Protocol(s) supported by the server are '{0}'. + + + The server didn't accept the connection request. It is possible that the client side message encoding format doesn't match the setting on the server side. Please check your binding settings. + + + The server didn't accept the connection request. It is possible that the client side message encoding format or message transfer mode doesn't match the setting on the server side. Please check your binding settings. + + + This collection holds request headers and cannot contain the specified response header '{0}'. + + + This collection holds response headers and cannot contain the specified request header '{0}'. + + + Support for {0} and {1} can not be enabled with {2} when the {3} of the {4} is '{5}'. Ensure the {4} used with the binding has a {3} of '{6}'. + + + Enumeration has either not started or has already finished. + + + The parameter '{0}' cannot be an empty string. + + + Specified value has invalid Control characters. + + + Specified value has invalid CRLF characters. + + + Specified value has invalid HTTP Header characters. + + + Specified value has invalid non-ASCII characters. + + + Specified argument was out of the range of valid values. + + + Failed to copy the HTTP header '{0}' with value '{1}' to '{2}'. + + + The size quota for this stream ({0}) has been exceeded. + + + The IAsyncResult implementation '{0}' tried to complete a single operation multiple times. This could be caused by an incorrect application IAsyncResult implementation or other extensibility code, such as an IAsyncResult that returns incorrect CompletedSynchronously values or invokes the AsyncCallback multiple times. + + + Async Callback threw an exception. + + + A null value was returned from an async 'Begin' method or passed to an AsyncCallback. Async 'Begin' implementations must return a non-null IAsyncResult and pass the same IAsyncResult object as the parameter to the AsyncCallback. + + + An incorrect implementation of the IAsyncResult interface may be returning incorrect values from the CompletedSynchronously property or calling the AsyncCallback more than once. The type {0} could be the incorrect implementation. + + + An incorrect implementation of the IAsyncResult interface may be returning incorrect values from the CompletedSynchronously property or calling the AsyncCallback more than once. + + + An incorrect IAsyncResult was provided to an 'End' method. The IAsyncResult object passed to 'End' must be the one returned from the matching 'Begin' or passed to the callback provided to 'Begin'. + + + End cannot be called twice on an AsyncResult. + + + This buffer cannot be returned to the buffer manager because it is the wrong size. + + + The argument must be a non-empty string. + + + The ActionItem was already scheduled for execution that hasn't been completed yet. + + + A Dequeue operation timed out after {0}. The time allotted to this operation may have been a portion of a longer timeout. + + + The task timed out after {0}. The time allotted to this operation may have been a portion of a longer timeout. + + + '{0}' is an invalid XmlNodeType. + + + Object synchronization method was called from an unsynchronized block of code. + + + Argument {0} must be a non-negative timeout value. Provided value was {1}. + + + The specified method handle is incorrect for the proxy of type '{0}' + + + Failed to create a typed proxy for type '{0}' + + + Stream returned by OperationStreamProvider cannot be null. + + + The value '{0}' cannot be parsed as the type '{1}'. + + + Argument {0} must be a positive timeout value. Provided value was {1}. + + + Cannot claim lock within the allotted timeout of {0}. The time allotted to this operation may have been a portion of a longer timeout. + + + The asynchronous result object used to end this operation was not the object that was returned when the operation was initiated. + + \ No newline at end of file diff --git a/src/CoreWCF.NetTcp/tests/BaseStartup.cs b/src/CoreWCF.NetTcp/tests/BaseStartup.cs new file mode 100644 index 000000000..6333aa367 --- /dev/null +++ b/src/CoreWCF.NetTcp/tests/BaseStartup.cs @@ -0,0 +1,38 @@ +using System; +using System.Linq; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting.Server.Features; +using Microsoft.Extensions.DependencyInjection; +using CoreWCF.Channels; +using System.Diagnostics; + +public abstract class BaseStartup where TService : class +{ + public void ConfigureServices(IServiceCollection services) + { + //services.AddServiceModelServices(); + } + + public void Configure(IApplicationBuilder app) + { + //var serverAddressesFeature = app.ServerFeatures.Get(); + //Assert.NotNull(serverAddressesFeature); + //Assert.NotEmpty(serverAddressesFeature.Addresses); + //string address = serverAddressesFeature.Addresses.Where(url => url.StartsWith("net.tcp", StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault(); + //Assert.False(string.IsNullOrEmpty(address)); + //string netTcplisteningUrl = "net.tcp://localhost:11808"; + //UriBuilder uriBuilder = new UriBuilder(new Uri(netTcplisteningUrl)); + //Debug.Assert(SerivceBaseAddress.StartsWith("/"), $"{nameof(SerivceBaseAddress)} must start with /"); + //uriBuilder.Path = SerivceBaseAddress; + //Uri baseAddress = uriBuilder.Uri; + //IService service = app.UseService(baseAddress); + //service.UseServiceEndpoint(Binding, RelativeEndpointAddress); + //app.UseMiddleware(app); + } + + public abstract string SerivceBaseAddress { get; } + + public abstract Binding Binding { get; } + + public abstract string RelativeEndpointAddress { get; } +} diff --git a/src/CoreWCF.NetTcp/tests/BasicServiceTest.cs b/src/CoreWCF.NetTcp/tests/BasicServiceTest.cs new file mode 100644 index 000000000..ea1bcd10d --- /dev/null +++ b/src/CoreWCF.NetTcp/tests/BasicServiceTest.cs @@ -0,0 +1,123 @@ +using System; +using System.IO; +using System.ServiceModel; +using System.Text; +using CoreWCF.Channels; +using Xunit; +using NetTcpBinding = CoreWCF.NetTcpBinding; +using System.Diagnostics; +using System.Threading; + +public static class BasicServiceTest +{ + //[Fact] + //public static void NetTcpRequestReplyEchoString() + //{ + // string httpListeningUrl = "http://localhost:18080"; // Dummy for now until we can run without HTTP + // string netTcplisteningUrl = "net.tcp://localhost:11808"; + // string testString = new string('a', 3000); + // var host = ServiceHelper.CreateWebHost(httpListeningUrl); + + // using (host) + // { + // host.Start(); + // var netTcpBinding = new System.ServiceModel.NetTcpBinding(); + // netTcpBinding.Security.Mode = SecurityMode.None; + // var factory = new ChannelFactory(netTcpBinding, + // new EndpointAddress(new Uri(netTcplisteningUrl + "/BasicWcfService/nettcp.svc"))); + // var channel = factory.CreateChannel(); + // System.ServiceModel.Channels.IChannel ichannel = (System.ServiceModel.Channels.IChannel)channel; + // ichannel.Open(); + // var result = channel.EchoString(testString); + // Assert.Equal(testString, result); + // ichannel.Close(); + // } + //} + + //[Fact] + //public static void NetTcpRequestReplyEchoStringAsync() + //{ + // string httpListeningUrl = "http://localhost:18080"; // Dummy for now until we can run without HTTP + // string netTcplisteningUrl = "net.tcp://localhost:11808"; + // string testString = new string('a', 3000); + // var host = ServiceHelper.CreateWebHost(httpListeningUrl); + + // using (host) + // { + // host.Start(); + // var netTcpBinding = new System.ServiceModel.NetTcpBinding(); + // netTcpBinding.Security.Mode = SecurityMode.None; + // var factory = new ChannelFactory(netTcpBinding, + // new EndpointAddress(new Uri(netTcplisteningUrl + "/BasicWcfService/nettcp.svc"))); + // var channel = factory.CreateChannel(); + // System.ServiceModel.Channels.IChannel ichannel = (System.ServiceModel.Channels.IChannel)channel; + // ichannel.Open(); + // var result = channel.EchoStringAsync(testString); + // Assert.Equal(testString, result); + // } + //} + + //[Fact] + //public static void NetTcpRequestReplyEchoStream() + //{ + // string httpListeningUrl = "http://localhost:18080"; // Dummy for now until we can run without HTTP + // string netTcplisteningUrl = "net.tcp://localhost:11808"; + // string testString = new string('a', 3000); + // MemoryStream testStream = new MemoryStream(Encoding.UTF8.GetBytes(testString)); + // var host = ServiceHelper.CreateWebHost(httpListeningUrl); + + // using (host) + // { + // host.Start(); + // var netTcpBinding = new System.ServiceModel.NetTcpBinding(); + // netTcpBinding.Security.Mode = SecurityMode.None; + // var factory = new ChannelFactory(netTcpBinding, + // new EndpointAddress(new Uri(netTcplisteningUrl + "/BasicWcfService/nettcp.svc"))); + // var channel = factory.CreateChannel(); + // System.ServiceModel.Channels.IChannel ichannel = (System.ServiceModel.Channels.IChannel)channel; + // ichannel.Open(); + // var result = channel.EchoStream(testStream); + // testStream.SetLength(0); + // result.CopyTo(testStream); + // var resultString = Encoding.UTF8.GetString(testStream.ToArray()); + // Assert.Equal(testString, resultString); + // } + //} + + //[Fact] + //public static void NetTcpRequestReplyEchoStreamAsync() + //{ + // string httpListeningUrl = "http://localhost:18080"; // Dummy for now until we can run without HTTP + // string netTcplisteningUrl = "net.tcp://localhost:11808"; + // string testString = new string('a', 3000); + // MemoryStream testStream = new MemoryStream(Encoding.UTF8.GetBytes(testString)); + // var host = ServiceHelper.CreateWebHost(httpListeningUrl); + + // using (host) + // { + // host.Start(); + // var netTcpBinding = new System.ServiceModel.NetTcpBinding(); + // netTcpBinding.Security.Mode = SecurityMode.None; + // var factory = new ChannelFactory(netTcpBinding, + // new EndpointAddress(new Uri(netTcplisteningUrl + "/BasicWcfService/nettcp.svc"))); + // var channel = factory.CreateChannel(); + // System.ServiceModel.Channels.IChannel ichannel = (System.ServiceModel.Channels.IChannel)channel; + // ichannel.Open(); + // var result = channel.EchoStreamAsync(testStream); + // testStream.SetLength(0); + // result.CopyTo(testStream); + // var resultString = Encoding.UTF8.GetString(testStream.ToArray()); + // Assert.Equal(testString, resultString); + // } + //} + + //public class Startup : BaseStartup + //{ + // public override string SerivceBaseAddress => "/BasicWcfService"; + // public override Binding Binding => new NetTcpBinding + // { Security = new CoreWCF.NetTcpSecurity + // { Mode = CoreWCF.SecurityMode.None } + // }; + // public override string RelativeEndpointAddress => "nettcp.svc"; + //} +} diff --git a/src/CoreWCF.NetTcp/tests/ClientContract/IEchoService.cs b/src/CoreWCF.NetTcp/tests/ClientContract/IEchoService.cs new file mode 100644 index 000000000..d8624cb14 --- /dev/null +++ b/src/CoreWCF.NetTcp/tests/ClientContract/IEchoService.cs @@ -0,0 +1,34 @@ +using System.IO; +using System.ServiceModel; +using System.Threading.Tasks; + +namespace ClientContract +{ + internal static class Constants + { + public const string NS = "http://tempuri.org/"; + public const string ECHOSERVICE_NAME = nameof(IEchoService); + public const string OPERATION_BASE = NS + ECHOSERVICE_NAME + "/"; + } + + [ServiceContract(Namespace = "http://tempuri.org/", Name = "IEchoService")] + public interface IEchoService + { + [OperationContract(Name = "Echo", Action = Constants.OPERATION_BASE + "Echo", + ReplyAction = Constants.OPERATION_BASE + "EchoResponse")] + string EchoString(string echo); + + //[OperationContract(Name = "EchoStream", Action = Constants.OPERATION_BASE + "EchoStream", + // ReplyAction = Constants.OPERATION_BASE + "EchoStreamResponse")] + //Stream EchoStream(Stream echo); + + //[OperationContract(Name = "EchoStringAsync", Action = Constants.OPERATION_BASE + "EchoStringAsync", + // ReplyAction = Constants.OPERATION_BASE + "EchoStringAsyncResponse")] + //string EchoStringAsync(string echo); + + //[OperationContract(Name = "EchoStreamAsync", Action = Constants.OPERATION_BASE + "EchoStreamAsync", + // ReplyAction = Constants.OPERATION_BASE + "EchoStreamAsyncResponse")] + //Stream EchoStreamAsync(Stream echo); + + } +} \ No newline at end of file diff --git a/src/CoreWCF.NetTcp/tests/ConnectionHandlerTests.cs b/src/CoreWCF.NetTcp/tests/ConnectionHandlerTests.cs new file mode 100644 index 000000000..8175e9003 --- /dev/null +++ b/src/CoreWCF.NetTcp/tests/ConnectionHandlerTests.cs @@ -0,0 +1,61 @@ +using Microsoft.AspNetCore; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Connections; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using CoreWCF.Channels.Framing; +using CoreWCF.Configuration; +using System; +using System.Collections.Generic; +using System.ServiceModel.Channels; +using System.Text; +using Xunit; + +public static class ConnectionHandlerTests +{ + [Fact] + public static void NetTcpClientConnection() + { + string testString = new string('a', 3000); + var host = CreateWebHostBuilder(new string[0]).Build(); + using (host) + { + host.Start(); + var binding = new System.ServiceModel.NetTcpBinding(); + var factory = new System.ServiceModel.ChannelFactory(binding, + new System.ServiceModel.EndpointAddress(new Uri("net.tcp://localhost:8808/nettcp.svc"))); + var channel = factory.CreateChannel(); + ((IChannel)channel).Open(); + var result = channel.EchoString(testString); + Assert.Equal(testString, result); + } + } + + public static IWebHostBuilder CreateWebHostBuilder(string[] args) => + WebHost.CreateDefaultBuilder(args) + .UseNetTcp(8808) + .UseStartup(); + + public class Startup + { + public void ConfigureServices(IServiceCollection services) + { + services.AddServiceModelServices(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IHostingEnvironment env) + { + app.UseServiceModel(builder => + { + builder.AddService(); + builder.AddServiceEndpoint(new CoreWCF.NetTcpBinding(), "/nettcp.svc"); + }); + app.Run(async (context) => + { + await context.Response.WriteAsync("Hello World!"); + }); + } + } +} diff --git a/src/CoreWCF.NetTcp/tests/CoreWCF.NetTcp.Tests.csproj b/src/CoreWCF.NetTcp/tests/CoreWCF.NetTcp.Tests.csproj new file mode 100644 index 000000000..4c5fe84fd --- /dev/null +++ b/src/CoreWCF.NetTcp/tests/CoreWCF.NetTcp.Tests.csproj @@ -0,0 +1,29 @@ + + + Exe + netcoreapp2.2 + true + false + false + false + + + + + all + runtime; build; native; contentfiles; analyzers + + + + + + + + + + + + + + + diff --git a/src/CoreWCF.NetTcp/tests/ServiceContract/IEchoService.cs b/src/CoreWCF.NetTcp/tests/ServiceContract/IEchoService.cs new file mode 100644 index 000000000..8ac28f70d --- /dev/null +++ b/src/CoreWCF.NetTcp/tests/ServiceContract/IEchoService.cs @@ -0,0 +1,33 @@ +using System.IO; +using System.Threading.Tasks; +using CoreWCF; + +namespace ServiceContract +{ + internal static class Constants + { + public const string NS = "http://tempuri.org/"; + public const string ECHOSERVICE_NAME = nameof(IEchoService); + public const string OPERATION_BASE = NS + ECHOSERVICE_NAME + "/"; + } + + [ServiceContract(Namespace = Constants.NS, Name = Constants.ECHOSERVICE_NAME)] + public interface IEchoService + { + [OperationContract(Name = "Echo", Action = Constants.OPERATION_BASE + "Echo", + ReplyAction = Constants.OPERATION_BASE + "EchoResponse")] + string EchoString(string echo); + + //[OperationContract(Name = "EchoStream", Action = Constants.OPERATION_BASE + "EchoStream", + // ReplyAction = Constants.OPERATION_BASE + "EchoStreamResponse")] + //Stream EchoStream(Stream echo); + + //[OperationContract(Name = "EchoStringAsync", Action = Constants.OPERATION_BASE + "EchoStringAsync", + // ReplyAction = Constants.OPERATION_BASE + "EchoStringAsyncResponse")] + //Task EchoStringAsync(string echo); + + //[OperationContract(Name = "EchoStreamAsync", Action = Constants.OPERATION_BASE + "EchoStreamAsync", + // ReplyAction = Constants.OPERATION_BASE + "EchoStreamAsyncResponse")] + //Task EchoStreamAsync(Stream echo); + } +} \ No newline at end of file diff --git a/src/CoreWCF.NetTcp/tests/ServiceHelper.cs b/src/CoreWCF.NetTcp/tests/ServiceHelper.cs new file mode 100644 index 000000000..349695104 --- /dev/null +++ b/src/CoreWCF.NetTcp/tests/ServiceHelper.cs @@ -0,0 +1,17 @@ +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; + +public static class ServiceHelper +{ + public static IWebHost CreateWebHost(params string[] urls) where TStartup : class + { + var config = new ConfigurationBuilder().Build(); + var builder = new WebHostBuilder() + .UseConfiguration(config) + .UseStartup() + .UseKestrel() + .UseUrls(urls); + + return builder.Build(); + } +} diff --git a/src/CoreWCF.NetTcp/tests/Services/EchoService.cs b/src/CoreWCF.NetTcp/tests/Services/EchoService.cs new file mode 100644 index 000000000..a6d033aa2 --- /dev/null +++ b/src/CoreWCF.NetTcp/tests/Services/EchoService.cs @@ -0,0 +1,35 @@ +using System.IO; +using System.Threading.Tasks; + +namespace Services +{ + public class EchoService : ServiceContract.IEchoService + { + public string EchoString(string echo) + { + return echo; + } + + public Stream EchoStream(Stream echo) + { + var stream = new MemoryStream(); + echo.CopyTo(stream); + stream.Position = 0; + return stream; + } + + public async Task EchoStringAsync(string echo) + { + await Task.Yield(); + return echo; + } + + public async Task EchoStreamAsync(Stream echo) + { + var stream = new MemoryStream(); + await echo.CopyToAsync(stream); + stream.Position = 0; + return stream; + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.NetTcp/tests/XunitTestAssemblyAttribute.cs b/src/CoreWCF.NetTcp/tests/XunitTestAssemblyAttribute.cs new file mode 100644 index 000000000..13a3fb3ad --- /dev/null +++ b/src/CoreWCF.NetTcp/tests/XunitTestAssemblyAttribute.cs @@ -0,0 +1,4 @@ +using Xunit; + +// Registry tests can conflict with each other due to accessing the same keys/values in the registry +[assembly: CollectionBehavior(DisableTestParallelization = true)] diff --git a/src/CoreWCF.Primitives/src/CoreWCF.Primitives.csproj b/src/CoreWCF.Primitives/src/CoreWCF.Primitives.csproj new file mode 100644 index 000000000..186d69e8e --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF.Primitives.csproj @@ -0,0 +1,16 @@ + + + netcoreapp2.2 + CoreWCF.Primitives + CoreWCF.Primitives + True + + + + + + + + + + \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/ActionMismatchAddressingException.cs b/src/CoreWCF.Primitives/src/CoreWCF/ActionMismatchAddressingException.cs new file mode 100644 index 000000000..277c32fc8 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/ActionMismatchAddressingException.cs @@ -0,0 +1,50 @@ +using CoreWCF.Runtime; +using CoreWCF.Channels; + +namespace CoreWCF +{ + //[Serializable] + internal class ActionMismatchAddressingException : ProtocolException + { + string httpActionHeader; + string soapActionHeader; + + public ActionMismatchAddressingException(string message, string soapActionHeader, string httpActionHeader) + : base(message) + { + this.httpActionHeader = httpActionHeader; + this.soapActionHeader = soapActionHeader; + } + + //protected ActionMismatchAddressingException(SerializationInfo info, StreamingContext context) + // : base(info, context) + //{ + //} + + public string HttpActionHeader + { + get + { + return httpActionHeader; + } + } + + public string SoapActionHeader + { + get + { + return soapActionHeader; + } + } + + internal Message ProvideFault(MessageVersion messageVersion) + { + Fx.Assert(messageVersion.Addressing == AddressingVersion.WSAddressing10, ""); + WSAddressing10ProblemHeaderQNameFault phf = new WSAddressing10ProblemHeaderQNameFault(this); + Message message = CoreWCF.Channels.Message.CreateMessage(messageVersion, phf, messageVersion.Addressing.FaultAction); + phf.AddHeaders(message.Headers); + return message; + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/ActionNotSupportedException.cs b/src/CoreWCF.Primitives/src/CoreWCF/ActionNotSupportedException.cs new file mode 100644 index 000000000..0a6e17b32 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/ActionNotSupportedException.cs @@ -0,0 +1,25 @@ +using System; +using System.Runtime.Serialization; +using CoreWCF.Runtime; +using CoreWCF.Channels; + +namespace CoreWCF +{ + //[Serializable] + public class ActionNotSupportedException : CommunicationException + { + public ActionNotSupportedException() { } + public ActionNotSupportedException(string message) : base(message) { } + public ActionNotSupportedException(string message, Exception innerException) : base(message, innerException) { } + // protected ActionNotSupportedException(SerializationInfo info, StreamingContext context) : base(info, context) { } + + internal Message ProvideFault(MessageVersion messageVersion) + { + Fx.Assert(messageVersion.Addressing != AddressingVersion.None, ""); + FaultCode code = FaultCode.CreateSenderFaultCode(AddressingStrings.ActionNotSupported, messageVersion.Addressing.Namespace); + string reason = Message; + return CoreWCF.Channels.Message.CreateMessage( + messageVersion, code, reason, messageVersion.Addressing.FaultAction); + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/AddressAlreadyInUseException.cs b/src/CoreWCF.Primitives/src/CoreWCF/AddressAlreadyInUseException.cs new file mode 100644 index 000000000..1691eb84d --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/AddressAlreadyInUseException.cs @@ -0,0 +1,14 @@ +using System; +using System.Runtime.Serialization; + +namespace CoreWCF +{ + [Serializable] + public class AddressAlreadyInUseException : CommunicationException + { + public AddressAlreadyInUseException() { } + public AddressAlreadyInUseException(string message) : base(message) { } + public AddressAlreadyInUseException(string message, Exception innerException) : base(message, innerException) { } + protected AddressAlreadyInUseException(SerializationInfo info, StreamingContext context) : base(info, context) { } + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/AddressFilterMode.cs b/src/CoreWCF.Primitives/src/CoreWCF/AddressFilterMode.cs new file mode 100644 index 000000000..505dbfb50 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/AddressFilterMode.cs @@ -0,0 +1,20 @@ +namespace CoreWCF +{ + public enum AddressFilterMode + { + Exact, // AddressFilterMode.default + Prefix, + Any, + } + + static class AddressFilterModeHelper + { + static public bool IsDefined(AddressFilterMode x) + { + return + x == AddressFilterMode.Exact || + x == AddressFilterMode.Prefix || + x == AddressFilterMode.Any; + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/CallbackBehaviorAttribute.cs b/src/CoreWCF.Primitives/src/CoreWCF/CallbackBehaviorAttribute.cs new file mode 100644 index 000000000..79005867d --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/CallbackBehaviorAttribute.cs @@ -0,0 +1,13 @@ +namespace CoreWCF +{ + public sealed class CallbackBehaviorAttribute : System.Attribute, Description.IEndpointBehavior + { + public CallbackBehaviorAttribute() { } + public bool AutomaticSessionShutdown { get { return default(bool); } set { } } + public bool UseSynchronizationContext { get { return default(bool); } set { } } + void Description.IEndpointBehavior.AddBindingParameters(Description.ServiceEndpoint serviceEndpoint, Channels.BindingParameterCollection parameters) { } + void CoreWCF.Description.IEndpointBehavior.ApplyClientBehavior(CoreWCF.Description.ServiceEndpoint serviceEndpoint, CoreWCF.Dispatcher.ClientRuntime clientRuntime) { } + void Description.IEndpointBehavior.ApplyDispatchBehavior(Description.ServiceEndpoint serviceEndpoint, Dispatcher.EndpointDispatcher endpointDispatcher) { } + void Description.IEndpointBehavior.Validate(Description.ServiceEndpoint serviceEndpoint) { } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/ChannelTerminatedException.cs b/src/CoreWCF.Primitives/src/CoreWCF/ChannelTerminatedException.cs new file mode 100644 index 000000000..c2ab72082 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/ChannelTerminatedException.cs @@ -0,0 +1,13 @@ +using System; + +namespace CoreWCF +{ + //[Serializable] + internal class ChannelTerminatedException : CommunicationException + { + public ChannelTerminatedException() { } + public ChannelTerminatedException(string message) : base(message) { } + public ChannelTerminatedException(string message, Exception innerException) : base(message, innerException) { } + //protected ChannelTerminatedException(SerializationInfo info, StreamingContext context) : base(info, context) { } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/AddressHeader.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/AddressHeader.cs new file mode 100644 index 000000000..aeacf3415 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/AddressHeader.cs @@ -0,0 +1,379 @@ +using System; +using System.Runtime.Serialization; +using System.Text; +using System.Xml; +using CoreWCF.Runtime; +using CoreWCF.Dispatcher; + +namespace CoreWCF.Channels +{ + public abstract class AddressHeader + { + ParameterHeader header; + + protected AddressHeader() + { + } + + internal bool IsReferenceProperty + { + get + { + BufferedAddressHeader bah = this as BufferedAddressHeader; + return bah != null && bah.IsReferencePropertyHeader; + } + } + + public abstract string Name { get; } + public abstract string Namespace { get; } + + //public static AddressHeader CreateAddressHeader(object value) + //{ + // Type type = GetObjectType(value); + // return CreateAddressHeader(value, DataContractSerializerDefaults.CreateSerializer(type, int.MaxValue/*maxItems*/)); + //} + + //public static AddressHeader CreateAddressHeader(object value, XmlObjectSerializer serializer) + //{ + // if (serializer == null) + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("serializer")); + // return new XmlObjectSerializerAddressHeader(value, serializer); + //} + + public static AddressHeader CreateAddressHeader(string name, string ns, object value) + { + return CreateAddressHeader(name, ns, value, DataContractSerializerDefaults.CreateSerializer(GetObjectType(value), name, ns, int.MaxValue/*maxItems*/)); + } + + internal static AddressHeader CreateAddressHeader(XmlDictionaryString name, XmlDictionaryString ns, object value) + { + return new DictionaryAddressHeader(name, ns, value); + } + + public static AddressHeader CreateAddressHeader(string name, string ns, object value, XmlObjectSerializer serializer) + { + if (serializer == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(serializer)); + return new XmlObjectSerializerAddressHeader(name, ns, value, serializer); + } + + static Type GetObjectType(object value) + { + return (value == null) ? typeof(object) : value.GetType(); + } + + public override bool Equals(object obj) + { + AddressHeader hdr = obj as AddressHeader; + if (hdr == null) + return false; + + StringBuilder builder = new StringBuilder(); + string hdr1 = GetComparableForm(builder); + + builder.Remove(0, builder.Length); + string hdr2 = hdr.GetComparableForm(builder); + + if (hdr1.Length != hdr2.Length) + return false; + + if (string.CompareOrdinal(hdr1, hdr2) != 0) + return false; + + return true; + } + + internal string GetComparableForm() + { + return GetComparableForm(new StringBuilder()); + } + + internal string GetComparableForm(StringBuilder builder) + { + return EndpointAddressProcessor.GetComparableForm(builder, GetComparableReader()); + } + + public override int GetHashCode() + { + return GetComparableForm().GetHashCode(); + } + + public T GetValue() + { + return GetValue(DataContractSerializerDefaults.CreateSerializer(typeof(T), Name, Namespace, int.MaxValue/*maxItems*/)); + } + + public T GetValue(XmlObjectSerializer serializer) + { + if (serializer == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(serializer)); + using (XmlDictionaryReader reader = GetAddressHeaderReader()) + { + if (serializer.IsStartObject(reader)) + return (T)serializer.ReadObject(reader); + else + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.Format(SR.ExpectedElementMissing, Name, Namespace))); + } + } + + public virtual XmlDictionaryReader GetAddressHeaderReader() + { + XmlBuffer buffer = new XmlBuffer(int.MaxValue); + XmlDictionaryWriter writer = buffer.OpenSection(XmlDictionaryReaderQuotas.Max); + WriteAddressHeader(writer); + buffer.CloseSection(); + buffer.Close(); + return buffer.GetReader(0); + } + + XmlDictionaryReader GetComparableReader() + { + XmlBuffer buffer = new XmlBuffer(int.MaxValue); + XmlDictionaryWriter writer = buffer.OpenSection(XmlDictionaryReaderQuotas.Max); + // WSAddressingAugust2004 does not write the IsReferenceParameter attribute, + // and that's good for a consistent comparable form. But it's not available for .Net Core + ParameterHeader.WriteStartHeader(writer, this, AddressingVersion.WSAddressing10); + //ParameterHeader.WriteStartHeader(writer, this, AddressingVersion.WSAddressingAugust2004); + ParameterHeader.WriteHeaderContents(writer, this); + writer.WriteEndElement(); + buffer.CloseSection(); + buffer.Close(); + return buffer.GetReader(0); + } + + protected virtual void OnWriteStartAddressHeader(XmlDictionaryWriter writer) + { + writer.WriteStartElement(Name, Namespace); + } + + protected abstract void OnWriteAddressHeaderContents(XmlDictionaryWriter writer); + + public MessageHeader ToMessageHeader() + { + if (header == null) + header = new ParameterHeader(this); + return header; + } + + public void WriteAddressHeader(XmlWriter writer) + { + WriteAddressHeader(XmlDictionaryWriter.CreateDictionaryWriter(writer)); + } + + public void WriteAddressHeader(XmlDictionaryWriter writer) + { + if (writer == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(writer)); + WriteStartAddressHeader(writer); + WriteAddressHeaderContents(writer); + writer.WriteEndElement(); + } + + public void WriteStartAddressHeader(XmlDictionaryWriter writer) + { + if (writer == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(writer)); + OnWriteStartAddressHeader(writer); + } + + public void WriteAddressHeaderContents(XmlDictionaryWriter writer) + { + if (writer == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(writer)); + OnWriteAddressHeaderContents(writer); + } + + class ParameterHeader : MessageHeader + { + AddressHeader parameter; + + public override bool IsReferenceParameter + { + get { return true; } + } + + public override string Name + { + get { return parameter.Name; } + } + + public override string Namespace + { + get { return parameter.Namespace; } + } + + public ParameterHeader(AddressHeader parameter) + { + this.parameter = parameter; + } + + protected override void OnWriteStartHeader(XmlDictionaryWriter writer, MessageVersion messageVersion) + { + if (messageVersion == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(messageVersion)); + + WriteStartHeader(writer, parameter, messageVersion.Addressing); + } + + protected override void OnWriteHeaderContents(XmlDictionaryWriter writer, MessageVersion messageVersion) + { + WriteHeaderContents(writer, parameter); + } + + internal static void WriteStartHeader(XmlDictionaryWriter writer, AddressHeader parameter, AddressingVersion addressingVersion) + { + parameter.WriteStartAddressHeader(writer); + if (addressingVersion == AddressingVersion.WSAddressing10) + { + writer.WriteAttributeString(XD.AddressingDictionary.IsReferenceParameter, XD.Addressing10Dictionary.Namespace, "true"); + } + } + + internal static void WriteHeaderContents(XmlDictionaryWriter writer, AddressHeader parameter) + { + parameter.WriteAddressHeaderContents(writer); + } + } + + class XmlObjectSerializerAddressHeader : AddressHeader + { + XmlObjectSerializer serializer; + object objectToSerialize; + string name; + string ns; + + public XmlObjectSerializerAddressHeader(object objectToSerialize, XmlObjectSerializer serializer) + { + this.serializer = serializer; + this.objectToSerialize = objectToSerialize; + + throw new PlatformNotSupportedException(); + + //Type type = (objectToSerialize == null) ? typeof(object) : objectToSerialize.GetType(); + //XmlQualifiedName rootName = new XsdDataContractExporter().GetRootElementName(type); + //this.name = rootName.Name; + //this.ns = rootName.Namespace; + } + + public XmlObjectSerializerAddressHeader(string name, string ns, object objectToSerialize, XmlObjectSerializer serializer) + { + if ((null == name) || (name.Length == 0)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(name)); + } + + this.serializer = serializer; + this.objectToSerialize = objectToSerialize; + this.name = name; + this.ns = ns; + } + + public override string Name + { + get { return name; } + } + + public override string Namespace + { + get { return ns; } + } + + object ThisLock + { + get { return this; } + } + + protected override void OnWriteAddressHeaderContents(XmlDictionaryWriter writer) + { + lock (ThisLock) + { + serializer.WriteObjectContent(writer, objectToSerialize); + } + } + } + + // astern, This will be kept internal for now. If the optimization needs to be public, we'll re-evaluate it. + class DictionaryAddressHeader : XmlObjectSerializerAddressHeader + { + XmlDictionaryString name; + XmlDictionaryString ns; + + public DictionaryAddressHeader(XmlDictionaryString name, XmlDictionaryString ns, object value) + : base(name.Value, ns.Value, value, DataContractSerializerDefaults.CreateSerializer(GetObjectType(value), name, ns, int.MaxValue/*maxItems*/)) + { + this.name = name; + this.ns = ns; + } + + protected override void OnWriteStartAddressHeader(XmlDictionaryWriter writer) + { + writer.WriteStartElement(name, ns); + } + } + } + + class BufferedAddressHeader : AddressHeader + { + string name; + string ns; + XmlBuffer buffer; + bool isReferenceProperty; + + public BufferedAddressHeader(XmlDictionaryReader reader) + { + buffer = new XmlBuffer(int.MaxValue); + XmlDictionaryWriter writer = buffer.OpenSection(reader.Quotas); + Fx.Assert(reader.NodeType == XmlNodeType.Element, ""); + name = reader.LocalName; + ns = reader.NamespaceURI; + Fx.Assert(name != null, ""); + Fx.Assert(ns != null, ""); + writer.WriteNode(reader, false); + buffer.CloseSection(); + buffer.Close(); + isReferenceProperty = false; + } + + public BufferedAddressHeader(XmlDictionaryReader reader, bool isReferenceProperty) + : this(reader) + { + this.isReferenceProperty = isReferenceProperty; + } + + public bool IsReferencePropertyHeader { get { return isReferenceProperty; } } + + public override string Name + { + get { return name; } + } + + public override string Namespace + { + get { return ns; } + } + + public override XmlDictionaryReader GetAddressHeaderReader() + { + return buffer.GetReader(0); + } + + protected override void OnWriteStartAddressHeader(XmlDictionaryWriter writer) + { + XmlDictionaryReader reader = GetAddressHeaderReader(); + writer.WriteStartElement(reader.Prefix, reader.LocalName, reader.NamespaceURI); + writer.WriteAttributes(reader, false); + reader.Dispose(); + } + + protected override void OnWriteAddressHeaderContents(XmlDictionaryWriter writer) + { + XmlDictionaryReader reader = GetAddressHeaderReader(); + reader.ReadStartElement(); + while (reader.NodeType != XmlNodeType.EndElement) + writer.WriteNode(reader, false); + reader.ReadEndElement(); + reader.Dispose(); + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/AddressHeaderCollection.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/AddressHeaderCollection.cs new file mode 100644 index 000000000..b4e069d57 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/AddressHeaderCollection.cs @@ -0,0 +1,188 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Xml; + +namespace CoreWCF.Channels +{ + public sealed class AddressHeaderCollection : System.Collections.ObjectModel.ReadOnlyCollection + { + static AddressHeaderCollection emptyHeaderCollection = new AddressHeaderCollection(); + + public AddressHeaderCollection() + : base(new List()) + { + } + + public AddressHeaderCollection(IEnumerable addressHeaders) + : base(new List(addressHeaders)) + { + // avoid allocating an enumerator when possible + IList collection = addressHeaders as IList; + if (collection != null) + { + for (int i = 0; i < collection.Count; i++) + { + if (collection[i] == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new ArgumentException(SR.MessageHeaderIsNull0)); + } + } + else + { + foreach (AddressHeader addressHeader in addressHeaders) + { + if (addressHeader == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new ArgumentException(SR.MessageHeaderIsNull0)); + } + } + } + + int InternalCount + { + get + { + if (this == (object)emptyHeaderCollection) + return 0; + return Count; + } + } + + public void AddHeadersTo(Message message) + { + if (message == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("message"); + + for (int i = 0; i < InternalCount; i++) + { + message.Headers.Add(this[i].ToMessageHeader()); + } + } + + public AddressHeader[] FindAll(string name, string ns) + { + if (name == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(name)); + if (ns == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(ns)); + + List results = new List(); + for (int i = 0; i < Count; i++) + { + AddressHeader header = this[i]; + if (header.Name == name && header.Namespace == ns) + { + results.Add(header); + } + } + + return results.ToArray(); + } + + public AddressHeader FindHeader(string name, string ns) + { + if (name == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(name)); + if (ns == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(ns)); + + AddressHeader matchingHeader = null; + + for (int i = 0; i < Count; i++) + { + AddressHeader header = this[i]; + if (header.Name == name && header.Namespace == ns) + { + if (matchingHeader != null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.Format(SR.MultipleMessageHeaders, name, ns))); + matchingHeader = header; + } + } + + return matchingHeader; + } + + internal bool IsEquivalent(AddressHeaderCollection col) + { + if (InternalCount != col.InternalCount) + return false; + + StringBuilder builder = new StringBuilder(); + Dictionary myHeaders = new Dictionary(); + PopulateHeaderDictionary(builder, myHeaders); + + Dictionary otherHeaders = new Dictionary(); + col.PopulateHeaderDictionary(builder, otherHeaders); + + if (myHeaders.Count != otherHeaders.Count) + return false; + + foreach (KeyValuePair pair in myHeaders) + { + int count; + if (otherHeaders.TryGetValue(pair.Key, out count)) + { + if (count != pair.Value) + return false; + } + else + { + return false; + } + } + + return true; + } + + internal void PopulateHeaderDictionary(StringBuilder builder, Dictionary headers) + { + string key; + for (int i = 0; i < InternalCount; ++i) + { + builder.Remove(0, builder.Length); + key = this[i].GetComparableForm(builder); + if (headers.ContainsKey(key)) + { + headers[key] = headers[key] + 1; + } + else + { + headers.Add(key, 1); + } + } + } + + internal static AddressHeaderCollection ReadServiceParameters(XmlDictionaryReader reader) + { + return ReadServiceParameters(reader, false); + } + + internal static AddressHeaderCollection ReadServiceParameters(XmlDictionaryReader reader, bool isReferenceProperty) + { + reader.MoveToContent(); + if (reader.IsEmptyElement) + { + reader.Skip(); + return null; + } + else + { + reader.ReadStartElement(); + List headerList = new List(); + while (reader.IsStartElement()) + { + headerList.Add(new BufferedAddressHeader(reader, isReferenceProperty)); + } + reader.ReadEndElement(); + return new AddressHeaderCollection(headerList); + } + } + + internal void WriteContentsTo(XmlDictionaryWriter writer) + { + for (int i = 0; i < InternalCount; i++) + this[i].WriteAddressHeader(writer); + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/Addressing.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/Addressing.cs new file mode 100644 index 000000000..5d60acbf5 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/Addressing.cs @@ -0,0 +1,917 @@ +using System; +using System.Xml; +using CoreWCF.Runtime; + +namespace CoreWCF.Channels +{ + // TODO: This needed to be made public for NetTcp, investigate making it internal again + public abstract class AddressingHeader : DictionaryHeader, IMessageHeaderWithSharedNamespace + { + AddressingVersion version; + + protected AddressingHeader(AddressingVersion version) + { + this.version = version; + } + + internal AddressingVersion Version + { + get { return version; } + } + + XmlDictionaryString IMessageHeaderWithSharedNamespace.SharedPrefix + { + get { return XD.AddressingDictionary.Prefix; } + } + + XmlDictionaryString IMessageHeaderWithSharedNamespace.SharedNamespace + { + get { return version.DictionaryNamespace; } + } + + public override XmlDictionaryString DictionaryNamespace + { + get { return version.DictionaryNamespace; } + } + } + + class ActionHeader : AddressingHeader + { + string action; + const bool mustUnderstandValue = true; + + ActionHeader(string action, AddressingVersion version) + : base(version) + { + this.action = action; + } + + public string Action + { + get { return action; } + } + + public override bool MustUnderstand + { + get { return mustUnderstandValue; } + } + + public override XmlDictionaryString DictionaryName + { + get { return XD.AddressingDictionary.Action; } + } + + public static ActionHeader Create(string action, AddressingVersion addressingVersion) + { + if (action == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("action"); + if (addressingVersion == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("addressingVersion"); + return new ActionHeader(action, addressingVersion); + } + + public static ActionHeader Create(XmlDictionaryString dictionaryAction, AddressingVersion addressingVersion) + { + if (dictionaryAction == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("action"); + if (addressingVersion == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("addressingVersion"); + return new DictionaryActionHeader(dictionaryAction, addressingVersion); + } + + protected override void OnWriteHeaderContents(XmlDictionaryWriter writer, MessageVersion messageVersion) + { + writer.WriteString(action); + } + + public static string ReadHeaderValue(XmlDictionaryReader reader, AddressingVersion addressingVersion) + { + Fx.Assert(reader.IsStartElement(XD.AddressingDictionary.Action, addressingVersion.DictionaryNamespace), ""); + string act = reader.ReadElementContentAsString(); + + if (act.Length > 0 && (act[0] <= 32 || act[act.Length - 1] <= 32)) + act = XmlUtil.Trim(act); + + return act; + } + + public static ActionHeader ReadHeader(XmlDictionaryReader reader, AddressingVersion version, + string actor, bool mustUnderstand, bool relay) + { + string action = ReadHeaderValue(reader, version); + + if (actor.Length == 0 && mustUnderstand == mustUnderstandValue && !relay) + { + return new ActionHeader(action, version); + } + else + { + return new FullActionHeader(action, actor, mustUnderstand, relay, version); + } + } + + class DictionaryActionHeader : ActionHeader + { + XmlDictionaryString dictionaryAction; + + public DictionaryActionHeader(XmlDictionaryString dictionaryAction, AddressingVersion version) + : base(dictionaryAction.Value, version) + { + this.dictionaryAction = dictionaryAction; + } + + protected override void OnWriteHeaderContents(XmlDictionaryWriter writer, MessageVersion messageVersion) + { + writer.WriteString(dictionaryAction); + } + } + + class FullActionHeader : ActionHeader + { + string actor; + bool mustUnderstand; + bool relay; + + public FullActionHeader(string action, string actor, bool mustUnderstand, bool relay, AddressingVersion version) + : base(action, version) + { + this.actor = actor; + this.mustUnderstand = mustUnderstand; + this.relay = relay; + } + + public override string Actor + { + get { return actor; } + } + + public override bool MustUnderstand + { + get { return mustUnderstand; } + } + + public override bool Relay + { + get { return relay; } + } + } + } + + class FromHeader : AddressingHeader + { + EndpointAddress from; + const bool mustUnderstandValue = false; + + FromHeader(EndpointAddress from, AddressingVersion version) + : base(version) + { + this.from = from; + } + + public EndpointAddress From + { + get { return from; } + } + + public override XmlDictionaryString DictionaryName + { + get { return XD.AddressingDictionary.From; } + } + + public override bool MustUnderstand + { + get { return mustUnderstandValue; } + } + + public static FromHeader Create(EndpointAddress from, AddressingVersion addressingVersion) + { + if (from == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("from"); + if (addressingVersion == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("addressingVersion"); + return new FromHeader(from, addressingVersion); + } + + protected override void OnWriteHeaderContents(XmlDictionaryWriter writer, MessageVersion messageVersion) + { + from.WriteContentsTo(Version, writer); + } + + public static FromHeader ReadHeader(XmlDictionaryReader reader, AddressingVersion version, + string actor, bool mustUnderstand, bool relay) + { + EndpointAddress from = ReadHeaderValue(reader, version); + + if (actor.Length == 0 && mustUnderstand == mustUnderstandValue && !relay) + { + return new FromHeader(from, version); + } + else + { + return new FullFromHeader(from, actor, mustUnderstand, relay, version); + } + } + + public static EndpointAddress ReadHeaderValue(XmlDictionaryReader reader, AddressingVersion addressingVersion) + { + Fx.Assert(reader.IsStartElement(XD.AddressingDictionary.From, addressingVersion.DictionaryNamespace), ""); + return EndpointAddress.ReadFrom(addressingVersion, reader); + } + + class FullFromHeader : FromHeader + { + string actor; + bool mustUnderstand; + bool relay; + + public FullFromHeader(EndpointAddress from, string actor, bool mustUnderstand, bool relay, AddressingVersion version) + : base(from, version) + { + this.actor = actor; + this.mustUnderstand = mustUnderstand; + this.relay = relay; + } + + public override string Actor + { + get { return actor; } + } + + public override bool MustUnderstand + { + get { return mustUnderstand; } + } + + public override bool Relay + { + get { return relay; } + } + } + } + + class FaultToHeader : AddressingHeader + { + EndpointAddress faultTo; + const bool mustUnderstandValue = false; + + FaultToHeader(EndpointAddress faultTo, AddressingVersion version) + : base(version) + { + this.faultTo = faultTo; + } + + public EndpointAddress FaultTo + { + get { return faultTo; } + } + + public override XmlDictionaryString DictionaryName + { + get { return XD.AddressingDictionary.FaultTo; } + } + + public override bool MustUnderstand + { + get { return mustUnderstandValue; } + } + + protected override void OnWriteHeaderContents(XmlDictionaryWriter writer, MessageVersion messageVersion) + { + faultTo.WriteContentsTo(Version, writer); + } + + public static FaultToHeader Create(EndpointAddress faultTo, AddressingVersion addressingVersion) + { + if (faultTo == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("faultTo"); + if (addressingVersion == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("addressingVersion"); + return new FaultToHeader(faultTo, addressingVersion); + } + + public static FaultToHeader ReadHeader(XmlDictionaryReader reader, AddressingVersion version, + string actor, bool mustUnderstand, bool relay) + { + EndpointAddress faultTo = ReadHeaderValue(reader, version); + + if (actor.Length == 0 && mustUnderstand == mustUnderstandValue && !relay) + { + return new FaultToHeader(faultTo, version); + } + else + { + return new FullFaultToHeader(faultTo, actor, mustUnderstand, relay, version); + } + } + + public static EndpointAddress ReadHeaderValue(XmlDictionaryReader reader, AddressingVersion version) + { + Fx.Assert(reader.IsStartElement(XD.AddressingDictionary.FaultTo, version.DictionaryNamespace), ""); + return EndpointAddress.ReadFrom(version, reader); + } + + class FullFaultToHeader : FaultToHeader + { + string actor; + bool mustUnderstand; + bool relay; + + public FullFaultToHeader(EndpointAddress faultTo, string actor, bool mustUnderstand, bool relay, AddressingVersion version) + : base(faultTo, version) + { + this.actor = actor; + this.mustUnderstand = mustUnderstand; + this.relay = relay; + } + + public override string Actor + { + get { return actor; } + } + + public override bool MustUnderstand + { + get { return mustUnderstand; } + } + + public override bool Relay + { + get { return relay; } + } + } + } + + // TODO: This needed to be made public for NetTcp, investigate making it internal again + public class ToHeader : AddressingHeader + { + Uri to; + const bool mustUnderstandValue = true; + + static ToHeader anonymousToHeader10; + //static ToHeader anonymousToHeader200408; + + protected ToHeader(Uri to, AddressingVersion version) + : base(version) + { + this.to = to; + } + + static ToHeader AnonymousTo10 + { + get + { + if (anonymousToHeader10 == null) + anonymousToHeader10 = new AnonymousToHeader(AddressingVersion.WSAddressing10); + return anonymousToHeader10; + } + } + + //static ToHeader AnonymousTo200408 + //{ + // get + // { + // if (anonymousToHeader200408 == null) + // anonymousToHeader200408 = new AnonymousToHeader(AddressingVersion.WSAddressingAugust2004); + // return anonymousToHeader200408; + // } + //} + + public override XmlDictionaryString DictionaryName + { + get { return XD.AddressingDictionary.To; } + } + + public override bool MustUnderstand + { + get { return mustUnderstandValue; } + } + + public Uri To + { + get { return to; } + } + + public static ToHeader Create(Uri toUri, XmlDictionaryString dictionaryTo, AddressingVersion addressingVersion) + { + if (addressingVersion == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("addressingVersion"); + + if (((object)toUri == (object)addressingVersion.AnonymousUri)) + { + if (addressingVersion == AddressingVersion.WSAddressing10) + return AnonymousTo10; + else + //return AnonymousTo200408; + throw new PlatformNotSupportedException($"Unsupported adressing version {addressingVersion.ToString()}"); + } + else + { + return new DictionaryToHeader(toUri, dictionaryTo, addressingVersion); + } + } + + public static ToHeader Create(Uri to, AddressingVersion addressingVersion) + { + if ((object)to == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("to"); + } + else if ((object)to == (object)addressingVersion.AnonymousUri) + { + if (addressingVersion == AddressingVersion.WSAddressing10) + return AnonymousTo10; + else + throw new PlatformNotSupportedException($"Unsupported addressing version {addressingVersion.ToString()}"); + //return AnonymousTo200408; + } + else + { + return new ToHeader(to, addressingVersion); + } + } + + protected override void OnWriteHeaderContents(XmlDictionaryWriter writer, MessageVersion messageVersion) + { + writer.WriteString(to.AbsoluteUri); + } + + public static Uri ReadHeaderValue(XmlDictionaryReader reader, AddressingVersion version) + { + return ReadHeaderValue(reader, version, null); + } + + internal static Uri ReadHeaderValue(XmlDictionaryReader reader, AddressingVersion version, UriCache uriCache) + { + Fx.Assert(reader.IsStartElement(XD.AddressingDictionary.To, version.DictionaryNamespace), ""); + + string toString = reader.ReadElementContentAsString(); + + if ((object)toString == (object)version.Anonymous) + { + return version.AnonymousUri; + } + + if (uriCache == null) + { + return new Uri(toString); + } + + return uriCache.CreateUri(toString); + } + + internal static ToHeader ReadHeader(XmlDictionaryReader reader, AddressingVersion version, UriCache uriCache, + string actor, bool mustUnderstand, bool relay) + { + Uri to = ReadHeaderValue(reader, version, uriCache); + + if (actor.Length == 0 && mustUnderstand == mustUnderstandValue && !relay) + { + if ((object)to == (object)version.AnonymousUri) + { + if (version == AddressingVersion.WSAddressing10) + return AnonymousTo10; + else + throw new PlatformNotSupportedException($"Unsupported addressing version {version}"); + //return AnonymousTo200408; + } + else + { + return new ToHeader(to, version); + } + } + else + { + return new FullToHeader(to, actor, mustUnderstand, relay, version); + } + } + + class AnonymousToHeader : ToHeader + { + public AnonymousToHeader(AddressingVersion version) + : base(version.AnonymousUri, version) + { + } + + protected override void OnWriteHeaderContents(XmlDictionaryWriter writer, MessageVersion messageVersion) + { + writer.WriteString(Version.DictionaryAnonymous); + } + } + + class DictionaryToHeader : ToHeader + { + XmlDictionaryString dictionaryTo; + + public DictionaryToHeader(Uri to, XmlDictionaryString dictionaryTo, AddressingVersion version) + : base(to, version) + { + this.dictionaryTo = dictionaryTo; + } + + protected override void OnWriteHeaderContents(XmlDictionaryWriter writer, MessageVersion messageVersion) + { + writer.WriteString(dictionaryTo); + } + } + + class FullToHeader : ToHeader + { + string actor; + bool mustUnderstand; + bool relay; + + public FullToHeader(Uri to, string actor, bool mustUnderstand, bool relay, AddressingVersion version) + : base(to, version) + { + this.actor = actor; + this.mustUnderstand = mustUnderstand; + this.relay = relay; + } + + public override string Actor + { + get { return actor; } + } + + public override bool MustUnderstand + { + get { return mustUnderstand; } + } + + public override bool Relay + { + get { return relay; } + } + } + } + + class ReplyToHeader : AddressingHeader + { + EndpointAddress replyTo; + const bool mustUnderstandValue = false; + static ReplyToHeader anonymousReplyToHeader10; + + ReplyToHeader(EndpointAddress replyTo, AddressingVersion version) + : base(version) + { + this.replyTo = replyTo; + } + + public EndpointAddress ReplyTo + { + get { return replyTo; } + } + + public override XmlDictionaryString DictionaryName + { + get { return XD.AddressingDictionary.ReplyTo; } + } + + public override bool MustUnderstand + { + get { return mustUnderstandValue; } + } + + public static ReplyToHeader AnonymousReplyTo10 + { + get + { + if (anonymousReplyToHeader10 == null) + anonymousReplyToHeader10 = new ReplyToHeader(EndpointAddress.AnonymousAddress, AddressingVersion.WSAddressing10); + return anonymousReplyToHeader10; + } + } + + //public static ReplyToHeader AnonymousReplyTo200408 + //{ + // get + // { + // if (anonymousReplyToHeader200408 == null) + // anonymousReplyToHeader200408 = new ReplyToHeader(EndpointAddress.AnonymousAddress, AddressingVersion.WSAddressingAugust2004); + // return anonymousReplyToHeader200408; + // } + //} + + public static ReplyToHeader Create(EndpointAddress replyTo, AddressingVersion addressingVersion) + { + if (replyTo == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("replyTo"); + if (addressingVersion == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("addressingVersion"); + return new ReplyToHeader(replyTo, addressingVersion); + } + + protected override void OnWriteHeaderContents(XmlDictionaryWriter writer, MessageVersion messageVersion) + { + replyTo.WriteContentsTo(Version, writer); + } + + public static ReplyToHeader ReadHeader(XmlDictionaryReader reader, AddressingVersion version, + string actor, bool mustUnderstand, bool relay) + { + EndpointAddress replyTo = ReadHeaderValue(reader, version); + + if (actor.Length == 0 && mustUnderstand == mustUnderstandValue && !relay) + { + if ((object)replyTo == (object)EndpointAddress.AnonymousAddress) + { + if (version == AddressingVersion.WSAddressing10) + return AnonymousReplyTo10; + else + //return AnonymousReplyTo200408; + throw new PlatformNotSupportedException($"Addressing version {version.ToString()} not supported"); + } + return new ReplyToHeader(replyTo, version); + } + else + { + return new FullReplyToHeader(replyTo, actor, mustUnderstand, relay, version); + } + } + + public static EndpointAddress ReadHeaderValue(XmlDictionaryReader reader, AddressingVersion version) + { + Fx.Assert(reader.IsStartElement(XD.AddressingDictionary.ReplyTo, version.DictionaryNamespace), ""); + return EndpointAddress.ReadFrom(version, reader); + } + + class FullReplyToHeader : ReplyToHeader + { + string actor; + bool mustUnderstand; + bool relay; + + public FullReplyToHeader(EndpointAddress replyTo, string actor, bool mustUnderstand, bool relay, AddressingVersion version) + : base(replyTo, version) + { + this.actor = actor; + this.mustUnderstand = mustUnderstand; + this.relay = relay; + } + + public override string Actor + { + get { return actor; } + } + + public override bool MustUnderstand + { + get { return mustUnderstand; } + } + + public override bool Relay + { + get { return relay; } + } + } + } + + class MessageIDHeader : AddressingHeader + { + UniqueId messageId; + const bool mustUnderstandValue = false; + + MessageIDHeader(UniqueId messageId, AddressingVersion version) + : base(version) + { + this.messageId = messageId; + } + + public override XmlDictionaryString DictionaryName + { + get { return XD.AddressingDictionary.MessageId; } + } + + public UniqueId MessageId + { + get { return messageId; } + } + + public override bool MustUnderstand + { + get { return mustUnderstandValue; } + } + + public static MessageIDHeader Create(UniqueId messageId, AddressingVersion addressingVersion) + { + if (object.ReferenceEquals(messageId, null)) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("messageId"); + if (addressingVersion == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("addressingVersion"); + return new MessageIDHeader(messageId, addressingVersion); + } + + protected override void OnWriteHeaderContents(XmlDictionaryWriter writer, MessageVersion messageVersion) + { + writer.WriteValue(messageId); + } + + public static UniqueId ReadHeaderValue(XmlDictionaryReader reader, AddressingVersion version) + { + Fx.Assert(reader.IsStartElement(XD.AddressingDictionary.MessageId, version.DictionaryNamespace), ""); + return reader.ReadElementContentAsUniqueId(); + } + + public static MessageIDHeader ReadHeader(XmlDictionaryReader reader, AddressingVersion version, + string actor, bool mustUnderstand, bool relay) + { + UniqueId messageId = ReadHeaderValue(reader, version); + + if (actor.Length == 0 && mustUnderstand == mustUnderstandValue && !relay) + { + return new MessageIDHeader(messageId, version); + } + else + { + return new FullMessageIDHeader(messageId, actor, mustUnderstand, relay, version); + } + } + + class FullMessageIDHeader : MessageIDHeader + { + string actor; + bool mustUnderstand; + bool relay; + + public FullMessageIDHeader(UniqueId messageId, string actor, bool mustUnderstand, bool relay, AddressingVersion version) + : base(messageId, version) + { + this.actor = actor; + this.mustUnderstand = mustUnderstand; + this.relay = relay; + } + + public override string Actor + { + get { return actor; } + } + + public override bool MustUnderstand + { + get { return mustUnderstand; } + } + + public override bool Relay + { + get { return relay; } + } + } + } + + class RelatesToHeader : AddressingHeader + { + UniqueId messageId; + const bool mustUnderstandValue = false; + internal static readonly Uri ReplyRelationshipType = new Uri(Addressing10Strings.ReplyRelationship); + + RelatesToHeader(UniqueId messageId, AddressingVersion version) + : base(version) + { + this.messageId = messageId; + } + + public override XmlDictionaryString DictionaryName + { + get { return XD.AddressingDictionary.RelatesTo; } + } + + public UniqueId UniqueId + { + get { return messageId; } + } + + public override bool MustUnderstand + { + get { return mustUnderstandValue; } + } + + public virtual Uri RelationshipType + { + get { return ReplyRelationshipType; } + } + + public static RelatesToHeader Create(UniqueId messageId, AddressingVersion addressingVersion) + { + if (object.ReferenceEquals(messageId, null)) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("messageId"); + if (addressingVersion == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("addressingVersion"); + return new RelatesToHeader(messageId, addressingVersion); + } + + public static RelatesToHeader Create(UniqueId messageId, AddressingVersion addressingVersion, Uri relationshipType) + { + if (object.ReferenceEquals(messageId, null)) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("messageId"); + if (addressingVersion == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("addressingVersion"); + if (relationshipType == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("relationshipType"); + if (relationshipType == ReplyRelationshipType) + { + return new RelatesToHeader(messageId, addressingVersion); + } + else + { + return new FullRelatesToHeader(messageId, "", false, false, addressingVersion); + } + } + + protected override void OnWriteHeaderContents(XmlDictionaryWriter writer, MessageVersion messageVersion) + { + writer.WriteValue(messageId); + } + + public static void ReadHeaderValue(XmlDictionaryReader reader, AddressingVersion version, out Uri relationshipType, out UniqueId messageId) + { + AddressingDictionary addressingDictionary = XD.AddressingDictionary; + + // The RelationshipType attribute has no namespace. + relationshipType = ReplyRelationshipType; + /* + string relation = reader.GetAttribute(addressingDictionary.RelationshipType, addressingDictionary.Empty); + if (relation == null) + { + relationshipType = ReplyRelationshipType; + } + else + { + relationshipType = new Uri(relation); + } + */ + Fx.Assert(reader.IsStartElement(addressingDictionary.RelatesTo, version.DictionaryNamespace), ""); + messageId = reader.ReadElementContentAsUniqueId(); + } + + public static RelatesToHeader ReadHeader(XmlDictionaryReader reader, AddressingVersion version, + string actor, bool mustUnderstand, bool relay) + { + UniqueId messageId; + Uri relationship; + ReadHeaderValue(reader, version, out relationship, out messageId); + + if (actor.Length == 0 && mustUnderstand == mustUnderstandValue && !relay && (object)relationship == (object)ReplyRelationshipType) + { + return new RelatesToHeader(messageId, version); + } + else + { + return new FullRelatesToHeader(messageId, actor, mustUnderstand, relay, version); + } + } + + class FullRelatesToHeader : RelatesToHeader + { + string actor; + bool mustUnderstand; + bool relay; + //Uri relationship; + + public FullRelatesToHeader(UniqueId messageId, string actor, bool mustUnderstand, bool relay, AddressingVersion version) + : base(messageId, version) + { + //this.relationship = relationship; + this.actor = actor; + this.mustUnderstand = mustUnderstand; + this.relay = relay; + } + + public override string Actor + { + get { return actor; } + } + + public override bool MustUnderstand + { + get { return mustUnderstand; } + } + + /* + public override Uri RelationshipType + { + get { return relationship; } + } + */ + + public override bool Relay + { + get { return relay; } + } + + protected override void OnWriteHeaderContents(XmlDictionaryWriter writer, MessageVersion messageVersion) + { + /* + if ((object)relationship != (object)ReplyRelationshipType) + { + // The RelationshipType attribute has no namespace. + writer.WriteStartAttribute(AddressingStrings.RelationshipType, AddressingStrings.Empty); + writer.WriteString(relationship.AbsoluteUri); + writer.WriteEndAttribute(); + } + */ + writer.WriteValue(messageId); + } + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/AddressingVersion.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/AddressingVersion.cs new file mode 100644 index 000000000..c40440666 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/AddressingVersion.cs @@ -0,0 +1,69 @@ +using System; +using System.Xml; + +namespace CoreWCF.Channels +{ + public sealed class AddressingVersion + { + /*MessagePartSpecification signedMessageParts;*/ + string toStringFormat; + + AddressingVersion(string ns, XmlDictionaryString dictionaryNs, string toStringFormat, + /*MessagePartSpecification signedMessageParts,*/ string anonymous, XmlDictionaryString dictionaryAnonymous, string none, string faultAction, string defaultFaultAction) + { + Namespace = ns; + DictionaryNamespace = dictionaryNs; + this.toStringFormat = toStringFormat; + /*this.signedMessageParts = signedMessageParts;*/ + Anonymous = anonymous; + DictionaryAnonymous = dictionaryAnonymous; + + if (anonymous != null) + { + AnonymousUri = new Uri(anonymous); + } + + if (none != null) + { + NoneUri = new Uri(none); + } + + FaultAction = faultAction; + DefaultFaultAction = defaultFaultAction; + } + + public static AddressingVersion WSAddressing10 { get; } = new AddressingVersion(Addressing10Strings.Namespace, + XD.Addressing10Dictionary.Namespace, SR.Addressing10ToStringFormat, /*Addressing10SignedMessageParts,*/ + Addressing10Strings.Anonymous, XD.Addressing10Dictionary.Anonymous, Addressing10Strings.NoneAddress, + Addressing10Strings.FaultAction, Addressing10Strings.DefaultFaultAction); + + public static AddressingVersion None { get; } = new AddressingVersion(AddressingNoneStrings.Namespace, XD.AddressingNoneDictionary.Namespace, + SR.AddressingNoneToStringFormat, /*new MessagePartSpecification(),*/ null, null, null, null, null); + + public static AddressingVersion WSAddressingAugust2004 { get; } = new AddressingVersion(Addressing200408Strings.Namespace, + XD.Addressing200408Dictionary.Namespace, SR.Addressing200408ToStringFormat, /*Addressing200408SignedMessageParts,*/ + Addressing200408Strings.Anonymous, XD.Addressing200408Dictionary.Anonymous, null, + Addressing200408Strings.FaultAction, Addressing200408Strings.DefaultFaultAction); + + internal string Namespace { get; } + + internal XmlDictionaryString DictionaryNamespace { get; } + + internal string Anonymous { get; } + + internal XmlDictionaryString DictionaryAnonymous { get; } + + internal Uri AnonymousUri { get; } + + internal Uri NoneUri { get; } + + internal string FaultAction { get; } // the action for addressing faults + + internal string DefaultFaultAction { get; } // a default string that can be used for non-addressing faults + + public override string ToString() + { + return SR.Format(toStringFormat, Namespace); + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/AuthenticationSchemesHelper.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/AuthenticationSchemesHelper.cs new file mode 100644 index 000000000..3621cb825 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/AuthenticationSchemesHelper.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Text; + +namespace CoreWCF.Channels +{ + internal static class AuthenticationSchemesHelper + { + //public static bool DoesAuthTypeMatch(AuthenticationSchemes authScheme, string authType) + //{ + // if ((authType == null) || (authType.Length == 0)) + // { + // return authScheme.IsSet(AuthenticationSchemes.Anonymous); + // } + + // if (authType.Equals("kerberos", StringComparison.OrdinalIgnoreCase) || + // authType.Equals("negotiate", StringComparison.OrdinalIgnoreCase)) + // { + // return authScheme.IsSet(AuthenticationSchemes.Negotiate); + // } + // else if (authType.Equals("ntlm", StringComparison.OrdinalIgnoreCase)) + // { + // return authScheme.IsSet(AuthenticationSchemes.Negotiate) || + // authScheme.IsSet(AuthenticationSchemes.Ntlm); + // } + + // AuthenticationSchemes authTypeScheme; + // if (!Enum.TryParse(authType, true, out authTypeScheme)) + // { + // return false; + // } + + // return authScheme.IsSet(authTypeScheme); + //} + + //public static bool IsSingleton(this AuthenticationSchemes v) + //{ + // bool result; + // switch (v) + // { + // case AuthenticationSchemes.Digest: + // case AuthenticationSchemes.Negotiate: + // case AuthenticationSchemes.Ntlm: + // case AuthenticationSchemes.Basic: + // case AuthenticationSchemes.Anonymous: + // result = true; + // break; + // default: + // result = false; + // break; + // } + // return result; + //} + + public static bool IsSet(this AuthenticationSchemes thisPtr, AuthenticationSchemes authenticationSchemes) + { + return (thisPtr & authenticationSchemes) == authenticationSchemes; + } + + public static bool IsNotSet(this AuthenticationSchemes thisPtr, AuthenticationSchemes authenticationSchemes) + { + return (thisPtr & authenticationSchemes) == 0; + } + + //internal static string ToString(AuthenticationSchemes authScheme) + //{ + // return authScheme.ToString().ToLowerInvariant(); + //} + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/BaseUriWithWildcard.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/BaseUriWithWildcard.cs new file mode 100644 index 000000000..7743d22b3 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/BaseUriWithWildcard.cs @@ -0,0 +1,324 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Runtime.Serialization; +using CoreWCF.Runtime; +using System.Diagnostics; + +namespace CoreWCF.Channels +{ + // TODO: Make internal again + [DataContract] + public sealed class BaseUriWithWildcard + { + [DataMember] + Uri _baseAddress; + + const char segmentDelimiter = '/'; + + [DataMember] + HostNameComparisonMode _hostNameComparisonMode; + const string plus = "+"; + const string star = "*"; + const int HttpUriDefaultPort = 80; + const int HttpsUriDefaultPort = 443; + + // Derived from [DataMember] fields + Comparand _comparand; + int _hashCode; + + public BaseUriWithWildcard(Uri baseAddress, HostNameComparisonMode hostNameComparisonMode) + { + _baseAddress = baseAddress; + _hostNameComparisonMode = hostNameComparisonMode; + SetComparisonAddressAndHashCode(); + + // Note the Uri may contain query string for WSDL purpose. + // So do not check IsValid(). + } + + BaseUriWithWildcard(string protocol, int defaultPort, string binding, int segmentCount, string path, string sampleBinding) + { + string[] urlParameters = SplitBinding(binding); + + if (urlParameters.Length != segmentCount) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new UriFormatException(SR.Format(SR.Hosting_MisformattedBinding, binding, protocol, sampleBinding))); + } + + int currentIndex = segmentCount - 1; + string host = ParseHostAndHostNameComparisonMode(urlParameters[currentIndex]); + + int port = -1; + + if (--currentIndex >= 0) + { + string portString = urlParameters[currentIndex].Trim(); + + if (!string.IsNullOrEmpty(portString) && + !int.TryParse(portString, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out port)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new UriFormatException(SR.Format(SR.Hosting_MisformattedPort, protocol, binding, portString))); + } + + if (port == defaultPort) + { + // Set to -1 so that Uri does not show it in the string. + port = -1; + } + } + try + { + Fx.Assert(path != null, "path should never be null here"); + _baseAddress = new UriBuilder(protocol, host, port, path).Uri; + } + catch (Exception exception) + { + if (Fx.IsFatal(exception)) + { + throw; + } + + DiagnosticUtility.TraceHandledException(exception, TraceEventType.Error); + + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new UriFormatException(SR.Format(SR.Hosting_MisformattedBindingData, binding, + protocol))); + } + SetComparisonAddressAndHashCode(); + } + + // TODO: Make internal again + public Uri BaseAddress + { + get { return _baseAddress; } + } + + // TODO: Make internal again + public HostNameComparisonMode HostNameComparisonMode + { + get { return _hostNameComparisonMode; } + } + + static string[] SplitBinding(string binding) + { + bool parsingIPv6Address = false; + string[] tokens = null; + const char splitChar = ':', startIPv6Address = '[', endIPv6Address = ']'; + + List splitLocations = null; + + for (int i = 0; i < binding.Length; i++) + { + if (parsingIPv6Address && binding[i] == endIPv6Address) + { + parsingIPv6Address = false; + } + else if (binding[i] == startIPv6Address) + { + parsingIPv6Address = true; + } + else if (!parsingIPv6Address && binding[i] == splitChar) + { + if (splitLocations == null) + { + splitLocations = new List(); + } + splitLocations.Add(i); + } + } + + if (splitLocations == null) + { + tokens = new[] { binding }; + } + else + { + tokens = new string[splitLocations.Count + 1]; + int startIndex = 0; + for (int i = 0; i < tokens.Length; i++) + { + if (i < splitLocations.Count) + { + int nextSplitIndex = splitLocations[i]; + tokens[i] = binding.Substring(startIndex, nextSplitIndex - startIndex); + startIndex = nextSplitIndex + 1; + } + else //splitting the last segment + { + if (startIndex < binding.Length) + { + tokens[i] = binding.Substring(startIndex, binding.Length - startIndex); + } + else + { + //splitChar was the last character in the string + tokens[i] = string.Empty; + } + } + } + } + return tokens; + } + + internal static BaseUriWithWildcard CreateHostedUri(string protocol, string binding, string path) + { + Fx.Assert(protocol != null, "caller must verify"); + + if (binding == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("binding"); + } + + if (path == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("path"); + } + + if (protocol.Equals(UriEx.UriSchemeHttp, StringComparison.OrdinalIgnoreCase)) + { + // For http, binding format is: "::" + // as specified in http://www.microsoft.com/resources/documentation/WindowsServ/2003/standard/proddocs/en-us/Default.asp?url=/resources/documentation/WindowsServ/2003/standard/proddocs/en-us/ref_mb_serverbindings.asp + return new BaseUriWithWildcard(UriEx.UriSchemeHttp, HttpUriDefaultPort, binding, 3, path, ":80:"); + } + if (protocol.Equals(UriEx.UriSchemeHttps, StringComparison.OrdinalIgnoreCase)) + { + // For https, binding format is the same as http + return new BaseUriWithWildcard(UriEx.UriSchemeHttps, HttpsUriDefaultPort, binding, 3, path, ":443:"); + } + if (protocol.Equals(UriEx.UriSchemeNetTcp, StringComparison.OrdinalIgnoreCase)) + { + // For net.tcp, binding format is: ":" + return new BaseUriWithWildcard(UriEx.UriSchemeNetTcp, TransportDefaults.TcpUriDefaultPort, binding, 2, path, "808:*"); + } + + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new UriFormatException(SR.Format(SR.Hosting_NotSupportedProtocol, binding))); + } + + public override bool Equals(object o) + { + BaseUriWithWildcard other = o as BaseUriWithWildcard; + + if (other == null || other._hashCode != _hashCode || other._hostNameComparisonMode != _hostNameComparisonMode || + other._comparand.Port != _comparand.Port) + { + return false; + } + if (!ReferenceEquals(other._comparand.Scheme, _comparand.Scheme)) + { + return false; + } + return _comparand.Address.Equals(other._comparand.Address); + } + + public override int GetHashCode() + { + return _hashCode; + } + + internal bool IsBaseOf(Uri fullAddress) + { + if (_baseAddress.Scheme != (object)fullAddress.Scheme) + { + return false; + } + + if (_baseAddress.Port != fullAddress.Port) + { + return false; + } + + if (HostNameComparisonMode == HostNameComparisonMode.Exact) + { + if (string.Compare(_baseAddress.Host, fullAddress.Host, StringComparison.OrdinalIgnoreCase) != 0) + { + return false; + } + } + string s1 = _baseAddress.GetComponents(UriComponents.Path | UriComponents.KeepDelimiter, UriFormat.Unescaped); + string s2 = fullAddress.GetComponents(UriComponents.Path | UriComponents.KeepDelimiter, UriFormat.Unescaped); + + if (s1.Length > s2.Length) + { + return false; + } + + if (s1.Length < s2.Length && + s1[s1.Length - 1] != segmentDelimiter && + s2[s1.Length] != segmentDelimiter) + { + // Matching over segments + return false; + } + return string.Compare(s2, 0, s1, 0, s1.Length, StringComparison.OrdinalIgnoreCase) == 0; + } + + [OnDeserialized] + internal void OnDeserialized(StreamingContext context) + { + UriSchemeKeyedCollection.ValidateBaseAddress(_baseAddress, "context"); + + if (!HostNameComparisonModeHelper.IsDefined(HostNameComparisonMode)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("context", SR.Hosting_BaseUriDeserializedNotValid); + } + SetComparisonAddressAndHashCode(); + } + + string ParseHostAndHostNameComparisonMode(string host) + { + if (string.IsNullOrEmpty(host) || host.Equals(star)) + { + _hostNameComparisonMode = HostNameComparisonMode.WeakWildcard; + host = DnsCache.MachineName; + } + else if (host.Equals(plus)) + { + _hostNameComparisonMode = HostNameComparisonMode.StrongWildcard; + host = DnsCache.MachineName; + } + else + { + _hostNameComparisonMode = HostNameComparisonMode.Exact; + } + return host; + } + + void SetComparisonAddressAndHashCode() + { + if (HostNameComparisonMode == HostNameComparisonMode.Exact) + { + // Use canonical string representation of the full base address for comparison + _comparand.Address = _baseAddress.ToString(); + } + else + { + // Use canonical string representation of the absolute path for comparison + _comparand.Address = _baseAddress.GetComponents(UriComponents.Path | UriComponents.KeepDelimiter, UriFormat.UriEscaped); + } + + _comparand.Port = _baseAddress.Port; + _comparand.Scheme = _baseAddress.Scheme; + + if ((_comparand.Port == -1) && (_comparand.Scheme == (object)UriEx.UriSchemeNetTcp)) + { + // Compensate for the fact that the Uri type doesn't know about our default TCP port number + _comparand.Port = TransportDefaults.TcpUriDefaultPort; + } + _hashCode = _comparand.Address.GetHashCode() ^ _comparand.Port ^ (int)HostNameComparisonMode; + } + + public override string ToString() + { + return string.Format(CultureInfo.InvariantCulture, "{0}:{1}", HostNameComparisonMode, BaseAddress); + } + + struct Comparand + { + public string Address; + public int Port; + public string Scheme; + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/BinaryMessageEncoderFactory.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/BinaryMessageEncoderFactory.cs new file mode 100644 index 000000000..d9d2cf5a1 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/BinaryMessageEncoderFactory.cs @@ -0,0 +1,1815 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using CoreWCF.Runtime; +using System.Xml; +using CoreWCF.Xml; + +namespace CoreWCF.Channels +{ + class BinaryMessageEncoderFactory : MessageEncoderFactory + { + const int maxPooledXmlReaderPerMessage = 2; + + BinaryMessageEncoder messageEncoder; + MessageVersion messageVersion; + int maxReadPoolSize; + int maxWritePoolSize; + CompressionFormat compressionFormat; + + // Double-checked locking pattern requires volatile for read/write synchronization + //volatile SynchronizedPool streamedWriterPool; + //volatile SynchronizedPool streamedReaderPool; + volatile SynchronizedPool bufferedDataPool; + volatile SynchronizedPool bufferedWriterPool; + volatile SynchronizedPool recycledStatePool; + + object thisLock; + int maxSessionSize; + OnXmlDictionaryReaderClose onStreamedReaderClose; + XmlDictionaryReaderQuotas readerQuotas; + XmlDictionaryReaderQuotas bufferedReadReaderQuotas; + BinaryVersion binaryVersion; + + public BinaryMessageEncoderFactory(MessageVersion messageVersion, int maxReadPoolSize, int maxWritePoolSize, int maxSessionSize, + XmlDictionaryReaderQuotas readerQuotas, long maxReceivedMessageSize, BinaryVersion version, CompressionFormat compressionFormat) + { + this.messageVersion = messageVersion; + this.maxReadPoolSize = maxReadPoolSize; + this.maxWritePoolSize = maxWritePoolSize; + this.maxSessionSize = maxSessionSize; + thisLock = new object(); + onStreamedReaderClose = new OnXmlDictionaryReaderClose(ReturnStreamedReader); + this.readerQuotas = new XmlDictionaryReaderQuotas(); + if (readerQuotas != null) + { + readerQuotas.CopyTo(this.readerQuotas); + } + + bufferedReadReaderQuotas = EncoderHelpers.GetBufferedReadQuotas(this.readerQuotas); + MaxReceivedMessageSize = maxReceivedMessageSize; + + binaryVersion = version; + this.compressionFormat = compressionFormat; + messageEncoder = new BinaryMessageEncoder(this, false, 0); + } + + public static IXmlDictionary XmlDictionary + { + get { return XD.Dictionary; } + } + + public override MessageEncoder Encoder + { + get + { + return messageEncoder; + } + } + + public override MessageVersion MessageVersion + { + get { return messageVersion; } + } + + public int MaxWritePoolSize + { + get { return maxWritePoolSize; } + } + + public XmlDictionaryReaderQuotas ReaderQuotas + { + get + { + return readerQuotas; + } + } + + public int MaxReadPoolSize + { + get { return maxReadPoolSize; } + } + + public int MaxSessionSize + { + get { return maxSessionSize; } + } + + public CompressionFormat CompressionFormat + { + get { return compressionFormat; } + } + + long MaxReceivedMessageSize + { + get; + set; + } + + object ThisLock + { + get { return thisLock; } + } + + SynchronizedPool RecycledStatePool + { + get + { + if (recycledStatePool == null) + { + lock (ThisLock) + { + if (recycledStatePool == null) + { + //running = true; + recycledStatePool = new SynchronizedPool(maxReadPoolSize); + } + } + } + return recycledStatePool; + } + } + + public override MessageEncoder CreateSessionEncoder() + { + return new BinaryMessageEncoder(this, true, maxSessionSize); + } + + XmlDictionaryWriter TakeStreamedWriter(Stream stream) + { + return XmlDictionaryWriter.CreateBinaryWriter(stream, binaryVersion.Dictionary, null, false); + // TODO: Revert once IXmlBinaryWriterInitializer is available + //if (streamedWriterPool == null) + //{ + // lock (ThisLock) + // { + // if (streamedWriterPool == null) + // { + // //running = true; + // streamedWriterPool = new SynchronizedPool(maxWritePoolSize); + // } + // } + //} + //XmlDictionaryWriter xmlWriter = streamedWriterPool.Take(); + //if (xmlWriter == null) + //{ + // xmlWriter = XmlDictionaryWriter.CreateBinaryWriter(stream, binaryVersion.Dictionary, null, false); + //} + //else + //{ + // ((IXmlBinaryWriterInitializer)xmlWriter).SetOutput(stream, binaryVersion.Dictionary, null, false); + //} + //return xmlWriter; + } + + void ReturnStreamedWriter(XmlDictionaryWriter xmlWriter) + { + xmlWriter.Dispose(); + //streamedWriterPool.Return(xmlWriter); + } + + BinaryBufferedMessageWriter TakeBufferedWriter() + { + if (bufferedWriterPool == null) + { + lock (ThisLock) + { + if (bufferedWriterPool == null) + { + //running = true; + bufferedWriterPool = new SynchronizedPool(maxWritePoolSize); + } + } + } + + BinaryBufferedMessageWriter messageWriter = bufferedWriterPool.Take(); + if (messageWriter == null) + { + messageWriter = new BinaryBufferedMessageWriter(binaryVersion.Dictionary); + } + return messageWriter; + } + + void ReturnMessageWriter(BinaryBufferedMessageWriter messageWriter) + { + bufferedWriterPool.Return(messageWriter); + } + + XmlDictionaryReader TakeStreamedReader(Stream stream) + { + return XmlDictionaryReader.CreateBinaryReader(stream, + binaryVersion.Dictionary, + readerQuotas, + null); + // TODO: Revert once IXmlBinaryReaderInitializer is available + //if (streamedReaderPool == null) + //{ + // lock (ThisLock) + // { + // if (streamedReaderPool == null) + // { + // //running = true; + // streamedReaderPool = new SynchronizedPool(maxReadPoolSize); + // } + // } + //} + + //XmlDictionaryReader xmlReader = streamedReaderPool.Take(); + //if (xmlReader == null) + //{ + // xmlReader = XmlDictionaryReader.CreateBinaryReader(stream, + // binaryVersion.Dictionary, + // readerQuotas, + // null, + // onStreamedReaderClose); + // if (TD.ReadPoolMissIsEnabled()) + // { + // TD.ReadPoolMiss(xmlReader.GetType().Name); + // } + //} + //else + //{ + // ((IXmlBinaryReaderInitializer)xmlReader).SetInput(stream, + // binaryVersion.Dictionary, + // readerQuotas, + // null, + // onStreamedReaderClose); + //} + + //return xmlReader; + } + + void ReturnStreamedReader(XmlDictionaryReader xmlReader) + { + //streamedReaderPool.Return(xmlReader); + } + + BinaryBufferedMessageData TakeBufferedData(BinaryMessageEncoder messageEncoder) + { + if (bufferedDataPool == null) + { + lock (ThisLock) + { + if (bufferedDataPool == null) + { + //running = true; + bufferedDataPool = new SynchronizedPool(maxReadPoolSize); + } + } + } + BinaryBufferedMessageData messageData = bufferedDataPool.Take(); + if (messageData == null) + { + messageData = new BinaryBufferedMessageData(this, maxPooledXmlReaderPerMessage); + } + messageData.SetMessageEncoder(messageEncoder); + return messageData; + } + + void ReturnBufferedData(BinaryBufferedMessageData messageData) + { + messageData.SetMessageEncoder(null); + bufferedDataPool.Return(messageData); + } + + class BinaryBufferedMessageData : BufferedMessageData + { + BinaryMessageEncoderFactory factory; + BinaryMessageEncoder messageEncoder; + Pool readerPool; + OnXmlDictionaryReaderClose onClose; + + public BinaryBufferedMessageData(BinaryMessageEncoderFactory factory, int maxPoolSize) + : base(factory.RecycledStatePool) + { + this.factory = factory; + readerPool = new Pool(maxPoolSize); + onClose = new OnXmlDictionaryReaderClose(OnXmlReaderClosed); + } + + public override MessageEncoder MessageEncoder + { + get { return messageEncoder; } + } + + public override XmlDictionaryReaderQuotas Quotas + { + get { return factory.readerQuotas; } + } + + public void SetMessageEncoder(BinaryMessageEncoder messageEncoder) + { + this.messageEncoder = messageEncoder; + } + + protected override XmlDictionaryReader TakeXmlReader() + { + ArraySegment buffer = Buffer; + + return XmlDictionaryReader.CreateBinaryReader(buffer.Array, buffer.Offset, buffer.Count, + factory.binaryVersion.Dictionary, + factory.bufferedReadReaderQuotas, + messageEncoder.ReaderSession); + // TODO: Revert once IXmlBinaryReaderInitializer is available + //ArraySegment buffer = this.Buffer; + //XmlDictionaryReader xmlReader = readerPool.Take(); + + //if (xmlReader != null) + //{ + // ((IXmlBinaryReaderInitializer)xmlReader).SetInput(buffer.Array, buffer.Offset, buffer.Count, + // factory.binaryVersion.Dictionary, + // factory.bufferedReadReaderQuotas, + // messageEncoder.ReaderSession, + // onClose); + //} + //else + //{ + // xmlReader = XmlDictionaryReader.CreateBinaryReader(buffer.Array, buffer.Offset, buffer.Count, + // factory.binaryVersion.Dictionary, + // factory.bufferedReadReaderQuotas, + // messageEncoder.ReaderSession, + // onClose); + // if (TD.ReadPoolMissIsEnabled()) + // { + // TD.ReadPoolMiss(xmlReader.GetType().Name); + // } + //} + + //return xmlReader; + } + + protected override void ReturnXmlReader(XmlDictionaryReader reader) + { + readerPool.Return(reader); + } + + protected override void OnClosed() + { + factory.ReturnBufferedData(this); + } + } + + class BinaryBufferedMessageWriter : BufferedMessageWriter + { + XmlDictionaryWriter writer; + IXmlDictionary dictionary; + XmlBinaryWriterSession session; + + public BinaryBufferedMessageWriter(IXmlDictionary dictionary) + { + this.dictionary = dictionary; + } + + public BinaryBufferedMessageWriter(IXmlDictionary dictionary, XmlBinaryWriterSession session) + { + this.dictionary = dictionary; + this.session = session; + } + + protected override XmlDictionaryWriter TakeXmlWriter(Stream stream) + { + return XmlDictionaryWriter.CreateBinaryWriter(stream, dictionary, session, false); + // TODO: Revert once IXmlBinaryReaderInitializer is available + //XmlDictionaryWriter returnedWriter = writer; + //if (returnedWriter == null) + //{ + // returnedWriter = XmlDictionaryWriter.CreateBinaryWriter(stream, dictionary, session, false); + //} + //else + //{ + // writer = null; + // ((IXmlBinaryWriterInitializer)returnedWriter).SetOutput(stream, dictionary, session, false); + //} + //return returnedWriter; + } + + protected override void ReturnXmlWriter(XmlDictionaryWriter writer) + { + writer.Dispose(); + + if (this.writer == null) + { + this.writer = writer; + } + } + } + + class BinaryMessageEncoder : MessageEncoder, ICompressedMessageEncoder + { + const string SupportedCompressionTypesMessageProperty = "BinaryMessageEncoder.SupportedCompressionTypes"; + + BinaryMessageEncoderFactory factory; + bool isSession; + XmlBinaryWriterSessionWithQuota writerSession; + BinaryBufferedMessageWriter sessionMessageWriter; + XmlBinaryReaderSession readerSession; + //XmlBinaryReaderSession readerSessionForLogging; + //bool readerSessionForLoggingIsInvalid = false; + //int writeIdCounter; + int idCounter; + int maxSessionSize; + int remainingReaderSessionSize; + bool isReaderSessionInvalid; + MessagePatterns messagePatterns; + string contentType; + string normalContentType; + string gzipCompressedContentType; + string deflateCompressedContentType; + CompressionFormat sessionCompressionFormat; + readonly long maxReceivedMessageSize; + + public BinaryMessageEncoder(BinaryMessageEncoderFactory factory, bool isSession, int maxSessionSize) + { + this.factory = factory; + this.isSession = isSession; + this.maxSessionSize = maxSessionSize; + remainingReaderSessionSize = maxSessionSize; + normalContentType = isSession ? factory.binaryVersion.SessionContentType : factory.binaryVersion.ContentType; + gzipCompressedContentType = isSession ? BinaryVersion.GZipVersion1.SessionContentType : BinaryVersion.GZipVersion1.ContentType; + deflateCompressedContentType = isSession ? BinaryVersion.DeflateVersion1.SessionContentType : BinaryVersion.DeflateVersion1.ContentType; + sessionCompressionFormat = this.factory.CompressionFormat; + maxReceivedMessageSize = this.factory.MaxReceivedMessageSize; + + switch (this.factory.CompressionFormat) + { + case CompressionFormat.Deflate: + contentType = deflateCompressedContentType; + break; + case CompressionFormat.GZip: + contentType = gzipCompressedContentType; + break; + default: + contentType = normalContentType; + break; + } + } + + public override string ContentType + { + get + { + return contentType; + } + } + + public override MessageVersion MessageVersion + { + get { return factory.messageVersion; } + } + + public override string MediaType + { + get { return contentType; } + } + + public XmlBinaryReaderSession ReaderSession + { + get { return readerSession; } + } + + public bool CompressionEnabled + { + get { return factory.CompressionFormat != CompressionFormat.None; } + } + + ArraySegment AddSessionInformationToMessage(ArraySegment messageData, BufferManager bufferManager, int maxMessageSize) + { + int dictionarySize = 0; + byte[] buffer = messageData.Array; + + if (writerSession.HasNewStrings) + { + IList newStrings = writerSession.GetNewStrings(); + for (int i = 0; i < newStrings.Count; i++) + { + int utf8ValueSize = Encoding.UTF8.GetByteCount(newStrings[i].Value); + dictionarySize += IntEncoder.GetEncodedSize(utf8ValueSize) + utf8ValueSize; + } + + int messageSize = messageData.Offset + messageData.Count; + int remainingMessageSize = maxMessageSize - messageSize; + if (remainingMessageSize - dictionarySize < 0) + { + string excMsg = SR.Format(SR.MaxSentMessageSizeExceeded, maxMessageSize); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new QuotaExceededException(excMsg)); + } + + int requiredBufferSize = messageData.Offset + messageData.Count + dictionarySize; + if (buffer.Length < requiredBufferSize) + { + byte[] newBuffer = bufferManager.TakeBuffer(requiredBufferSize); + Buffer.BlockCopy(buffer, messageData.Offset, newBuffer, messageData.Offset, messageData.Count); + bufferManager.ReturnBuffer(buffer); + buffer = newBuffer; + } + + Buffer.BlockCopy(buffer, messageData.Offset, buffer, messageData.Offset + dictionarySize, messageData.Count); + + int offset = messageData.Offset; + for (int i = 0; i < newStrings.Count; i++) + { + string newString = newStrings[i].Value; + int utf8ValueSize = Encoding.UTF8.GetByteCount(newString); + offset += IntEncoder.Encode(utf8ValueSize, buffer, offset); + offset += Encoding.UTF8.GetBytes(newString, 0, newString.Length, buffer, offset); + } + + writerSession.ClearNewStrings(); + } + + int headerSize = IntEncoder.GetEncodedSize(dictionarySize); + int newOffset = messageData.Offset - headerSize; + int newSize = headerSize + messageData.Count + dictionarySize; + IntEncoder.Encode(dictionarySize, buffer, newOffset); + return new ArraySegment(buffer, newOffset, newSize); + } + + ArraySegment ExtractSessionInformationFromMessage(ArraySegment messageData) + { + if (isReaderSessionInvalid) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataException(SR.BinaryEncoderSessionInvalid)); + } + + byte[] buffer = messageData.Array; + int dictionarySize; + int headerSize; + int newOffset; + int newSize; + bool throwing = true; + try + { + IntDecoder decoder = new IntDecoder(); + headerSize = decoder.Decode(buffer, messageData.Offset, messageData.Count); + dictionarySize = decoder.Value; + if (dictionarySize > messageData.Count) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataException(SR.BinaryEncoderSessionMalformed)); + } + newOffset = messageData.Offset + headerSize + dictionarySize; + newSize = messageData.Count - headerSize - dictionarySize; + if (newSize < 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataException(SR.BinaryEncoderSessionMalformed)); + } + if (dictionarySize > 0) + { + if (dictionarySize > remainingReaderSessionSize) + { + string message = SR.Format(SR.BinaryEncoderSessionTooLarge, maxSessionSize); + Exception inner = new QuotaExceededException(message); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(message, inner)); + } + else + { + remainingReaderSessionSize -= dictionarySize; + } + + int size = dictionarySize; + int offset = messageData.Offset + headerSize; + + while (size > 0) + { + decoder.Reset(); + int bytesDecoded = decoder.Decode(buffer, offset, size); + int utf8ValueSize = decoder.Value; + offset += bytesDecoded; + size -= bytesDecoded; + if (utf8ValueSize > size) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataException(SR.BinaryEncoderSessionMalformed)); + } + string value = Encoding.UTF8.GetString(buffer, offset, utf8ValueSize); + offset += utf8ValueSize; + size -= utf8ValueSize; + readerSession.Add(idCounter, value); + idCounter++; + } + } + throwing = false; + } + finally + { + if (throwing) + { + isReaderSessionInvalid = true; + } + } + + return new ArraySegment(buffer, newOffset, newSize); + } + + public override Message ReadMessage(ArraySegment buffer, BufferManager bufferManager, string contentType) + { + if (bufferManager == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("bufferManager"); + } + + CompressionFormat compressionFormat = CheckContentType(contentType); + + if (compressionFormat != CompressionFormat.None) + { + MessageEncoderCompressionHandler.DecompressBuffer(ref buffer, bufferManager, compressionFormat, maxReceivedMessageSize); + } + + if (isSession) + { + if (readerSession == null) + { + readerSession = new XmlBinaryReaderSession(); + messagePatterns = new MessagePatterns(factory.binaryVersion.Dictionary, readerSession, MessageVersion); + } + try + { + buffer = ExtractSessionInformationFromMessage(buffer); + } + catch (InvalidDataException) + { + //MessageLogger.LogMessage(buffer, MessageLoggingSource.Malformed); + throw; + } + } + BinaryBufferedMessageData messageData = factory.TakeBufferedData(this); + Message message; + if (messagePatterns != null) + { + message = messagePatterns.TryCreateMessage(buffer.Array, buffer.Offset, buffer.Count, bufferManager, messageData); + } + else + { + message = null; + } + if (message == null) + { + messageData.Open(buffer, bufferManager); + RecycledMessageState messageState = messageData.TakeMessageState(); + if (messageState == null) + { + messageState = new RecycledMessageState(); + } + message = new BufferedMessage(messageData, messageState); + } + message.Properties.Encoder = this; + + return message; + } + + public override Message ReadMessage(Stream stream, int maxSizeOfHeaders, string contentType) + { + if (stream == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("stream"); + } + + CompressionFormat compressionFormat = CheckContentType(contentType); + + if (compressionFormat != CompressionFormat.None) + { + stream = new MaxMessageSizeStream( + MessageEncoderCompressionHandler.GetDecompressStream(stream, compressionFormat), maxReceivedMessageSize); + } + + XmlDictionaryReader reader = factory.TakeStreamedReader(stream); + Message message = Message.CreateMessage(reader, maxSizeOfHeaders, factory.messageVersion); + message.Properties.Encoder = this; + + return message; + } + + public override ArraySegment WriteMessage(Message message, int maxMessageSize, BufferManager bufferManager, int messageOffset) + { + if (message == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("message"); + } + + if (bufferManager == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("bufferManager"); + } + + if (maxMessageSize < 0) + { + + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("maxMessageSize", maxMessageSize, + SR.ValueMustBeNonNegative)); + } + + message.Properties.Encoder = this; + + if (isSession) + { + if (writerSession == null) + { + writerSession = new XmlBinaryWriterSessionWithQuota(maxSessionSize); + sessionMessageWriter = new BinaryBufferedMessageWriter(factory.binaryVersion.Dictionary, writerSession); + } + messageOffset += IntEncoder.MaxEncodedSize; + } + + if (messageOffset < 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("messageOffset", messageOffset, + SR.ValueMustBeNonNegative)); + } + + if (messageOffset > maxMessageSize) + { + string excMsg = SR.Format(SR.MaxSentMessageSizeExceeded, maxMessageSize); + + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new QuotaExceededException(excMsg)); + } + + ThrowIfMismatchedMessageVersion(message); + BinaryBufferedMessageWriter messageWriter; + if (isSession) + { + messageWriter = sessionMessageWriter; + } + else + { + messageWriter = factory.TakeBufferedWriter(); + } + ArraySegment messageData = messageWriter.WriteMessage(message, bufferManager, messageOffset, maxMessageSize); + + //this.readerSessionForLoggingIsInvalid = true; + + if (isSession) + { + messageData = AddSessionInformationToMessage(messageData, bufferManager, maxMessageSize); + } + else + { + factory.ReturnMessageWriter(messageWriter); + } + + CompressionFormat compressionFormat = CheckCompressedWrite(message); + if (compressionFormat != CompressionFormat.None) + { + MessageEncoderCompressionHandler.CompressBuffer(ref messageData, bufferManager, compressionFormat); + } + + return messageData; + } + + public override void WriteMessage(Message message, Stream stream) + { + if (message == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("message")); + } + if (stream == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("stream")); + } + + CompressionFormat compressionFormat = CheckCompressedWrite(message); + if (compressionFormat != CompressionFormat.None) + { + stream = MessageEncoderCompressionHandler.GetCompressStream(stream, compressionFormat); + } + + ThrowIfMismatchedMessageVersion(message); + message.Properties.Encoder = this; + XmlDictionaryWriter xmlWriter = factory.TakeStreamedWriter(stream); + message.WriteMessage(xmlWriter); + xmlWriter.Flush(); + + + + factory.ReturnStreamedWriter(xmlWriter); + if (compressionFormat != CompressionFormat.None) + { + stream.Dispose(); + } + } + + public override bool IsContentTypeSupported(string contentType) + { + bool supported = true; + if (!base.IsContentTypeSupported(contentType)) + { + if (CompressionEnabled) + { + supported = (factory.CompressionFormat == CompressionFormat.GZip && + base.IsContentTypeSupported(contentType, gzipCompressedContentType, gzipCompressedContentType)) || + (factory.CompressionFormat == CompressionFormat.Deflate && + base.IsContentTypeSupported(contentType, deflateCompressedContentType, deflateCompressedContentType)) || + base.IsContentTypeSupported(contentType, normalContentType, normalContentType); + } + else + { + supported = false; + } + } + return supported; + } + + public void SetSessionContentType(string contentType) + { + if (base.IsContentTypeSupported(contentType, gzipCompressedContentType, gzipCompressedContentType)) + { + sessionCompressionFormat = CompressionFormat.GZip; + } + else if (base.IsContentTypeSupported(contentType, deflateCompressedContentType, deflateCompressedContentType)) + { + sessionCompressionFormat = CompressionFormat.Deflate; + } + else + { + sessionCompressionFormat = CompressionFormat.None; + } + } + + public void AddCompressedMessageProperties(Message message, string supportedCompressionTypes) + { + message.Properties.Add(SupportedCompressionTypesMessageProperty, supportedCompressionTypes); + } + + static bool ContentTypeEqualsOrStartsWith(string contentType, string supportedContentType) + { + return contentType == supportedContentType || contentType.StartsWith(supportedContentType, StringComparison.OrdinalIgnoreCase); + } + + CompressionFormat CheckContentType(string contentType) + { + CompressionFormat compressionFormat = CompressionFormat.None; + if (contentType == null) + { + compressionFormat = sessionCompressionFormat; + } + else + { + if (!CompressionEnabled) + { + if (!ContentTypeEqualsOrStartsWith(contentType, ContentType)) + { + throw Fx.Exception.AsError(new ProtocolException(SR.Format(SR.EncoderUnrecognizedContentType, contentType, ContentType))); + } + } + else + { + if (factory.CompressionFormat == CompressionFormat.GZip && ContentTypeEqualsOrStartsWith(contentType, gzipCompressedContentType)) + { + compressionFormat = CompressionFormat.GZip; + } + else if (factory.CompressionFormat == CompressionFormat.Deflate && ContentTypeEqualsOrStartsWith(contentType, deflateCompressedContentType)) + { + compressionFormat = CompressionFormat.Deflate; + } + else if (ContentTypeEqualsOrStartsWith(contentType, normalContentType)) + { + compressionFormat = CompressionFormat.None; + } + else + { + throw Fx.Exception.AsError(new ProtocolException(SR.Format(SR.EncoderUnrecognizedContentType, contentType, ContentType))); + } + } + } + + return compressionFormat; + } + + CompressionFormat CheckCompressedWrite(Message message) + { + CompressionFormat compressionFormat = sessionCompressionFormat; + if (compressionFormat != CompressionFormat.None && !isSession) + { + string acceptEncoding; + if (message.Properties.TryGetValue(SupportedCompressionTypesMessageProperty, out acceptEncoding) && + acceptEncoding != null) + { + acceptEncoding = acceptEncoding.ToLowerInvariant(); + if ((compressionFormat == CompressionFormat.GZip && + !acceptEncoding.Contains(MessageEncoderCompressionHandler.GZipContentEncoding)) || + (compressionFormat == CompressionFormat.Deflate && + !acceptEncoding.Contains(MessageEncoderCompressionHandler.DeflateContentEncoding))) + { + compressionFormat = CompressionFormat.None; + } + } + } + return compressionFormat; + } + } + + class XmlBinaryWriterSessionWithQuota : XmlBinaryWriterSession + { + int bytesRemaining; + List newStrings; + + public XmlBinaryWriterSessionWithQuota(int maxSessionSize) + { + bytesRemaining = maxSessionSize; + } + + public bool HasNewStrings + { + get { return newStrings != null; } + } + + public override bool TryAdd(XmlDictionaryString s, out int key) + { + if (bytesRemaining == 0) + { + key = -1; + return false; + } + + int bytesRequired = Encoding.UTF8.GetByteCount(s.Value); + bytesRequired += IntEncoder.GetEncodedSize(bytesRequired); + + if (bytesRequired > bytesRemaining) + { + key = -1; + bytesRemaining = 0; + return false; + } + + if (base.TryAdd(s, out key)) + { + if (newStrings == null) + { + newStrings = new List(); + } + newStrings.Add(s); + bytesRemaining -= bytesRequired; + return true; + } + else + { + return false; + } + } + + public IList GetNewStrings() + { + return newStrings; + } + + public void ClearNewStrings() + { + newStrings = null; + } + } + } + + class BinaryFormatBuilder + { + List bytes; + + public BinaryFormatBuilder() + { + bytes = new List(); + } + + public int Count + { + get { return bytes.Count; } + } + + public void AppendPrefixDictionaryElement(char prefix, int key) + { + AppendNode(XmlBinaryNodeType.PrefixDictionaryElementA + GetPrefixOffset(prefix)); + AppendKey(key); + } + + public void AppendDictionaryXmlnsAttribute(char prefix, int key) + { + AppendNode(XmlBinaryNodeType.DictionaryXmlnsAttribute); + AppendUtf8(prefix); + AppendKey(key); + } + + public void AppendPrefixDictionaryAttribute(char prefix, int key, char value) + { + AppendNode(XmlBinaryNodeType.PrefixDictionaryAttributeA + GetPrefixOffset(prefix)); + AppendKey(key); + if (value == '1') + { + AppendNode(XmlBinaryNodeType.OneText); + } + else + { + AppendNode(XmlBinaryNodeType.Chars8Text); + AppendUtf8(value); + } + } + + public void AppendDictionaryAttribute(char prefix, int key, char value) + { + AppendNode(XmlBinaryNodeType.DictionaryAttribute); + AppendUtf8(prefix); + AppendKey(key); + AppendNode(XmlBinaryNodeType.Chars8Text); + AppendUtf8(value); + } + + public void AppendDictionaryTextWithEndElement(int key) + { + AppendNode(XmlBinaryNodeType.DictionaryTextWithEndElement); + AppendKey(key); + } + + public void AppendDictionaryTextWithEndElement() + { + AppendNode(XmlBinaryNodeType.DictionaryTextWithEndElement); + } + + public void AppendUniqueIDWithEndElement() + { + AppendNode(XmlBinaryNodeType.UniqueIdTextWithEndElement); + } + + public void AppendEndElement() + { + AppendNode(XmlBinaryNodeType.EndElement); + } + + void AppendKey(int key) + { + if (key < 0 || key >= 0x4000) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("key", key, + SR.Format(SR.ValueMustBeInRange, 0, 0x4000))); + } + if (key >= 0x80) + { + AppendByte((key & 0x7f) | 0x80); + AppendByte(key >> 7); + } + else + { + AppendByte(key); + } + } + + void AppendNode(XmlBinaryNodeType value) + { + AppendByte((int)value); + } + + void AppendByte(int value) + { + if (value < 0 || value > 0xFF) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value, + SR.Format(SR.ValueMustBeInRange, 0, 0xFF))); + } + bytes.Add((byte)value); + } + + void AppendUtf8(char value) + { + AppendByte(1); + AppendByte((int)value); + } + + public int GetStaticKey(int value) + { + return value * 2; + } + + public int GetSessionKey(int value) + { + return value * 2 + 1; + } + + int GetPrefixOffset(char prefix) + { + if (prefix < 'a' && prefix > 'z') + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("prefix", prefix, + SR.Format(SR.ValueMustBeInRange, 'a', 'z'))); + } + return prefix - 'a'; + } + + public byte[] ToByteArray() + { + byte[] array = bytes.ToArray(); + bytes.Clear(); + return array; + } + } + + static class BinaryFormatParser + { + public static bool IsSessionKey(int value) + { + return (value & 1) != 0; + } + + public static int GetSessionKey(int value) + { + return value / 2; + } + + public static int GetStaticKey(int value) + { + return value / 2; + } + + public static int ParseInt32(byte[] buffer, int offset, int size) + { + switch (size) + { + case 1: + return buffer[offset]; + case 2: + return (buffer[offset] & 0x7f) + (buffer[offset + 1] << 7); + case 3: + return (buffer[offset] & 0x7f) + ((buffer[offset + 1] & 0x7f) << 7) + (buffer[offset + 2] << 14); + case 4: + return (buffer[offset] & 0x7f) + ((buffer[offset + 1] & 0x7f) << 7) + ((buffer[offset + 2] & 0x7f) << 14) + (buffer[offset + 3] << 21); + default: + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("size", size, + SR.Format(SR.ValueMustBeInRange, 1, 4))); + } + } + + public static int ParseKey(byte[] buffer, int offset, int size) + { + return ParseInt32(buffer, offset, size); + } + + public unsafe static UniqueId ParseUniqueID(byte[] buffer, int offset, int size) + { + return new UniqueId(buffer, offset); + } + + public static int MatchBytes(byte[] buffer, int offset, int size, byte[] buffer2) + { + if (size < buffer2.Length) + { + return 0; + } + int j = offset; + for (int i = 0; i < buffer2.Length; i++, j++) + { + if (buffer2[i] != buffer[j]) + { + return 0; + } + } + return buffer2.Length; + } + + + public static bool MatchAttributeNode(byte[] buffer, int offset, int size) + { + const XmlBinaryNodeType minAttribute = XmlBinaryNodeType.ShortAttribute; + const XmlBinaryNodeType maxAttribute = XmlBinaryNodeType.DictionaryAttribute; + if (size < 1) + { + return false; + } + XmlBinaryNodeType nodeType = (XmlBinaryNodeType)buffer[offset]; + return nodeType >= minAttribute && nodeType <= maxAttribute; + } + + public static int MatchKey(byte[] buffer, int offset, int size) + { + return MatchInt32(buffer, offset, size); + } + + public static int MatchInt32(byte[] buffer, int offset, int size) + { + if (size > 0) + { + if ((buffer[offset] & 0x80) == 0) + { + return 1; + } + } + if (size > 1) + { + if ((buffer[offset + 1] & 0x80) == 0) + { + return 2; + } + } + if (size > 2) + { + if ((buffer[offset + 2] & 0x80) == 0) + { + return 3; + } + } + if (size > 3) + { + if ((buffer[offset + 3] & 0x80) == 0) + { + return 4; + } + } + + return 0; + } + + public static int MatchUniqueID(byte[] buffer, int offset, int size) + { + if (size < 16) + { + return 0; + } + return 16; + } + } + + class MessagePatterns + { + static readonly byte[] commonFragment; // + static readonly byte[] requestFragment1; // + static readonly byte[] requestFragment2; // ...session-to-key + static readonly byte[] responseFragment1; // + static readonly byte[] responseFragment2; // static-anonymous-key + static readonly byte[] bodyFragment; // + const int ToValueSessionKey = 1; + + IXmlDictionary dictionary; + XmlBinaryReaderSession readerSession; + ToHeader toHeader; + MessageVersion messageVersion; + + static MessagePatterns() + { + BinaryFormatBuilder builder = new BinaryFormatBuilder(); + + MessageDictionary messageDictionary = XD.MessageDictionary; + Message12Dictionary message12Dictionary = XD.Message12Dictionary; + AddressingDictionary addressingDictionary = XD.AddressingDictionary; + Addressing10Dictionary addressing10Dictionary = XD.Addressing10Dictionary; + + char messagePrefix = MessageStrings.Prefix[0]; + char addressingPrefix = AddressingStrings.Prefix[0]; + + // + builder.AppendPrefixDictionaryElement(messagePrefix, builder.GetStaticKey(messageDictionary.Envelope.Key)); + builder.AppendDictionaryXmlnsAttribute(messagePrefix, builder.GetStaticKey(message12Dictionary.Namespace.Key)); + builder.AppendDictionaryXmlnsAttribute(addressingPrefix, builder.GetStaticKey(addressing10Dictionary.Namespace.Key)); + + // + builder.AppendPrefixDictionaryElement(messagePrefix, builder.GetStaticKey(messageDictionary.Header.Key)); + + // ... + builder.AppendPrefixDictionaryElement(addressingPrefix, builder.GetStaticKey(addressingDictionary.Action.Key)); + builder.AppendPrefixDictionaryAttribute(messagePrefix, builder.GetStaticKey(messageDictionary.MustUnderstand.Key), '1'); + builder.AppendDictionaryTextWithEndElement(); + commonFragment = builder.ToByteArray(); + + // ... + builder.AppendPrefixDictionaryElement(addressingPrefix, builder.GetStaticKey(addressingDictionary.MessageId.Key)); + builder.AppendUniqueIDWithEndElement(); + requestFragment1 = builder.ToByteArray(); + + // static-anonymous-key + builder.AppendPrefixDictionaryElement(addressingPrefix, builder.GetStaticKey(addressingDictionary.ReplyTo.Key)); + builder.AppendPrefixDictionaryElement(addressingPrefix, builder.GetStaticKey(addressingDictionary.Address.Key)); + builder.AppendDictionaryTextWithEndElement(builder.GetStaticKey(addressing10Dictionary.Anonymous.Key)); + builder.AppendEndElement(); + + // session-to-key + builder.AppendPrefixDictionaryElement(addressingPrefix, builder.GetStaticKey(addressingDictionary.To.Key)); + builder.AppendPrefixDictionaryAttribute(messagePrefix, builder.GetStaticKey(messageDictionary.MustUnderstand.Key), '1'); + builder.AppendDictionaryTextWithEndElement(builder.GetSessionKey(ToValueSessionKey)); + + // + builder.AppendEndElement(); + + // + builder.AppendPrefixDictionaryElement(messagePrefix, builder.GetStaticKey(messageDictionary.Body.Key)); + requestFragment2 = builder.ToByteArray(); + + // ... + builder.AppendPrefixDictionaryElement(addressingPrefix, builder.GetStaticKey(addressingDictionary.RelatesTo.Key)); + builder.AppendUniqueIDWithEndElement(); + responseFragment1 = builder.ToByteArray(); + + // static-anonymous-key + builder.AppendPrefixDictionaryElement(addressingPrefix, builder.GetStaticKey(addressingDictionary.To.Key)); + builder.AppendPrefixDictionaryAttribute(messagePrefix, builder.GetStaticKey(messageDictionary.MustUnderstand.Key), '1'); + builder.AppendDictionaryTextWithEndElement(builder.GetStaticKey(addressing10Dictionary.Anonymous.Key)); + + // + builder.AppendEndElement(); + + // + builder.AppendPrefixDictionaryElement(messagePrefix, builder.GetStaticKey(messageDictionary.Body.Key)); + responseFragment2 = builder.ToByteArray(); + + // + builder.AppendPrefixDictionaryElement(messagePrefix, builder.GetStaticKey(messageDictionary.Envelope.Key)); + builder.AppendDictionaryXmlnsAttribute(messagePrefix, builder.GetStaticKey(message12Dictionary.Namespace.Key)); + builder.AppendDictionaryXmlnsAttribute(addressingPrefix, builder.GetStaticKey(addressing10Dictionary.Namespace.Key)); + + // + builder.AppendPrefixDictionaryElement(messagePrefix, builder.GetStaticKey(messageDictionary.Body.Key)); + bodyFragment = builder.ToByteArray(); + } + + public MessagePatterns(IXmlDictionary dictionary, XmlBinaryReaderSession readerSession, MessageVersion messageVersion) + { + this.dictionary = dictionary; + this.readerSession = readerSession; + this.messageVersion = messageVersion; + } + + public Message TryCreateMessage(byte[] buffer, int offset, int size, BufferManager bufferManager, BufferedMessageData messageData) + { + RelatesToHeader relatesToHeader; + MessageIDHeader messageIDHeader; + XmlDictionaryString toString; + + int currentOffset = offset; + int remainingSize = size; + + int bytesMatched = BinaryFormatParser.MatchBytes(buffer, currentOffset, remainingSize, commonFragment); + if (bytesMatched == 0) + { + return null; + } + currentOffset += bytesMatched; + remainingSize -= bytesMatched; + + bytesMatched = BinaryFormatParser.MatchKey(buffer, currentOffset, remainingSize); + if (bytesMatched == 0) + { + return null; + } + int actionOffset = currentOffset; + int actionSize = bytesMatched; + currentOffset += bytesMatched; + remainingSize -= bytesMatched; + + int totalBytesMatched; + + bytesMatched = BinaryFormatParser.MatchBytes(buffer, currentOffset, remainingSize, requestFragment1); + if (bytesMatched != 0) + { + currentOffset += bytesMatched; + remainingSize -= bytesMatched; + + bytesMatched = BinaryFormatParser.MatchUniqueID(buffer, currentOffset, remainingSize); + if (bytesMatched == 0) + { + return null; + } + int messageIDOffset = currentOffset; + int messageIDSize = bytesMatched; + currentOffset += bytesMatched; + remainingSize -= bytesMatched; + + bytesMatched = BinaryFormatParser.MatchBytes(buffer, currentOffset, remainingSize, requestFragment2); + if (bytesMatched == 0) + { + return null; + } + currentOffset += bytesMatched; + remainingSize -= bytesMatched; + + if (BinaryFormatParser.MatchAttributeNode(buffer, currentOffset, remainingSize)) + { + return null; + } + + UniqueId messageId = BinaryFormatParser.ParseUniqueID(buffer, messageIDOffset, messageIDSize); + messageIDHeader = MessageIDHeader.Create(messageId, messageVersion.Addressing); + relatesToHeader = null; + + if (!readerSession.TryLookup(ToValueSessionKey, out toString)) + { + return null; + } + + totalBytesMatched = requestFragment1.Length + messageIDSize + requestFragment2.Length; + } + else + { + bytesMatched = BinaryFormatParser.MatchBytes(buffer, currentOffset, remainingSize, responseFragment1); + + if (bytesMatched == 0) + { + return null; + } + + currentOffset += bytesMatched; + remainingSize -= bytesMatched; + + bytesMatched = BinaryFormatParser.MatchUniqueID(buffer, currentOffset, remainingSize); + if (bytesMatched == 0) + { + return null; + } + int messageIDOffset = currentOffset; + int messageIDSize = bytesMatched; + currentOffset += bytesMatched; + remainingSize -= bytesMatched; + + bytesMatched = BinaryFormatParser.MatchBytes(buffer, currentOffset, remainingSize, responseFragment2); + if (bytesMatched == 0) + { + return null; + } + currentOffset += bytesMatched; + remainingSize -= bytesMatched; + + if (BinaryFormatParser.MatchAttributeNode(buffer, currentOffset, remainingSize)) + { + return null; + } + + UniqueId messageId = BinaryFormatParser.ParseUniqueID(buffer, messageIDOffset, messageIDSize); + relatesToHeader = RelatesToHeader.Create(messageId, messageVersion.Addressing); + messageIDHeader = null; + toString = XD.Addressing10Dictionary.Anonymous; + + totalBytesMatched = responseFragment1.Length + messageIDSize + responseFragment2.Length; + } + + totalBytesMatched += commonFragment.Length + actionSize; + + int actionKey = BinaryFormatParser.ParseKey(buffer, actionOffset, actionSize); + + XmlDictionaryString actionString; + if (!TryLookupKey(actionKey, out actionString)) + { + return null; + } + + ActionHeader actionHeader = ActionHeader.Create(actionString, messageVersion.Addressing); + + if (toHeader == null) + { + toHeader = ToHeader.Create(new Uri(toString.Value), messageVersion.Addressing); + } + + int abandonedSize = totalBytesMatched - bodyFragment.Length; + + offset += abandonedSize; + size -= abandonedSize; + + Buffer.BlockCopy(bodyFragment, 0, buffer, offset, bodyFragment.Length); + + messageData.Open(new ArraySegment(buffer, offset, size), bufferManager); + + PatternMessage patternMessage = new PatternMessage(messageData, messageVersion); + + MessageHeaders headers = patternMessage.Headers; + headers.AddActionHeader(actionHeader); + if (messageIDHeader != null) + { + headers.AddMessageIDHeader(messageIDHeader); + headers.AddReplyToHeader(ReplyToHeader.AnonymousReplyTo10); + } + else + { + headers.AddRelatesToHeader(relatesToHeader); + } + headers.AddToHeader(toHeader); + + return patternMessage; + } + + bool TryLookupKey(int key, out XmlDictionaryString result) + { + if (BinaryFormatParser.IsSessionKey(key)) + { + return readerSession.TryLookup(BinaryFormatParser.GetSessionKey(key), out result); + } + else + { + return dictionary.TryLookup(BinaryFormatParser.GetStaticKey(key), out result); + } + } + + sealed class PatternMessage : ReceivedMessage + { + IBufferedMessageData messageData; + MessageHeaders headers; + RecycledMessageState recycledMessageState; + MessageProperties properties; + XmlDictionaryReader reader; + + public PatternMessage(IBufferedMessageData messageData, MessageVersion messageVersion) + { + this.messageData = messageData; + recycledMessageState = messageData.TakeMessageState(); + if (recycledMessageState == null) + { + recycledMessageState = new RecycledMessageState(); + } + properties = recycledMessageState.TakeProperties(); + if (properties == null) + { + properties = new MessageProperties(); + } + headers = recycledMessageState.TakeHeaders(); + if (headers == null) + { + headers = new MessageHeaders(messageVersion); + } + else + { + headers.Init(messageVersion); + } + XmlDictionaryReader reader = messageData.GetMessageReader(); + reader.ReadStartElement(); + VerifyStartBody(reader, messageVersion.Envelope); + ReadStartBody(reader); + this.reader = reader; + } + + public PatternMessage(IBufferedMessageData messageData, MessageVersion messageVersion, + KeyValuePair[] properties, MessageHeaders headers) + { + this.messageData = messageData; + this.messageData.Open(); + recycledMessageState = this.messageData.TakeMessageState(); + if (recycledMessageState == null) + { + recycledMessageState = new RecycledMessageState(); + } + + this.properties = recycledMessageState.TakeProperties(); + if (this.properties == null) + { + this.properties = new MessageProperties(); + } + if (properties != null) + { + this.properties.CopyProperties(properties); + } + + this.headers = recycledMessageState.TakeHeaders(); + if (this.headers == null) + { + this.headers = new MessageHeaders(messageVersion); + } + if (headers != null) + { + this.headers.CopyHeadersFrom(headers); + } + + XmlDictionaryReader reader = messageData.GetMessageReader(); + reader.ReadStartElement(); + VerifyStartBody(reader, messageVersion.Envelope); + ReadStartBody(reader); + this.reader = reader; + } + + + public override MessageHeaders Headers + { + get + { + if (IsDisposed) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateMessageDisposedException()); + } + return headers; + } + } + + public override MessageProperties Properties + { + get + { + if (IsDisposed) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateMessageDisposedException()); + } + return properties; + } + } + + public override MessageVersion Version + { + get + { + if (IsDisposed) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateMessageDisposedException()); + } + return headers.MessageVersion; + } + } + + internal override RecycledMessageState RecycledMessageState + { + get { return recycledMessageState; } + } + + XmlDictionaryReader GetBufferedReaderAtBody() + { + XmlDictionaryReader reader = messageData.GetMessageReader(); + reader.ReadStartElement(); + reader.ReadStartElement(); + return reader; + } + + protected override void OnBodyToString(XmlDictionaryWriter writer) + { + using (XmlDictionaryReader reader = GetBufferedReaderAtBody()) + { + while (reader.NodeType != XmlNodeType.EndElement) + { + writer.WriteNode(reader, false); + } + } + } + + protected override void OnClose() + { + Exception ex = null; + try + { + base.OnClose(); + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + ex = e; + } + + try + { + properties.Dispose(); + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + if (ex == null) + { + ex = e; + } + } + + try + { + if (reader != null) + { + reader.Dispose(); + } + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + if (ex == null) + { + ex = e; + } + } + + try + { + recycledMessageState.ReturnHeaders(headers); + recycledMessageState.ReturnProperties(properties); + messageData.ReturnMessageState(recycledMessageState); + recycledMessageState = null; + messageData.Close(); + messageData = null; + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + if (ex == null) + { + ex = e; + } + } + + if (ex != null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(ex); + } + } + + protected override MessageBuffer OnCreateBufferedCopy(int maxBufferSize) + { + KeyValuePair[] properties = new KeyValuePair[Properties.Count]; + ((ICollection>)Properties).CopyTo(properties, 0); + messageData.EnableMultipleUsers(); + return new PatternMessageBuffer(messageData, Version, properties, headers); + } + + protected override XmlDictionaryReader OnGetReaderAtBodyContents() + { + XmlDictionaryReader reader = this.reader; + this.reader = null; + return reader; + } + + protected override string OnGetBodyAttribute(string localName, string ns) + { + return null; + } + } + + class PatternMessageBuffer : MessageBuffer + { + bool closed; + MessageHeaders headers; + IBufferedMessageData messageDataAtBody; + MessageVersion messageVersion; + KeyValuePair[] properties; + object thisLock = new object(); + RecycledMessageState recycledMessageState; + + public PatternMessageBuffer(IBufferedMessageData messageDataAtBody, MessageVersion messageVersion, + KeyValuePair[] properties, MessageHeaders headers) + { + this.messageDataAtBody = messageDataAtBody; + this.messageDataAtBody.Open(); + + recycledMessageState = this.messageDataAtBody.TakeMessageState(); + if (recycledMessageState == null) + { + recycledMessageState = new RecycledMessageState(); + } + + this.headers = recycledMessageState.TakeHeaders(); + if (this.headers == null) + { + this.headers = new MessageHeaders(messageVersion); + } + this.headers.CopyHeadersFrom(headers); + this.properties = properties; + this.messageVersion = messageVersion; + } + + public override int BufferSize + { + get + { + lock (ThisLock) + { + if (closed) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateBufferDisposedException()); + } + + return messageDataAtBody.Buffer.Count; + } + } + } + + object ThisLock + { + get + { + return thisLock; + } + } + + public override void Close() + { + lock (thisLock) + { + if (!closed) + { + closed = true; + recycledMessageState.ReturnHeaders(headers); + messageDataAtBody.ReturnMessageState(recycledMessageState); + messageDataAtBody.Close(); + recycledMessageState = null; + messageDataAtBody = null; + properties = null; + messageVersion = null; + headers = null; + } + } + } + + public override Message CreateMessage() + { + lock (ThisLock) + { + if (closed) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateBufferDisposedException()); + } + + return new PatternMessage(messageDataAtBody, messageVersion, properties, + headers); + } + } + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/BinaryMessageEncodingBindingElement.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/BinaryMessageEncodingBindingElement.cs new file mode 100644 index 000000000..0ba6ab8e2 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/BinaryMessageEncodingBindingElement.cs @@ -0,0 +1,281 @@ +using System; +using System.ComponentModel; +using System.Xml; +using CoreWCF.Runtime; + +namespace CoreWCF.Channels +{ + public sealed class BinaryMessageEncodingBindingElement : MessageEncodingBindingElement + { + int maxReadPoolSize; + int maxWritePoolSize; + XmlDictionaryReaderQuotas readerQuotas; + int maxSessionSize; + BinaryVersion binaryVersion; + MessageVersion messageVersion; + CompressionFormat compressionFormat; + long maxReceivedMessageSize; + + public BinaryMessageEncodingBindingElement() + { + maxReadPoolSize = EncoderDefaults.MaxReadPoolSize; + maxWritePoolSize = EncoderDefaults.MaxWritePoolSize; + readerQuotas = new XmlDictionaryReaderQuotas(); + EncoderDefaults.ReaderQuotas.CopyTo(readerQuotas); + maxSessionSize = BinaryEncoderDefaults.MaxSessionSize; + binaryVersion = BinaryEncoderDefaults.BinaryVersion; + messageVersion = MessageVersion.CreateVersion(BinaryEncoderDefaults.EnvelopeVersion); + compressionFormat = EncoderDefaults.DefaultCompressionFormat; + } + + BinaryMessageEncodingBindingElement(BinaryMessageEncodingBindingElement elementToBeCloned) + : base(elementToBeCloned) + { + maxReadPoolSize = elementToBeCloned.maxReadPoolSize; + maxWritePoolSize = elementToBeCloned.maxWritePoolSize; + readerQuotas = new XmlDictionaryReaderQuotas(); + elementToBeCloned.readerQuotas.CopyTo(readerQuotas); + MaxSessionSize = elementToBeCloned.MaxSessionSize; + BinaryVersion = elementToBeCloned.BinaryVersion; + messageVersion = elementToBeCloned.messageVersion; + CompressionFormat = elementToBeCloned.CompressionFormat; + maxReceivedMessageSize = elementToBeCloned.maxReceivedMessageSize; + } + + [DefaultValue(EncoderDefaults.DefaultCompressionFormat)] + public CompressionFormat CompressionFormat + { + get + { + return compressionFormat; + } + set + { + compressionFormat = value; + } + } + + /* public */ + BinaryVersion BinaryVersion + { + get + { + return binaryVersion; + } + set + { + if (value == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException(nameof(value))); + } + binaryVersion = value; + } + } + + public override MessageVersion MessageVersion + { + get { return messageVersion; } + set + { + if (value == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value"); + } + if (value.Envelope != BinaryEncoderDefaults.EnvelopeVersion) + { + string errorMsg = SR.Format(SR.UnsupportedEnvelopeVersion, GetType().FullName, BinaryEncoderDefaults.EnvelopeVersion, value.Envelope); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(errorMsg)); + } + + messageVersion = MessageVersion.CreateVersion(BinaryEncoderDefaults.EnvelopeVersion, value.Addressing); + } + } + + [DefaultValue(EncoderDefaults.MaxReadPoolSize)] + public int MaxReadPoolSize + { + get + { + return maxReadPoolSize; + } + set + { + if (value <= 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(value), value, + SR.ValueMustBePositive)); + } + maxReadPoolSize = value; + } + } + + [DefaultValue(EncoderDefaults.MaxWritePoolSize)] + public int MaxWritePoolSize + { + get + { + return maxWritePoolSize; + } + set + { + if (value <= 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(value), value, + SR.ValueMustBePositive)); + } + maxWritePoolSize = value; + } + } + + public XmlDictionaryReaderQuotas ReaderQuotas + { + get + { + return readerQuotas; + } + set + { + if (value == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value"); + value.CopyTo(readerQuotas); + } + } + + [DefaultValue(BinaryEncoderDefaults.MaxSessionSize)] + public int MaxSessionSize + { + get + { + return maxSessionSize; + } + set + { + if (value < 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(value), value, + SR.ValueMustBeNonNegative)); + } + + maxSessionSize = value; + } + } + + private void VerifyCompression(BindingContext context) + { + if (compressionFormat != CompressionFormat.None) + { + //ITransportCompressionSupport compressionSupport = context.GetInnerProperty(); + //if (compressionSupport == null || !compressionSupport.IsCompressionFormatSupported(this.compressionFormat)) + //{ + throw Fx.Exception.AsError(new NotSupportedException(SR.Format( + SR.TransportDoesNotSupportCompression,compressionFormat.ToString(), + GetType().Name, + CompressionFormat.None.ToString()))); + //} + } + } + + void SetMaxReceivedMessageSizeFromTransport(BindingContext context) + { + TransportBindingElement transport = context.Binding.Elements.Find(); + if (transport != null) + { + // We are guaranteed that a transport exists when building a binding; + // Allow the regular flow/checks to happen rather than throw here + // (InternalBuildChannelListener will call into the BindingContext. Validation happens there and it will throw) + maxReceivedMessageSize = transport.MaxReceivedMessageSize; + } + } + + public override IChannelListener BuildChannelListener(BindingContext context) + { + VerifyCompression(context); + SetMaxReceivedMessageSizeFromTransport(context); + return InternalBuildChannelListener(context); + } + + public override bool CanBuildChannelListener(BindingContext context) + { + return InternalCanBuildChannelListener(context); + } + + public override BindingElement Clone() + { + return new BinaryMessageEncodingBindingElement(this); + } + + public override MessageEncoderFactory CreateMessageEncoderFactory() + { + return new BinaryMessageEncoderFactory( + MessageVersion, + MaxReadPoolSize, + MaxWritePoolSize, + MaxSessionSize, + ReaderQuotas, + maxReceivedMessageSize, + BinaryVersion, + CompressionFormat); + } + + public override T GetProperty(BindingContext context) + { + if (context == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("context"); + } + if (typeof(T) == typeof(XmlDictionaryReaderQuotas)) + { + return (T)(object)readerQuotas; + } + else + { + return base.GetProperty(context); + } + } + + protected override bool IsMatch(BindingElement b) + { + if (!base.IsMatch(b)) + return false; + + BinaryMessageEncodingBindingElement binary = b as BinaryMessageEncodingBindingElement; + if (binary == null) + return false; + if (maxReadPoolSize != binary.MaxReadPoolSize) + return false; + if (maxWritePoolSize != binary.MaxWritePoolSize) + return false; + + // compare XmlDictionaryReaderQuotas + if (readerQuotas.MaxStringContentLength != binary.ReaderQuotas.MaxStringContentLength) + return false; + if (readerQuotas.MaxArrayLength != binary.ReaderQuotas.MaxArrayLength) + return false; + if (readerQuotas.MaxBytesPerRead != binary.ReaderQuotas.MaxBytesPerRead) + return false; + if (readerQuotas.MaxDepth != binary.ReaderQuotas.MaxDepth) + return false; + if (readerQuotas.MaxNameTableCharCount != binary.ReaderQuotas.MaxNameTableCharCount) + return false; + + if (MaxSessionSize != binary.MaxSessionSize) + return false; + if (CompressionFormat != binary.CompressionFormat) + return false; + return true; + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public bool ShouldSerializeReaderQuotas() + { + return (!EncoderDefaults.IsDefaultReaderQuotas(ReaderQuotas)); + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public bool ShouldSerializeMessageVersion() + { + return (!messageVersion.IsMatch(MessageVersion.Default)); + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/BinaryVersion.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/BinaryVersion.cs new file mode 100644 index 000000000..f4261718a --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/BinaryVersion.cs @@ -0,0 +1,27 @@ +using System.Xml; + +namespace CoreWCF.Channels +{ + class BinaryVersion + { + static public readonly BinaryVersion Version1 = new BinaryVersion(FramingEncodingString.Binary, FramingEncodingString.BinarySession, ServiceModelDictionary.Version1); + static public readonly BinaryVersion GZipVersion1 = new BinaryVersion(FramingEncodingString.ExtendedBinaryGZip, FramingEncodingString.ExtendedBinarySessionGZip, ServiceModelDictionary.Version1); + static public readonly BinaryVersion DeflateVersion1 = new BinaryVersion(FramingEncodingString.ExtendedBinaryDeflate, FramingEncodingString.ExtendedBinarySessionDeflate, ServiceModelDictionary.Version1); + + string contentType; + string sessionContentType; + IXmlDictionary dictionary; + + BinaryVersion(string contentType, string sessionContentType, IXmlDictionary dictionary) + { + this.contentType = contentType; + this.sessionContentType = sessionContentType; + this.dictionary = dictionary; + } + + static public BinaryVersion CurrentVersion { get { return Version1; } } + public string ContentType { get { return contentType; } } + public string SessionContentType { get { return sessionContentType; } } + public IXmlDictionary Dictionary { get { return dictionary; } } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/Binding.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/Binding.cs new file mode 100644 index 000000000..b579261fd --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/Binding.cs @@ -0,0 +1,258 @@ +using System; +using CoreWCF.Runtime; +using CoreWCF.Description; + +namespace CoreWCF.Channels +{ + public abstract class Binding : IDefaultCommunicationTimeouts + { + TimeSpan closeTimeout = ServiceDefaults.CloseTimeout; + string name; + string namespaceIdentifier; + TimeSpan openTimeout = ServiceDefaults.OpenTimeout; + TimeSpan receiveTimeout = ServiceDefaults.ReceiveTimeout; + TimeSpan sendTimeout = ServiceDefaults.SendTimeout; + internal const string DefaultNamespace = NamingHelper.DefaultNamespace; + + protected Binding() + { + name = null; + namespaceIdentifier = DefaultNamespace; + } + + protected Binding(string name, string ns) + { + if (string.IsNullOrEmpty(name)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("name", SR.SFXBindingNameCannotBeNullOrEmpty); + } + if (ns == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("ns"); + } + + if (ns.Length > 0) + { + NamingHelper.CheckUriParameter(ns, "ns"); + } + + this.name = name; + namespaceIdentifier = ns; + } + + public TimeSpan CloseTimeout + { + get { return closeTimeout; } + set + { + if (value < TimeSpan.Zero) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(value), value, SR.SFxTimeoutOutOfRange0)); + } + if (TimeoutHelper.IsTooLarge(value)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(value), value, SR.SFxTimeoutOutOfRangeTooBig)); + } + + closeTimeout = value; + } + } + + public string Name + { + get + { + if (name == null) + name = GetType().Name; + + return name; + } + set + { + if (string.IsNullOrEmpty(value)) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("value", SR.SFXBindingNameCannotBeNullOrEmpty); + + name = value; + } + } + + public string Namespace + { + get { return namespaceIdentifier; } + set + { + if (value == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value"); + } + + if (value.Length > 0) + { + NamingHelper.CheckUriProperty(value, "Namespace"); + } + namespaceIdentifier = value; + } + } + + public TimeSpan OpenTimeout + { + get { return openTimeout; } + set + { + if (value < TimeSpan.Zero) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(value), value, SR.SFxTimeoutOutOfRange0)); + } + if (TimeoutHelper.IsTooLarge(value)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(value), value, SR.SFxTimeoutOutOfRangeTooBig)); + } + + openTimeout = value; + } + } + + public TimeSpan ReceiveTimeout + { + get { return receiveTimeout; } + set + { + if (value < TimeSpan.Zero) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(value), value, SR.SFxTimeoutOutOfRange0)); + } + if (TimeoutHelper.IsTooLarge(value)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(value), value, SR.SFxTimeoutOutOfRangeTooBig)); + } + + receiveTimeout = value; + } + } + + public abstract string Scheme { get; } + + public MessageVersion MessageVersion + { + get + { + return GetProperty(new BindingParameterCollection()); + } + } + + public TimeSpan SendTimeout + { + get { return sendTimeout; } + set + { + if (value < TimeSpan.Zero) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(value), value, SR.SFxTimeoutOutOfRange0)); + } + if (TimeoutHelper.IsTooLarge(value)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(value), value, SR.SFxTimeoutOutOfRangeTooBig)); + } + + sendTimeout = value; + } + } + + void ValidateSecurityCapabilities(ISecurityCapabilities runtimeSecurityCapabilities, BindingParameterCollection parameters) + { + ISecurityCapabilities bindingSecurityCapabilities = GetProperty(parameters); + + if (!SecurityCapabilities.IsEqual(bindingSecurityCapabilities, runtimeSecurityCapabilities)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new InvalidOperationException(SR.Format(SR.SecurityCapabilitiesMismatched, this))); + } + } + + public virtual IChannelListener BuildChannelListener(Uri listenUriBaseAddress, string listenUriRelativeAddress, ListenUriMode listenUriMode, BindingParameterCollection parameters) + where TChannel : class, IChannel + { + EnsureInvariants(); + BindingContext context = new BindingContext(new CustomBinding(this), parameters, listenUriBaseAddress, listenUriRelativeAddress, listenUriMode); + IChannelListener channelListener = context.BuildInnerChannelListener(); + context.ValidateBindingElementsConsumed(); + ValidateSecurityCapabilities(channelListener.GetProperty(), parameters); + + return channelListener; + } + + internal bool CanBuildChannelListener(params object[] parameters) where TChannel : class, IChannel + { + return CanBuildChannelListener(new BindingParameterCollection(parameters)); + } + + public virtual bool CanBuildChannelListener(BindingParameterCollection parameters) where TChannel : class, IChannel + { + BindingContext context = new BindingContext(new CustomBinding(this), parameters); + return context.CanBuildInnerChannelListener(); + } + + //public CoreWCF.Channels.IChannelFactory BuildChannelFactory(params object[] parameters) { return default(CoreWCF.Channels.IChannelFactory); } // Client + //public virtual CoreWCF.Channels.IChannelFactory BuildChannelFactory(CoreWCF.Channels.BindingParameterCollection parameters) { return default(CoreWCF.Channels.IChannelFactory); } // Client + //public bool CanBuildChannelFactory(params object[] parameters) { return default(bool); } + //public virtual bool CanBuildChannelFactory(CoreWCF.Channels.BindingParameterCollection parameters) { return default(bool); } + + public abstract BindingElementCollection CreateBindingElements(); + + public T GetProperty(BindingParameterCollection parameters) + where T : class + { + BindingContext context = new BindingContext(new CustomBinding(this), parameters); + return context.GetInnerProperty(); + } + + void EnsureInvariants() + { + EnsureInvariants(null); + } + + internal void EnsureInvariants(string contractName) + { + BindingElementCollection elements = CreateBindingElements(); + TransportBindingElement transport = null; + int index; + for (index = 0; index < elements.Count; index++) + { + transport = elements[index] as TransportBindingElement; + if (transport != null) + break; + } + + if (transport == null) + { + if (contractName == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException( + SR.Format(SR.CustomBindingRequiresTransport, Name))); + } + else + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException( + SR.Format(SR.SFxCustomBindingNeedsTransport1, contractName))); + } + } + if (index != elements.Count - 1) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException( + SR.Format(SR.TransportBindingElementMustBeLast, Name, transport.GetType().Name))); + } + if (string.IsNullOrEmpty(transport.Scheme)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException( + SR.Format(SR.InvalidBindingScheme, transport.GetType().Name))); + } + + if (MessageVersion == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException( + SR.Format(SR.MessageVersionMissingFromBinding, Name))); + } + } + + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/BindingContext.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/BindingContext.cs new file mode 100644 index 000000000..938218c2e --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/BindingContext.cs @@ -0,0 +1,152 @@ +using System; +using System.Globalization; +using System.Text; +using CoreWCF.Description; + +namespace CoreWCF.Channels +{ + public class BindingContext + { + CustomBinding _binding; + BindingParameterCollection _bindingParameters; + Uri _listenUriBaseAddress; + ListenUriMode _listenUriMode; + string _listenUriRelativeAddress; + BindingElementCollection _remainingBindingElements; // kept to ensure each BE builds itself once + + public BindingContext(CustomBinding binding, BindingParameterCollection parameters) + : this(binding, parameters, null, string.Empty, ListenUriMode.Explicit) + { + } + + public BindingContext(CustomBinding binding, BindingParameterCollection parameters, Uri listenUriBaseAddress, string listenUriRelativeAddress, ListenUriMode listenUriMode) + { + if (binding == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(binding)); + } + if (listenUriRelativeAddress == null) + { + listenUriRelativeAddress = string.Empty; + } + if (!ListenUriModeHelper.IsDefined(listenUriMode)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(listenUriMode))); + } + + Initialize(binding, binding.Elements, parameters, listenUriBaseAddress, listenUriRelativeAddress, listenUriMode); + } + + BindingContext(CustomBinding binding, + BindingElementCollection remainingBindingElements, + BindingParameterCollection parameters, + Uri listenUriBaseAddress, + string listenUriRelativeAddress, + ListenUriMode listenUriMode) + { + Initialize(binding, remainingBindingElements, parameters, listenUriBaseAddress, listenUriRelativeAddress, listenUriMode); + } + + private void Initialize(CustomBinding binding, + BindingElementCollection remainingBindingElements, + BindingParameterCollection parameters, + Uri listenUriBaseAddress, + string listenUriRelativeAddress, + ListenUriMode listenUriMode) + { + _binding = binding; + + _remainingBindingElements = new BindingElementCollection(remainingBindingElements); + _bindingParameters = new BindingParameterCollection(parameters); + _listenUriBaseAddress = listenUriBaseAddress; + _listenUriRelativeAddress = listenUriRelativeAddress; + _listenUriMode = listenUriMode; + } + + public CustomBinding Binding => _binding; + + public BindingParameterCollection BindingParameters => _bindingParameters; + + public Uri ListenUriBaseAddress + { + get { return _listenUriBaseAddress; } + set { _listenUriBaseAddress = value; } + } + + public ListenUriMode ListenUriMode + { + get { return _listenUriMode; } + set { _listenUriMode = value; } + } + + public string ListenUriRelativeAddress + { + get { return _listenUriRelativeAddress; } + set { _listenUriRelativeAddress = value; } + } + + public BindingElementCollection RemainingBindingElements => _remainingBindingElements; + //public CoreWCF.Channels.IChannelFactory BuildInnerChannelFactory() { return default(CoreWCF.Channels.IChannelFactory); } // Client + //public bool CanBuildInnerChannelFactory() { return default(bool); } // Client + + public IChannelListener BuildInnerChannelListener() + where TChannel : class, IChannel + { + return RemoveNextElement().BuildChannelListener(this); + } + + public bool CanBuildInnerChannelListener() + where TChannel : class, IChannel + { + BindingContext clone = Clone(); + return clone.RemoveNextElement().CanBuildChannelListener(clone); + } + + public T GetInnerProperty() + where T : class + { + if (_remainingBindingElements.Count == 0) + { + return null; + } + else + { + BindingContext clone = Clone(); + return clone.RemoveNextElement().GetProperty(clone); + } + } + public BindingContext Clone() + { + return new BindingContext(_binding, _remainingBindingElements, _bindingParameters, + _listenUriBaseAddress, _listenUriRelativeAddress, _listenUriMode); + } + + BindingElement RemoveNextElement() + { + BindingElement element = _remainingBindingElements.Remove(); + if (element != null) + return element; + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format( + SR.NoChannelBuilderAvailable, _binding.Name, _binding.Namespace))); + } + + internal void ValidateBindingElementsConsumed() + { + if (RemainingBindingElements.Count != 0) + { + StringBuilder builder = new StringBuilder(); + foreach (BindingElement bindingElement in RemainingBindingElements) + { + if (builder.Length > 0) + { + builder.Append(CultureInfo.CurrentCulture.TextInfo.ListSeparator); + builder.Append(" "); + } + string typeString = bindingElement.GetType().ToString(); + builder.Append(typeString.Substring(typeString.LastIndexOf('.') + 1)); + } + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.NotAllBindingElementsBuilt, builder.ToString()))); + } + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/BindingElement.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/BindingElement.cs new file mode 100644 index 000000000..371ff4358 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/BindingElement.cs @@ -0,0 +1,47 @@ +using CoreWCF.Runtime; + +namespace CoreWCF.Channels +{ + public abstract class BindingElement + { + protected BindingElement() { } +#pragma warning disable RECS0154 // Parameter is never used + protected BindingElement(BindingElement elementToBeCloned) { } +#pragma warning restore RECS0154 // Parameter is never used + + //public virtual CoreWCF.Channels.IChannelFactory BuildChannelFactory(CoreWCF.Channels.BindingContext context) { return default(CoreWCF.Channels.IChannelFactory); } // Client + //public virtual bool CanBuildChannelFactory(CoreWCF.Channels.BindingContext context) { return default(bool); } // Client + + public abstract BindingElement Clone(); + + public virtual IChannelListener BuildChannelListener(BindingContext context) where TChannel : class, IChannel + { + if (context == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("context"); + + return context.BuildInnerChannelListener(); + } + + public virtual bool CanBuildChannelListener(BindingContext context) where TChannel : class, IChannel + { + if (context == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("context"); + + return context.CanBuildInnerChannelListener(); + } + + public abstract T GetProperty(BindingContext context) where T : class; + + internal T GetIndividualProperty() where T : class + { + return GetProperty(new BindingContext(new CustomBinding(), new BindingParameterCollection())); + } + + //TODO: Move back to internal + protected virtual bool IsMatch(BindingElement b) + { + Fx.Assert(true, "Should not be called unless this binding element is used in one of the standard bindings. In which case, please re-implement the IsMatch() method."); + return false; + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/BindingElementCollection.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/BindingElementCollection.cs new file mode 100644 index 000000000..93b3d728e --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/BindingElementCollection.cs @@ -0,0 +1,145 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Reflection; + +namespace CoreWCF.Channels +{ + public class BindingElementCollection : Collection + { + public BindingElementCollection() + { + } + + public BindingElementCollection(IEnumerable elements) + { + if (elements == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(elements)); + + foreach (BindingElement element in elements) + { + Add(element); + } + } + + public BindingElementCollection(BindingElement[] elements) + { + if (elements == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(elements)); + + for (int i = 0; i < elements.Length; i++) + { + Add(elements[i]); + } + } + + // returns a new collection with clones of all the elements + public BindingElementCollection Clone() + { + BindingElementCollection result = new BindingElementCollection(); + for (int i = 0; i < Count; i++) + { + result.Add(this[i].Clone()); + } + return result; + } + + public void AddRange(params BindingElement[] elements) + { + if (elements == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(elements)); + + for (int i = 0; i < elements.Length; i++) + { + Add(elements[i]); + } + } + + public bool Contains(Type bindingElementType) + { + if (bindingElementType == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(bindingElementType)); + + for (int i = 0; i < Count; i++) + { + if (bindingElementType.IsInstanceOfType(this[i])) + return true; + } + return false; + } + + public T Find() + { + return Find(false); + } + + public T Remove() + { + return Find(true); + } + + T Find(bool remove) + { + for (int index = 0; index < Count; index++) + { + if (this[index] is T) + { + T item = (T)(object)this[index]; + if (remove) + { + RemoveAt(index); + } + return item; + } + } + return default(T); + } + + public Collection FindAll() + { + return FindAll(false); + } + + public Collection RemoveAll() + { + return FindAll(true); + } + + Collection FindAll(bool remove) + { + Collection collection = new Collection(); + + for (int index = 0; index < Count; index++) + { + if (this[index] is T) + { + T item = (T)(object)this[index]; + if (remove) + { + RemoveAt(index); + // back up the index so we inspect the new item at this location + index--; + } + collection.Add(item); + } + } + + return collection; + } + protected override void InsertItem(int index, BindingElement item) + { + if (item == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(item)); + + base.InsertItem(index, item); + } + + protected override void SetItem(int index, BindingElement item) + { + if (item == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(item)); + + base.SetItem(index, item); + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/BindingParameterCollection.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/BindingParameterCollection.cs new file mode 100644 index 000000000..5a23d6852 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/BindingParameterCollection.cs @@ -0,0 +1,38 @@ +using CoreWCF.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; + +namespace CoreWCF.Channels +{ + // Some binding elements can sometimes consume extra information when building factories. + // BindingParameterCollection is a collection of objects with this extra information. + // See comments in SecurityBindingElement and TransactionFlowBindingElement for examples + // of binding elements that go looking for certain data in this collection. + public class BindingParameterCollection : KeyedByTypeCollection + { + public BindingParameterCollection() { } + + internal BindingParameterCollection(params object[] parameters) + { + if (parameters == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("parameters"); + + for (int i = 0; i < parameters.Length; i++) + { + base.Add(parameters[i]); + } + } + + internal BindingParameterCollection(BindingParameterCollection parameters) + { + if (parameters == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("parameters"); + + for (int i = 0; i < parameters.Count; i++) + { + Add(parameters[i]); + } + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/BodyWriter.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/BodyWriter.cs new file mode 100644 index 000000000..ad90bd8b5 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/BodyWriter.cs @@ -0,0 +1,141 @@ +using System; +using System.Threading.Tasks; +using System.Xml; + +namespace CoreWCF.Channels +{ + public abstract class BodyWriter + { + bool isBuffered; + bool canWrite; + object thisLock; + + protected BodyWriter(bool isBuffered) + { + this.isBuffered = isBuffered; + canWrite = true; + if (!this.isBuffered) + { + thisLock = new object(); + } + } + + public bool IsBuffered + { + get { return isBuffered; } + } + + internal virtual bool IsEmpty + { + get { return false; } + } + + internal virtual bool IsFault + { + get { return false; } + } + + public BodyWriter CreateBufferedCopy(int maxBufferSize) + { + if (maxBufferSize < 0) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(maxBufferSize), maxBufferSize, + SR.ValueMustBeNonNegative)); + if (isBuffered) + { + return this; + } + else + { + lock (thisLock) + { + if (!canWrite) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.BodyWriterCanOnlyBeWrittenOnce)); + canWrite = false; + } + BodyWriter bodyWriter = OnCreateBufferedCopy(maxBufferSize); + if (!bodyWriter.IsBuffered) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.BodyWriterReturnedIsNotBuffered)); + return bodyWriter; + } + } + + protected virtual BodyWriter OnCreateBufferedCopy(int maxBufferSize) + { + return OnCreateBufferedCopy(maxBufferSize, XmlDictionaryReaderQuotas.Max); + } + + internal BodyWriter OnCreateBufferedCopy(int maxBufferSize, XmlDictionaryReaderQuotas quotas) + { + XmlBuffer buffer = new XmlBuffer(maxBufferSize); + using (XmlDictionaryWriter writer = buffer.OpenSection(quotas)) + { + writer.WriteStartElement("a"); + OnWriteBodyContents(writer); + writer.WriteEndElement(); + } + buffer.CloseSection(); + buffer.Close(); + return new BufferedBodyWriter(buffer); + } + + protected abstract void OnWriteBodyContents(XmlDictionaryWriter writer); + + protected virtual Task OnWriteBodyContentsAsync(XmlDictionaryWriter writer) + { + OnWriteBodyContents(writer); + return Task.CompletedTask; + } + + void EnsureWriteBodyContentsState(XmlDictionaryWriter writer) + { + if (writer == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(writer)); + if (!isBuffered) + { + lock (thisLock) + { + if (!canWrite) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.BodyWriterCanOnlyBeWrittenOnce)); + canWrite = false; + } + } + } + + public void WriteBodyContents(XmlDictionaryWriter writer) + { + EnsureWriteBodyContentsState(writer); + OnWriteBodyContents(writer); + } + + public Task WriteBodyContentsAsync(XmlDictionaryWriter writer) + { + EnsureWriteBodyContentsState(writer); + return OnWriteBodyContentsAsync(writer); + } + + class BufferedBodyWriter : BodyWriter + { + XmlBuffer buffer; + + public BufferedBodyWriter(XmlBuffer buffer) + : base(true) + { + this.buffer = buffer; + } + + protected override void OnWriteBodyContents(XmlDictionaryWriter writer) + { + XmlDictionaryReader reader = buffer.GetReader(0); + using (reader) + { + reader.ReadStartElement(); + while (reader.NodeType != XmlNodeType.EndElement) + { + writer.WriteNode(reader, false); + } + reader.ReadEndElement(); + } + } + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/BufferManager.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/BufferManager.cs new file mode 100644 index 000000000..8f07a7753 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/BufferManager.cs @@ -0,0 +1,107 @@ +using System; +using CoreWCF.Runtime; + +namespace CoreWCF.Channels +{ + public abstract class BufferManager + { + public abstract void ReturnBuffer(byte[] buffer); + public abstract byte[] TakeBuffer(int bufferSize); + public abstract void Clear(); + + public static BufferManager CreateBufferManager(long maxBufferPoolSize, int maxBufferSize) + { + if (maxBufferPoolSize < 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(maxBufferPoolSize), + maxBufferPoolSize, SR.ValueMustBeNonNegative)); + } + + if (maxBufferSize < 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(maxBufferSize), + maxBufferSize, SR.ValueMustBeNonNegative)); + } + + return new WrappingBufferManager(InternalBufferManager.Create(maxBufferPoolSize, maxBufferSize)); + } + + internal static InternalBufferManager GetInternalBufferManager(BufferManager bufferManager) + { + if (bufferManager is WrappingBufferManager) + { + return ((WrappingBufferManager)bufferManager).InternalBufferManager; + } + else + { + return new WrappingInternalBufferManager(bufferManager); + } + } + + class WrappingBufferManager : BufferManager + { + InternalBufferManager innerBufferManager; + + public WrappingBufferManager(InternalBufferManager innerBufferManager) + { + this.innerBufferManager = innerBufferManager; + } + + public InternalBufferManager InternalBufferManager + { + get { return innerBufferManager; } + } + + public override byte[] TakeBuffer(int bufferSize) + { + if (bufferSize < 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(bufferSize), bufferSize, + SR.ValueMustBeNonNegative)); + } + + return innerBufferManager.TakeBuffer(bufferSize); + } + + public override void ReturnBuffer(byte[] buffer) + { + if (buffer == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("buffer"); + } + + innerBufferManager.ReturnBuffer(buffer); + } + + public override void Clear() + { + innerBufferManager.Clear(); + } + } + + class WrappingInternalBufferManager : InternalBufferManager + { + BufferManager innerBufferManager; + + public WrappingInternalBufferManager(BufferManager innerBufferManager) + { + this.innerBufferManager = innerBufferManager; + } + + public override void Clear() + { + innerBufferManager.Clear(); + } + + public override void ReturnBuffer(byte[] buffer) + { + innerBufferManager.ReturnBuffer(buffer); + } + + public override byte[] TakeBuffer(int bufferSize) + { + return innerBufferManager.TakeBuffer(bufferSize); + } + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/BufferManagerOutputStream.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/BufferManagerOutputStream.cs new file mode 100644 index 000000000..deacb6bf3 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/BufferManagerOutputStream.cs @@ -0,0 +1,48 @@ +using System; +using CoreWCF.Runtime; + +namespace CoreWCF.Channels +{ + class BufferManagerOutputStream : BufferedOutputStream + { + string quotaExceededString; + + public BufferManagerOutputStream(string quotaExceededString) + : base() + { + this.quotaExceededString = quotaExceededString; + } + + public BufferManagerOutputStream(string quotaExceededString, int maxSize) + : base(maxSize) + { + this.quotaExceededString = quotaExceededString; + } + + public BufferManagerOutputStream(string quotaExceededString, int initialSize, int maxSize, BufferManager bufferManager) + : base(initialSize, maxSize, BufferManager.GetInternalBufferManager(bufferManager)) + { + this.quotaExceededString = quotaExceededString; + } + + public void Init(int initialSize, int maxSizeQuota, BufferManager bufferManager) + { + Init(initialSize, maxSizeQuota, maxSizeQuota, bufferManager); + } + + public void Init(int initialSize, int maxSizeQuota, int effectiveMaxSize, BufferManager bufferManager) + { + base.Reinitialize(initialSize, maxSizeQuota, effectiveMaxSize, BufferManager.GetInternalBufferManager(bufferManager)); + } + + protected override Exception CreateQuotaExceededException(int maxSizeQuota) + { + string excMsg = SR.Format(quotaExceededString, maxSizeQuota); + //if (TD.MaxSentMessageSizeExceededIsEnabled()) + //{ + // TD.MaxSentMessageSizeExceeded(excMsg); + //} + return new QuotaExceededException(excMsg); + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/BufferedMessageData.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/BufferedMessageData.cs new file mode 100644 index 000000000..e1910b3b4 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/BufferedMessageData.cs @@ -0,0 +1,202 @@ +using System; +using System.Xml; +using CoreWCF.Runtime; + +namespace CoreWCF.Channels +{ + internal abstract class BufferedMessageData : IBufferedMessageData + { + ArraySegment buffer; + BufferManager bufferManager; + int refCount; + int outstandingReaders; + bool multipleUsers; + RecycledMessageState messageState; + SynchronizedPool messageStatePool; + + public BufferedMessageData(SynchronizedPool messageStatePool) + { + this.messageStatePool = messageStatePool; + } + + public ArraySegment Buffer + { + get { return buffer; } + } + + public BufferManager BufferManager + { + get { return bufferManager; } + } + + public virtual XmlDictionaryReaderQuotas Quotas + { + get { return XmlDictionaryReaderQuotas.Max; } + } + + public abstract MessageEncoder MessageEncoder { get; } + + object ThisLock + { + get { return this; } + } + + public void EnableMultipleUsers() + { + multipleUsers = true; + } + + public void Close() + { + if (multipleUsers) + { + lock (ThisLock) + { + if (--refCount == 0) + { + DoClose(); + } + } + } + else + { + DoClose(); + } + } + + void DoClose() + { + bufferManager.ReturnBuffer(buffer.Array); + if (outstandingReaders == 0) + { + bufferManager = null; + buffer = new ArraySegment(); + OnClosed(); + } + } + + public void DoReturnMessageState(RecycledMessageState messageState) + { + if (this.messageState == null) + { + this.messageState = messageState; + } + else + { + messageStatePool.Return(messageState); + } + } + + void DoReturnXmlReader(XmlDictionaryReader reader) + { + ReturnXmlReader(reader); + outstandingReaders--; + } + + public RecycledMessageState DoTakeMessageState() + { + RecycledMessageState messageState = this.messageState; + if (messageState != null) + { + this.messageState = null; + return messageState; + } + else + { + return messageStatePool.Take(); + } + } + + XmlDictionaryReader DoTakeXmlReader() + { + XmlDictionaryReader reader = TakeXmlReader(); + outstandingReaders++; + return reader; + } + + public XmlDictionaryReader GetMessageReader() + { + if (multipleUsers) + { + lock (ThisLock) + { + return DoTakeXmlReader(); + } + } + else + { + return DoTakeXmlReader(); + } + } + + public void OnXmlReaderClosed(XmlDictionaryReader reader) + { + if (multipleUsers) + { + lock (ThisLock) + { + DoReturnXmlReader(reader); + } + } + else + { + DoReturnXmlReader(reader); + } + } + + protected virtual void OnClosed() + { + } + + public RecycledMessageState TakeMessageState() + { + if (multipleUsers) + { + lock (ThisLock) + { + return DoTakeMessageState(); + } + } + else + { + return DoTakeMessageState(); + } + } + + protected abstract XmlDictionaryReader TakeXmlReader(); + + public void Open() + { + lock (ThisLock) + { + refCount++; + } + } + + public void Open(ArraySegment buffer, BufferManager bufferManager) + { + refCount = 1; + this.bufferManager = bufferManager; + this.buffer = buffer; + multipleUsers = false; + } + + protected abstract void ReturnXmlReader(XmlDictionaryReader xmlReader); + + public void ReturnMessageState(RecycledMessageState messageState) + { + if (multipleUsers) + { + lock (ThisLock) + { + DoReturnMessageState(messageState); + } + } + else + { + DoReturnMessageState(messageState); + } + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/BufferedMessageWriter.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/BufferedMessageWriter.cs new file mode 100644 index 000000000..05472bd88 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/BufferedMessageWriter.cs @@ -0,0 +1,94 @@ +using System; +using System.IO; +using System.Xml; + +namespace CoreWCF.Channels +{ + abstract class BufferedMessageWriter + { + int[] sizeHistory; + int sizeHistoryIndex; + const int sizeHistoryCount = 4; + const int expectedSizeVariance = 256; + BufferManagerOutputStream stream; + + public BufferedMessageWriter() + { + stream = new BufferManagerOutputStream(SR.MaxSentMessageSizeExceeded); + InitMessagePredicter(); + } + + protected abstract XmlDictionaryWriter TakeXmlWriter(Stream stream); + protected abstract void ReturnXmlWriter(XmlDictionaryWriter writer); + + public ArraySegment WriteMessage(Message message, BufferManager bufferManager, int initialOffset, int maxSizeQuota) + { + int effectiveMaxSize; + + // make sure that maxSize has room for initialOffset without overflowing, since + // the effective buffer size is message size + initialOffset + if (maxSizeQuota <= int.MaxValue - initialOffset) + effectiveMaxSize = maxSizeQuota + initialOffset; + else + effectiveMaxSize = int.MaxValue; + + int predictedMessageSize = PredictMessageSize(); + if (predictedMessageSize > effectiveMaxSize) + predictedMessageSize = effectiveMaxSize; + else if (predictedMessageSize < initialOffset) + predictedMessageSize = initialOffset; + + try + { + stream.Init(predictedMessageSize, maxSizeQuota, effectiveMaxSize, bufferManager); + stream.Skip(initialOffset); + + XmlDictionaryWriter writer = TakeXmlWriter(stream); + OnWriteStartMessage(writer); + message.WriteMessage(writer); + OnWriteEndMessage(writer); + writer.Flush(); + ReturnXmlWriter(writer); + int size; + byte[] buffer = stream.ToArray(out size); + RecordActualMessageSize(size); + return new ArraySegment(buffer, initialOffset, size - initialOffset); + } + finally + { + stream.Clear(); + } + } + + protected virtual void OnWriteStartMessage(XmlDictionaryWriter writer) + { + } + + protected virtual void OnWriteEndMessage(XmlDictionaryWriter writer) + { + } + + void InitMessagePredicter() + { + sizeHistory = new int[4]; + for (int i = 0; i < sizeHistoryCount; i++) + sizeHistory[i] = 256; + } + + int PredictMessageSize() + { + int max = 0; + for (int i = 0; i < sizeHistoryCount; i++) + if (sizeHistory[i] > max) + max = sizeHistory[i]; + return max + expectedSizeVariance; + } + + void RecordActualMessageSize(int size) + { + sizeHistory[sizeHistoryIndex] = size; + sizeHistoryIndex = (sizeHistoryIndex + 1) % sizeHistoryCount; + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/ChannelAcceptor.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/ChannelAcceptor.cs new file mode 100644 index 000000000..513efbfcb --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/ChannelAcceptor.cs @@ -0,0 +1,51 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace CoreWCF.Channels +{ + public abstract class ChannelAcceptor : CommunicationObject, IChannelAcceptor + where TChannel : class, IChannel + { + ChannelManagerBase channelManager; + + protected ChannelAcceptor(ChannelManagerBase channelManager) + { + this.channelManager = channelManager; + } + + protected ChannelManagerBase ChannelManager + { + get { return channelManager; } + } + + protected override TimeSpan DefaultCloseTimeout + { + get { return channelManager.InternalCloseTimeout; } + } + + protected override TimeSpan DefaultOpenTimeout + { + get { return channelManager.InternalOpenTimeout; } + } + + public abstract Task AcceptChannelAsync(CancellationToken token); + + public abstract Task WaitForChannelAsync(CancellationToken token); + + protected override void OnAbort() + { + } + + protected override Task OnCloseAsync(CancellationToken token) + { + return Task.CompletedTask; + } + + protected override Task OnOpenAsync(CancellationToken token) + { + return Task.CompletedTask; + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/ChannelBase.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/ChannelBase.cs new file mode 100644 index 000000000..f0de6a117 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/ChannelBase.cs @@ -0,0 +1,91 @@ +using System; +using CoreWCF.Diagnostics; + +namespace CoreWCF.Channels +{ + public abstract class ChannelBase : CommunicationObject, IChannel, IDefaultCommunicationTimeouts + { + ChannelManagerBase channelManager; + + protected ChannelBase(ChannelManagerBase channelManager) + { + if (channelManager == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("channelManager"); + } + + this.channelManager = channelManager; + } + + TimeSpan IDefaultCommunicationTimeouts.CloseTimeout + { + get { return DefaultCloseTimeout; } + } + + TimeSpan IDefaultCommunicationTimeouts.OpenTimeout + { + get { return DefaultOpenTimeout; } + } + + TimeSpan IDefaultCommunicationTimeouts.ReceiveTimeout + { + get { return DefaultReceiveTimeout; } + } + + TimeSpan IDefaultCommunicationTimeouts.SendTimeout + { + get { return DefaultSendTimeout; } + } + + protected override TimeSpan DefaultCloseTimeout + { + get { return ((IDefaultCommunicationTimeouts)channelManager).CloseTimeout; } + } + + protected override TimeSpan DefaultOpenTimeout + { + get { return ((IDefaultCommunicationTimeouts)channelManager).OpenTimeout; } + } + + protected TimeSpan DefaultReceiveTimeout + { + get { return ((IDefaultCommunicationTimeouts)channelManager).ReceiveTimeout; } + } + + protected TimeSpan DefaultSendTimeout + { + get { return ((IDefaultCommunicationTimeouts)channelManager).SendTimeout; } + } + + protected ChannelManagerBase Manager + { + get + { + return channelManager; + } + } + + public virtual T GetProperty() where T : class + { + //IChannelFactory factory = this.channelManager as IChannelFactory; + //if (factory != null) + //{ + // return factory.GetProperty(); + //} + + IChannelListener listener = channelManager as IChannelListener; + if (listener != null) + { + return listener.GetProperty(); + } + + return null; + } + + protected override void OnClosed() + { + base.OnClosed(); + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/ChannelBindingMessageProperty.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/ChannelBindingMessageProperty.cs new file mode 100644 index 000000000..8bdaf584f --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/ChannelBindingMessageProperty.cs @@ -0,0 +1,78 @@ +using System; +using System.Security.Authentication.ExtendedProtection; + +namespace CoreWCF.Channels +{ + public sealed class ChannelBindingMessageProperty : IDisposable, IMessageProperty + { + internal const string PropertyName = "ChannelBindingMessageProperty"; + + ChannelBinding channelBinding; + object thisLock; + bool ownsCleanup; + int refCount; + + public ChannelBindingMessageProperty(ChannelBinding channelBinding, bool ownsCleanup) + { + this.channelBinding = channelBinding ?? throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("channelBinding"); + refCount = 1; + thisLock = new object(); + this.ownsCleanup = ownsCleanup; + } + + public static string Name { get { return PropertyName; } } + + bool IsDisposed + { + get + { + return refCount <= 0; + } + } + + public ChannelBinding ChannelBinding + { + get + { + ThrowIfDisposed(); + return channelBinding; + } + } + + IMessageProperty IMessageProperty.CreateCopy() + { + lock (thisLock) + { + ThrowIfDisposed(); + refCount++; + return this; + } + } + + void IDisposable.Dispose() + { + if (!IsDisposed) + { + lock (thisLock) + { + if (!IsDisposed && --refCount == 0) + { + if (ownsCleanup) + { + // Accessing via IDisposable to avoid Security check (functionally the same) + ((IDisposable)channelBinding).Dispose(); + } + } + } + } + } + + void ThrowIfDisposed() + { + if (IsDisposed) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ObjectDisposedException(GetType().FullName)); + } + } + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/ChannelListenerBase.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/ChannelListenerBase.cs new file mode 100644 index 000000000..45d9564e7 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/ChannelListenerBase.cs @@ -0,0 +1,158 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using CoreWCF.Runtime; + +namespace CoreWCF.Channels +{ + public abstract class ChannelListenerBase : ChannelManagerBase, IChannelListener + { + TimeSpan closeTimeout = ServiceDefaults.CloseTimeout; + TimeSpan openTimeout = ServiceDefaults.OpenTimeout; + TimeSpan receiveTimeout = ServiceDefaults.ReceiveTimeout; + TimeSpan sendTimeout = ServiceDefaults.SendTimeout; + + protected ChannelListenerBase() + { + } + + protected ChannelListenerBase(IDefaultCommunicationTimeouts timeouts) + { + if (timeouts != null) + { + closeTimeout = timeouts.CloseTimeout; + openTimeout = timeouts.OpenTimeout; + receiveTimeout = timeouts.ReceiveTimeout; + sendTimeout = timeouts.SendTimeout; + } + } + + protected override TimeSpan DefaultCloseTimeout + { + get { return closeTimeout; } + } + + protected override TimeSpan DefaultOpenTimeout + { + get { return openTimeout; } + } + + protected override TimeSpan DefaultReceiveTimeout + { + get { return receiveTimeout; } + } + + protected override TimeSpan DefaultSendTimeout + { + get { return sendTimeout; } + } + + public abstract Uri Uri { get; } + + public virtual T GetProperty() + where T : class + { + if (typeof(T) == typeof(IChannelListener)) + { + return (T)(object)this; + } + + return default(T); + } + + //public bool WaitForChannel(TimeSpan timeout) + //{ + // this.ThrowIfNotOpened(); + // this.ThrowPending(); + // return this.OnWaitForChannel(timeout); + //} + + //public IAsyncResult BeginWaitForChannel(TimeSpan timeout, AsyncCallback callback, object state) + //{ + // this.ThrowIfNotOpened(); + // this.ThrowPending(); + // return this.OnBeginWaitForChannel(timeout, callback, state); + //} + + //public bool EndWaitForChannel(IAsyncResult result) + //{ + // return this.OnEndWaitForChannel(result); + //} + + //protected abstract bool OnWaitForChannel(TimeSpan timeout); + //protected abstract IAsyncResult OnBeginWaitForChannel(TimeSpan timeout, AsyncCallback callback, object state); + //protected abstract bool OnEndWaitForChannel(IAsyncResult result); + + } + + public abstract class ChannelListenerBase : ChannelListenerBase, IChannelListener + where TChannel : class, IChannel + { + //private AcceptChannelDelegate _acceptHandler; + //private ExceptionHandlerDelegate _exceptionHandler; + //private bool _acceptLoopStarted = false; + + protected ChannelListenerBase() + { + } + + protected ChannelListenerBase(IDefaultCommunicationTimeouts timeouts) + : base(timeouts) + { + } + + protected abstract Task OnAcceptChannelAsync(CancellationToken token); + + //protected abstract IAsyncResult OnBeginAcceptChannel(TimeSpan timeout, AsyncCallback callback, object state); + //protected abstract TChannel OnEndAcceptChannel(IAsyncResult result); + + public Task AcceptChannelAsync() + { + var helper = new TimeoutHelper(InternalReceiveTimeout); + return AcceptChannelAsync(helper.GetCancellationToken()); + } + + public Task AcceptChannelAsync(CancellationToken token) + { + ThrowIfNotOpened(); + ThrowPending(); + return OnAcceptChannelAsync(token); + } + + +//#pragma warning disable RECS0165 // Asynchronous methods should return a Task instead of void +// public async void AcceptChannelLoopAsync(CancellationToken token) +//#pragma warning restore RECS0165 // Asynchronous methods should return a Task instead of void +// { +// if (_acceptLoopStarted) +// return; +// this.ThrowIfNotOpened(); +// this.ThrowPending(); +// do +// { +// try +// { +// TChannel channel = await OnAcceptChannelAsync(token); +// await _acceptHandler(channel); +// } +// catch (Exception e) +// { +// if (_exceptionHandler != null) +// await _exceptionHandler(e); +// } +// } while (!IsDisposed); +// } + +// public virtual void UseHandler(AcceptChannelDelegate handler) +// { +// // TODO: work out how multiple waiters should work. +// _acceptHandler = handler; +// AcceptChannelLoopAsync(CancellationToken.None); +// } + + //public virtual void UseExceptionHandler(ExceptionHandlerDelegate handler) + //{ + // _exceptionHandler = handler; + //} + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/ChannelManagerBase.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/ChannelManagerBase.cs new file mode 100644 index 000000000..774439c8d --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/ChannelManagerBase.cs @@ -0,0 +1,50 @@ +using System; + +namespace CoreWCF.Channels +{ + public abstract class ChannelManagerBase : CommunicationObject, IDefaultCommunicationTimeouts + { + protected ChannelManagerBase() + { + } + + protected abstract TimeSpan DefaultReceiveTimeout { get; } + protected abstract TimeSpan DefaultSendTimeout { get; } + + internal TimeSpan InternalReceiveTimeout + { + get { return DefaultReceiveTimeout; } + } + + internal TimeSpan InternalSendTimeout + { + get { return DefaultSendTimeout; } + } + + TimeSpan IDefaultCommunicationTimeouts.CloseTimeout + { + get { return DefaultCloseTimeout; } + } + + TimeSpan IDefaultCommunicationTimeouts.OpenTimeout + { + get { return DefaultOpenTimeout; } + } + + TimeSpan IDefaultCommunicationTimeouts.ReceiveTimeout + { + get { return DefaultReceiveTimeout; } + } + + TimeSpan IDefaultCommunicationTimeouts.SendTimeout + { + get { return DefaultSendTimeout; } + } + + internal Exception CreateChannelTypeNotSupportedException(Type type) + { + return new ArgumentException(SR.Format(SR.ChannelTypeNotSupported, type), "TChannel"); + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/ChannelRequirements.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/ChannelRequirements.cs new file mode 100644 index 000000000..2e7baef98 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/ChannelRequirements.cs @@ -0,0 +1,379 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CoreWCF.Description; + +namespace CoreWCF.Channels +{ + internal struct ChannelRequirements + { + public bool usesInput; + public bool usesReply; + public bool usesOutput; + public bool usesRequest; + public SessionMode sessionMode; + + public static void ComputeContractRequirements(ContractDescription contractDescription, + out ChannelRequirements requirements) + { + requirements = new ChannelRequirements(); + + requirements.usesInput = false; + requirements.usesReply = false; + requirements.usesOutput = false; + requirements.usesRequest = false; + requirements.sessionMode = contractDescription.SessionMode; + + for (int i = 0; i < contractDescription.Operations.Count; i++) + { + OperationDescription operation = contractDescription.Operations[i]; + bool oneWay = (operation.IsOneWay); + if (!operation.IsServerInitiated()) + { + if (oneWay) + { + requirements.usesInput = true; + } + else + { + requirements.usesReply = true; + } + } + else + { + if (oneWay) + { + requirements.usesOutput = true; + } + else + { + requirements.usesRequest = true; + } + } + } + } + + public static Type[] ComputeRequiredChannels(ref ChannelRequirements requirements) + { + if (requirements.usesOutput || requirements.usesRequest) + { + switch (requirements.sessionMode) + { + case SessionMode.Allowed: + return new[] { + typeof(IDuplexChannel), + typeof(IDuplexSessionChannel), + }; + + case SessionMode.Required: + return new[] { + typeof(IDuplexSessionChannel), + }; + + case SessionMode.NotAllowed: + return new[] { + typeof(IDuplexChannel), + }; + } + } + else if (requirements.usesInput && requirements.usesReply) + { + switch (requirements.sessionMode) + { + case SessionMode.Allowed: + return new[] { + typeof(IRequestChannel), + typeof(IRequestSessionChannel), + typeof(IDuplexChannel), + typeof(IDuplexSessionChannel), + }; + + case SessionMode.Required: + return new[] { + typeof(IRequestSessionChannel), + typeof(IDuplexSessionChannel), + }; + + case SessionMode.NotAllowed: + return new[] { + typeof(IRequestChannel), + typeof(IDuplexChannel), + }; + } + } + else if (requirements.usesInput) + { + switch (requirements.sessionMode) + { + case SessionMode.Allowed: + return new[] { + typeof(IOutputChannel), + typeof(IOutputSessionChannel), + typeof(IRequestChannel), + typeof(IRequestSessionChannel), + typeof(IDuplexChannel), + typeof(IDuplexSessionChannel), + }; + + case SessionMode.Required: + return new[] { + typeof(IOutputSessionChannel), + typeof(IRequestSessionChannel), + typeof(IDuplexSessionChannel), + }; + + case SessionMode.NotAllowed: + return new[] { + typeof(IOutputChannel), + typeof(IRequestChannel), + typeof(IDuplexChannel), + }; + } + } + else if (requirements.usesReply) + { + switch (requirements.sessionMode) + { + case SessionMode.Allowed: + return new[] { + typeof(IRequestChannel), + typeof(IRequestSessionChannel), + typeof(IDuplexChannel), + typeof(IDuplexSessionChannel), + }; + + case SessionMode.Required: + return new[] { + typeof(IRequestSessionChannel), + typeof(IDuplexSessionChannel), + }; + + case SessionMode.NotAllowed: + return new[] { + typeof(IRequestChannel), + typeof(IDuplexChannel), + }; + } + } + else + { + switch (requirements.sessionMode) + { + case SessionMode.Allowed: + return new[] { + typeof(IOutputSessionChannel), + typeof(IOutputChannel), + typeof(IRequestSessionChannel), + typeof(IRequestChannel), + typeof(IDuplexChannel), + typeof(IDuplexSessionChannel), + }; + + case SessionMode.Required: + return new[] { + typeof(IOutputSessionChannel), + typeof(IRequestSessionChannel), + typeof(IDuplexSessionChannel), + }; + + case SessionMode.NotAllowed: + return new[] { + typeof(IOutputChannel), + typeof(IRequestChannel), + typeof(IDuplexChannel), + }; + } + } + return null; + } + + public static bool IsSessionful(Type channelType) + { + return (channelType == typeof(IDuplexSessionChannel) || + channelType == typeof(IOutputSessionChannel) || + channelType == typeof(IInputSessionChannel) || + channelType == typeof(IReplySessionChannel) || + channelType == typeof(IRequestSessionChannel)); + } + + public static bool IsOneWay(Type channelType) + { + return (channelType == typeof(IOutputChannel) || + channelType == typeof(IInputChannel) || + channelType == typeof(IInputSessionChannel) || + channelType == typeof(IOutputSessionChannel)); + } + + public static bool IsRequestReply(Type channelType) + { + return (channelType == typeof(IRequestChannel) || + channelType == typeof(IReplyChannel) || + channelType == typeof(IReplySessionChannel) || + channelType == typeof(IRequestSessionChannel)); + } + + public static bool IsDuplex(Type channelType) + { + return (channelType == typeof(IDuplexChannel) || + channelType == typeof(IDuplexSessionChannel)); + } + + public static Exception CantCreateListenerException(IEnumerable supportedChannels, IEnumerable requiredChannels, string bindingName) + { + string contractChannelTypesString = ""; + string bindingChannelTypesString = ""; + + Exception exception = BindingContractMismatchException(supportedChannels, requiredChannels, bindingName, + ref contractChannelTypesString, ref bindingChannelTypesString); + + if (exception == null) + { + // none of the obvious speculations about the failure holds, so we fall back to the generic error message + exception = new InvalidOperationException(SR.Format(SR.EndpointListenerRequirementsCannotBeMetBy3,bindingName, contractChannelTypesString, bindingChannelTypesString)); + } + + return exception; + } + + public static Exception CantCreateChannelException(IEnumerable supportedChannels, IEnumerable requiredChannels, string bindingName) + { + string contractChannelTypesString = ""; + string bindingChannelTypesString = ""; + + Exception exception = BindingContractMismatchException(supportedChannels, requiredChannels, bindingName, + ref contractChannelTypesString, ref bindingChannelTypesString); + + if (exception == null) + { + // none of the obvious speculations about the failure holds, so we fall back to the generic error message + exception = new InvalidOperationException(SR.Format(SR.CouldnTCreateChannelForType2, bindingName, contractChannelTypesString)); + } + + return exception; + } + + public static Exception BindingContractMismatchException(IEnumerable supportedChannels, IEnumerable requiredChannels, + string bindingName, ref string contractChannelTypesString, ref string bindingChannelTypesString) + { + StringBuilder contractChannelTypes = new StringBuilder(); + bool contractRequiresOneWay = true; + bool contractRequiresRequestReply = true; + bool contractRequiresDuplex = true; + bool contractRequiresTwoWay = true; // request-reply or duplex + bool contractRequiresSession = true; + bool contractRequiresDatagram = true; + foreach (Type channelType in requiredChannels) + { + if (contractChannelTypes.Length > 0) + { + contractChannelTypes.Append(CultureInfo.CurrentCulture.TextInfo.ListSeparator); + contractChannelTypes.Append(" "); + } + string typeString = channelType.ToString(); + contractChannelTypes.Append(typeString.Substring(typeString.LastIndexOf('.') + 1)); + + if (!IsOneWay(channelType)) + { + contractRequiresOneWay = false; + } + if (!IsRequestReply(channelType)) + { + contractRequiresRequestReply = false; + } + if (!IsDuplex(channelType)) + { + contractRequiresDuplex = false; + } + if (!(IsRequestReply(channelType) || IsDuplex(channelType))) + { + contractRequiresTwoWay = false; + } + if (!IsSessionful(channelType)) + { + contractRequiresSession = false; + } + else + { + contractRequiresDatagram = false; + } + } + + StringBuilder bindingChannelTypes = new StringBuilder(); + bool bindingSupportsOneWay = false; + bool bindingSupportsRequestReply = false; + bool bindingSupportsDuplex = false; + bool bindingSupportsSession = false; + bool bindingSupportsDatagram = false; + bool bindingSupportsAtLeastOneChannelType = false; + foreach (Type channelType in supportedChannels) + { + bindingSupportsAtLeastOneChannelType = true; + if (bindingChannelTypes.Length > 0) + { + bindingChannelTypes.Append(CultureInfo.CurrentCulture.TextInfo.ListSeparator); + bindingChannelTypes.Append(" "); + } + string typeString = channelType.ToString(); + bindingChannelTypes.Append(typeString.Substring(typeString.LastIndexOf('.') + 1)); + + if (IsOneWay(channelType)) + { + bindingSupportsOneWay = true; + } + if (IsRequestReply(channelType)) + { + bindingSupportsRequestReply = true; + } + if (IsDuplex(channelType)) + { + bindingSupportsDuplex = true; + } + if (IsSessionful(channelType)) + { + bindingSupportsSession = true; + } + else + { + bindingSupportsDatagram = true; + } + } + bool bindingSupportsTwoWay = bindingSupportsRequestReply || bindingSupportsDuplex; + + if (!bindingSupportsAtLeastOneChannelType) + { + return new InvalidOperationException(SR.Format(SR.BindingDoesnTSupportAnyChannelTypes1, bindingName)); + } + if (contractRequiresSession && !bindingSupportsSession) + { + return new InvalidOperationException(SR.Format(SR.BindingDoesnTSupportSessionButContractRequires1, bindingName)); + } + if (contractRequiresDatagram && !bindingSupportsDatagram) + { + return new InvalidOperationException(SR.Format(SR.BindingDoesntSupportDatagramButContractRequires, bindingName)); + } + if (contractRequiresDuplex && !bindingSupportsDuplex) + { + return new InvalidOperationException(SR.Format(SR.BindingDoesnTSupportDuplexButContractRequires1, bindingName)); + } + if (contractRequiresRequestReply && !bindingSupportsRequestReply) + { + return new InvalidOperationException(SR.Format(SR.BindingDoesnTSupportRequestReplyButContract1, bindingName)); + } + if (contractRequiresOneWay && !bindingSupportsOneWay) + { + return new InvalidOperationException(SR.Format(SR.BindingDoesnTSupportOneWayButContractRequires1, bindingName)); + } + if (contractRequiresTwoWay && !bindingSupportsTwoWay) + { + return new InvalidOperationException(SR.Format(SR.BindingDoesnTSupportTwoWayButContractRequires1, bindingName)); + } + + contractChannelTypesString = contractChannelTypes.ToString(); + bindingChannelTypesString = bindingChannelTypes.ToString(); + + return null; + } + + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/CommunicationObject.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/CommunicationObject.cs new file mode 100644 index 000000000..6e3bbcecb --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/CommunicationObject.cs @@ -0,0 +1,799 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.Contracts; +using System.Threading; +using CoreWCF.Runtime; +using CoreWCF.Diagnostics; +using System.Threading.Tasks; + +namespace CoreWCF.Channels +{ + // TODO: Go through and verify all the internal methods to see if they are needed/used + public abstract class CommunicationObject : ICommunicationObject + { + bool _aborted; + bool _closeCalled; + ExceptionQueue _exceptionQueue; + object _mutex; + bool _onClosingCalled; + bool _onClosedCalled; + bool _onOpeningCalled; + bool _onOpenedCalled; + bool _raisedClosed; + bool _raisedClosing; + bool _raisedFaulted; + //bool traceOpenAndClose; + object _eventSender; + CommunicationState _state; + + protected CommunicationObject() : this(new object()) { } + + protected CommunicationObject(object mutex) + { + _mutex = mutex; + _eventSender = this; + _state = CommunicationState.Created; + } + + internal bool Aborted + { + get { return _aborted; } + } + + internal object EventSender + { + get { return _eventSender; } + set { _eventSender = value; } + } + + protected bool IsDisposed + { + get { return _state == CommunicationState.Closed; } + } + + public CommunicationState State + { + get { return _state; } + } + + protected object ThisLock + { + get { return _mutex; } + } + + protected abstract TimeSpan DefaultCloseTimeout { get; } + protected abstract TimeSpan DefaultOpenTimeout { get; } + + internal TimeSpan InternalCloseTimeout + { + get { return DefaultCloseTimeout; } + } + + internal TimeSpan InternalOpenTimeout + { + get { return DefaultOpenTimeout; } + } + + public event EventHandler Closed; + public event EventHandler Closing; + public event EventHandler Faulted; + public event EventHandler Opened; + public event EventHandler Opening; + + public void Abort() + { + lock (ThisLock) + { + if (_aborted || _state == CommunicationState.Closed) + return; + _aborted = true; + _state = CommunicationState.Closing; + } + + //if (DiagnosticUtility.ShouldTraceInformation) + //{ + // TraceUtility.TraceEvent(TraceEventType.Information, TraceCode.CommunicationObjectAborted, SR.Format(SR.TraceCodeCommunicationObjectAborted, TraceUtility.CreateSourceString(this)), this); + //} + + //bool throwing = true; + + //try + //{ + OnClosing(); + if (!_onClosingCalled) + throw TraceUtility.ThrowHelperError(CreateBaseClassMethodNotCalledException("OnClosing"), Guid.Empty, this); + + OnAbort(); + + OnClosed(); + if (!_onClosedCalled) + throw TraceUtility.ThrowHelperError(CreateBaseClassMethodNotCalledException("OnClosed"), Guid.Empty, this); + + //throwing = false; + //} + //finally + //{ + // if (throwing) + // { + // if (DiagnosticUtility.ShouldTraceWarning) + // TraceUtility.TraceEvent(TraceEventType.Warning, TraceCode.CommunicationObjectAbortFailed, SR.Format(SR.TraceCodeCommunicationObjectAbortFailed, this.GetCommunicationObjectType().ToString()), this); + // } + //} + } + + public Task CloseAsync() + { + var helper = new TimeoutHelper(DefaultCloseTimeout); + return CloseAsync(helper.GetCancellationToken()); + } + + public async Task CloseAsync(CancellationToken token) + { + token.ThrowIfCancellationRequested(); + + //using (DiagnosticUtility.ShouldUseActivity && this.TraceOpenAndClose ? this.CreateCloseActivity() : null) + //{ + + CommunicationState originalState; + lock (ThisLock) + { + originalState = _state; + if (originalState != CommunicationState.Closed) + _state = CommunicationState.Closing; + + _closeCalled = true; + } + + switch (originalState) + { + case CommunicationState.Created: + case CommunicationState.Opening: + case CommunicationState.Faulted: + Abort(); + if (originalState == CommunicationState.Faulted) + { + throw TraceUtility.ThrowHelperError(CreateFaultedException(), Guid.Empty, this); + } + break; + + case CommunicationState.Opened: + { + bool throwing = true; + try + { + //TimeoutHelper actualTimeout = new TimeoutHelper(timeout); + + OnClosing(); + if (!_onClosingCalled) + throw TraceUtility.ThrowHelperError(CreateBaseClassMethodNotCalledException("OnClosing"), Guid.Empty, this); + + await OnCloseAsync(token); + + OnClosed(); + if (!_onClosedCalled) + throw TraceUtility.ThrowHelperError(CreateBaseClassMethodNotCalledException("OnClosed"), Guid.Empty, this); + + throwing = false; + } + finally + { + if (throwing) + { + //if (DiagnosticUtility.ShouldTraceWarning) + //{ + // TraceUtility.TraceEvent(TraceEventType.Warning, TraceCode.CommunicationObjectCloseFailed, SR.Format(SR.TraceCodeCommunicationObjectCloseFailed, this.GetCommunicationObjectType().ToString()), this); + //} + + Abort(); + } + } + break; + } + + case CommunicationState.Closing: + case CommunicationState.Closed: + break; + + default: + throw Fx.AssertAndThrow("CommunicationObject.BeginClose: Unknown CommunicationState"); + } + //} + + } + + public System.Threading.Tasks.Task OpenAsync() + { + // TODO: Switch to TimeoutHelper from client + var cts = new CancellationTokenSource(); + cts.CancelAfter(DefaultCloseTimeout); + return OpenAsync(cts.Token); + } + + public async Task OpenAsync(CancellationToken token) + { + token.ThrowIfCancellationRequested(); + + //using (ServiceModelActivity activity = DiagnosticUtility.ShouldUseActivity && this.TraceOpenAndClose ? ServiceModelActivity.CreateBoundedActivity() : null) + //{ + //if (DiagnosticUtility.ShouldUseActivity) + //{ + // ServiceModelActivity.Start(activity, this.OpenActivityName, this.OpenActivityType); + //} + lock (ThisLock) + { + ThrowIfDisposedOrImmutable(); + _state = CommunicationState.Opening; + } + + bool throwing = true; + try + { + OnOpening(); + if (!_onOpeningCalled) + throw TraceUtility.ThrowHelperError(CreateBaseClassMethodNotCalledException("OnOpening"), Guid.Empty, this); + + await OnOpenAsync(token); + + OnOpened(); + if (!_onOpenedCalled) + throw TraceUtility.ThrowHelperError(CreateBaseClassMethodNotCalledException("OnOpened"), Guid.Empty, this); + + throwing = false; + } + finally + { + if (throwing) + { + //if (DiagnosticUtility.ShouldTraceWarning) + //{ + // TraceUtility.TraceEvent(TraceEventType.Warning, TraceCode.CommunicationObjectOpenFailed, SR.Format(SR.TraceCodeCommunicationObjectOpenFailed, this.GetCommunicationObjectType().ToString()), this); + //} + + Fault(); + } + } + //} + } + + // TODO: Make internal again + public void Fault(Exception exception) + { + lock (ThisLock) + { + if (_exceptionQueue == null) + _exceptionQueue = new ExceptionQueue(ThisLock); + } + + //if (exception != null && DiagnosticUtility.ShouldTraceInformation) + //{ + // TraceUtility.TraceEvent(TraceEventType.Information, TraceCode.CommunicationObjectFaultReason, + // SR.TraceCodeCommunicationObjectFaultReason, exception, null); + //} + + _exceptionQueue.AddException(exception); + Fault(); + } + + internal void AddPendingException(Exception exception) + { + lock (ThisLock) + { + if (_exceptionQueue == null) + _exceptionQueue = new ExceptionQueue(ThisLock); + } + + _exceptionQueue.AddException(exception); + } + + internal Exception GetPendingException() + { + CommunicationState currentState = _state; + + Fx.Assert(currentState == CommunicationState.Closing || currentState == CommunicationState.Closed || currentState == CommunicationState.Faulted, + "CommunicationObject.GetPendingException(currentState == CommunicationState.Closing || currentState == CommunicationState.Closed || currentState == CommunicationState.Faulted)"); + + ExceptionQueue queue = _exceptionQueue; + if (queue != null) + { + return queue.GetException(); + } + else + { + return null; + } + } + + protected void Fault() + { + lock (ThisLock) + { + if (_state == CommunicationState.Closed || _state == CommunicationState.Closing) + return; + + if (_state == CommunicationState.Faulted) + return; + + _state = CommunicationState.Faulted; + } + + OnFaulted(); + } + + internal void ThrowIfClosed() + { + ThrowPending(); + + switch (_state) + { + case CommunicationState.Created: + break; + + case CommunicationState.Opening: + break; + + case CommunicationState.Opened: + break; + + case CommunicationState.Closing: + break; + + case CommunicationState.Closed: + throw TraceUtility.ThrowHelperError(CreateClosedException(), Guid.Empty, this); + + case CommunicationState.Faulted: + throw TraceUtility.ThrowHelperError(CreateFaultedException(), Guid.Empty, this); + + default: + throw Fx.AssertAndThrow("ThrowIfClosed: Unknown CommunicationObject.state"); + } + } + + protected virtual Type GetCommunicationObjectType() + { + return GetType(); + } + + protected abstract void OnAbort(); + + protected abstract Task OnCloseAsync(CancellationToken token); + + protected abstract Task OnOpenAsync(CancellationToken token); + + protected virtual void OnClosed() + { + _onClosedCalled = true; + + lock (ThisLock) + { + if (_raisedClosed) + return; + _raisedClosed = true; + _state = CommunicationState.Closed; + } + + //if (DiagnosticUtility.ShouldTraceVerbose) + //{ + // TraceUtility.TraceEvent(TraceEventType.Verbose, TraceCode.CommunicationObjectClosed, SR.Format(SR.TraceCodeCommunicationObjectClosed, TraceUtility.CreateSourceString(this)), this); + //} + + EventHandler handler = Closed; + if (handler != null) + { + try + { + handler(_eventSender, EventArgs.Empty); + } + catch (Exception exception) + { + if (Fx.IsFatal(exception)) + throw; + + throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(exception); + } + } + } + + protected virtual void OnClosing() + { + _onClosingCalled = true; + + lock (ThisLock) + { + if (_raisedClosing) + return; + _raisedClosing = true; + } + + //if (DiagnosticUtility.ShouldTraceVerbose) + //{ + // TraceUtility.TraceEvent(TraceEventType.Verbose, TraceCode.CommunicationObjectClosing, SR.Format(SR.TraceCodeCommunicationObjectClosing, TraceUtility.CreateSourceString(this)), this); + //} + EventHandler handler = Closing; + if (handler != null) + { + try + { + handler(_eventSender, EventArgs.Empty); + } + catch (Exception exception) + { + if (Fx.IsFatal(exception)) + throw; + + throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(exception); + } + } + } + + protected virtual void OnFaulted() + { + lock (ThisLock) + { + if (_raisedFaulted) + return; + _raisedFaulted = true; + } + + //if (DiagnosticUtility.ShouldTraceWarning) + //{ + // TraceUtility.TraceEvent(TraceEventType.Warning, TraceCode.CommunicationObjectFaulted, SR.Format(SR.TraceCodeCommunicationObjectFaulted, this.GetCommunicationObjectType().ToString()), this); + //} + + EventHandler handler = Faulted; + if (handler != null) + { + try + { + handler(_eventSender, EventArgs.Empty); + } + catch (Exception exception) + { + if (Fx.IsFatal(exception)) + throw; + + throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(exception); + } + } + } + + protected virtual void OnOpened() + { + _onOpenedCalled = true; + + lock (ThisLock) + { + if (_aborted || _state != CommunicationState.Opening) + return; + _state = CommunicationState.Opened; + } + + //if (DiagnosticUtility.ShouldTraceVerbose) + // TraceUtility.TraceEvent(TraceEventType.Verbose, TraceCode.CommunicationObjectOpened, SR.Format(SR.TraceCodeCommunicationObjectOpened, TraceUtility.CreateSourceString(this)), this); + + EventHandler handler = Opened; + if (handler != null) + { + try + { + handler(_eventSender, EventArgs.Empty); + } + catch (Exception exception) + { + if (Fx.IsFatal(exception)) + throw; + + throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(exception); + } + } + } + + protected virtual void OnOpening() + { + _onOpeningCalled = true; + + //if (DiagnosticUtility.ShouldTraceVerbose) + //{ + // TraceUtility.TraceEvent(TraceEventType.Verbose, TraceCode.CommunicationObjectOpening, SR.Format(SR.TraceCodeCommunicationObjectOpening, TraceUtility.CreateSourceString(this)), this); + //} + + EventHandler handler = Opening; + if (handler != null) + { + try + { + handler(_eventSender, EventArgs.Empty); + } + catch (Exception exception) + { + if (Fx.IsFatal(exception)) + throw; + + throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(exception); + } + } + } + internal void ThrowIfFaulted() + { + ThrowPending(); + + switch (_state) + { + case CommunicationState.Created: + break; + + case CommunicationState.Opening: + break; + + case CommunicationState.Opened: + break; + + case CommunicationState.Closing: + break; + + case CommunicationState.Closed: + break; + + case CommunicationState.Faulted: + throw TraceUtility.ThrowHelperError(CreateFaultedException(), Guid.Empty, this); + + default: + throw Fx.AssertAndThrow("ThrowIfFaulted: Unknown CommunicationObject.state"); + } + } + + internal void ThrowIfAborted() + { + if (_aborted && !_closeCalled) + { + throw TraceUtility.ThrowHelperError(CreateAbortedException(), Guid.Empty, this); + } + } + + Exception CreateNotOpenException() + { + return new InvalidOperationException(SR.Format(SR.CommunicationObjectCannotBeUsed, GetCommunicationObjectType().ToString(), _state.ToString())); + } + + Exception CreateBaseClassMethodNotCalledException(string method) + { + return new InvalidOperationException(SR.Format(SR.CommunicationObjectBaseClassMethodNotCalled, GetCommunicationObjectType().ToString(), method)); + } + + Exception CreateImmutableException() + { + return new InvalidOperationException(SR.Format(SR.CommunicationObjectCannotBeModifiedInState, GetCommunicationObjectType().ToString(), _state.ToString())); + } + + internal Exception CreateClosedException() + { + if (!_closeCalled) + { + return CreateAbortedException(); + } + else + { + return new ObjectDisposedException(GetCommunicationObjectType().ToString()); + } + } + + internal Exception CreateFaultedException() + { + string message = SR.Format(SR.CommunicationObjectFaulted1, GetCommunicationObjectType().ToString()); + return new CommunicationObjectFaultedException(message); + } + + internal Exception CreateAbortedException() + { + return new CommunicationObjectAbortedException(SR.Format(SR.CommunicationObjectAborted1, GetCommunicationObjectType().ToString())); + } + + protected internal void ThrowIfDisposed() + { + ThrowPending(); + + switch (_state) + { + case CommunicationState.Created: + break; + + case CommunicationState.Opening: + break; + + case CommunicationState.Opened: + break; + + case CommunicationState.Closing: + throw TraceUtility.ThrowHelperError(CreateClosedException(), Guid.Empty, this); + + case CommunicationState.Closed: + throw TraceUtility.ThrowHelperError(CreateClosedException(), Guid.Empty, this); + + case CommunicationState.Faulted: + throw TraceUtility.ThrowHelperError(CreateFaultedException(), Guid.Empty, this); + + default: + throw Fx.AssertAndThrow("ThrowIfDisposed: Unknown CommunicationObject.state"); + } + } + + internal void ThrowIfClosedOrOpened() + { + ThrowPending(); + + switch (_state) + { + case CommunicationState.Created: + break; + + case CommunicationState.Opening: + break; + + case CommunicationState.Opened: + throw TraceUtility.ThrowHelperError(CreateImmutableException(), Guid.Empty, this); + + case CommunicationState.Closing: + throw TraceUtility.ThrowHelperError(CreateImmutableException(), Guid.Empty, this); + + case CommunicationState.Closed: + throw TraceUtility.ThrowHelperError(CreateClosedException(), Guid.Empty, this); + + case CommunicationState.Faulted: + throw TraceUtility.ThrowHelperError(CreateFaultedException(), Guid.Empty, this); + + default: + throw Fx.AssertAndThrow("ThrowIfClosedOrOpened: Unknown CommunicationObject.state"); + } + } + + protected internal void ThrowIfDisposedOrImmutable() + { + ThrowPending(); + + switch (_state) + { + case CommunicationState.Created: + break; + + case CommunicationState.Opening: + throw TraceUtility.ThrowHelperError(CreateImmutableException(), Guid.Empty, this); + + case CommunicationState.Opened: + throw TraceUtility.ThrowHelperError(CreateImmutableException(), Guid.Empty, this); + + case CommunicationState.Closing: + throw TraceUtility.ThrowHelperError(CreateClosedException(), Guid.Empty, this); + + case CommunicationState.Closed: + throw TraceUtility.ThrowHelperError(CreateClosedException(), Guid.Empty, this); + + case CommunicationState.Faulted: + throw TraceUtility.ThrowHelperError(CreateFaultedException(), Guid.Empty, this); + + default: + throw Fx.AssertAndThrow("ThrowIfDisposedOrImmutable: Unknown CommunicationObject.state"); + } + } + + protected internal void ThrowIfDisposedOrNotOpen() + { + ThrowPending(); + + switch (_state) + { + case CommunicationState.Created: + throw TraceUtility.ThrowHelperError(CreateNotOpenException(), Guid.Empty, this); + + case CommunicationState.Opening: + throw TraceUtility.ThrowHelperError(CreateNotOpenException(), Guid.Empty, this); + + case CommunicationState.Opened: + break; + + case CommunicationState.Closing: + throw TraceUtility.ThrowHelperError(CreateClosedException(), Guid.Empty, this); + + case CommunicationState.Closed: + throw TraceUtility.ThrowHelperError(CreateClosedException(), Guid.Empty, this); + + case CommunicationState.Faulted: + throw TraceUtility.ThrowHelperError(CreateFaultedException(), Guid.Empty, this); + + default: + throw Fx.AssertAndThrow("ThrowIfDisposedOrNotOpen: Unknown CommunicationObject.state"); + } + } + + public void ThrowIfNotOpened() + { + if (_state == CommunicationState.Created || _state == CommunicationState.Opening) + throw TraceUtility.ThrowHelperError(CreateNotOpenException(), Guid.Empty, this); + } + + internal void ThrowIfClosedOrNotOpen() + { + ThrowPending(); + + switch (_state) + { + case CommunicationState.Created: + throw TraceUtility.ThrowHelperError(CreateNotOpenException(), Guid.Empty, this); + + case CommunicationState.Opening: + throw TraceUtility.ThrowHelperError(CreateNotOpenException(), Guid.Empty, this); + + case CommunicationState.Opened: + break; + + case CommunicationState.Closing: + break; + + case CommunicationState.Closed: + throw TraceUtility.ThrowHelperError(CreateClosedException(), Guid.Empty, this); + + case CommunicationState.Faulted: + throw TraceUtility.ThrowHelperError(CreateFaultedException(), Guid.Empty, this); + + default: + throw Fx.AssertAndThrow("ThrowIfClosedOrNotOpen: Unknown CommunicationObject.state"); + } + } + + //TODO: Make internal again + public void ThrowPending() + { + ExceptionQueue queue = _exceptionQueue; + + if (queue != null) + { + Exception exception = queue.GetException(); + + if (exception != null) + { + throw TraceUtility.ThrowHelperError(exception, Guid.Empty, this); + } + } + } + + class ExceptionQueue + { + Queue _exceptions = new Queue(); + object _thisLock; + + internal ExceptionQueue(object thisLock) + { + _thisLock = thisLock; + } + + object ThisLock + { + get { return _thisLock; } + } + + public void AddException(Exception exception) + { + if (exception == null) + { + return; + } + + lock (ThisLock) + { + _exceptions.Enqueue(exception); + } + } + + public Exception GetException() + { + lock (ThisLock) + { + if (_exceptions.Count > 0) + { + return _exceptions.Dequeue(); + } + } + + return null; + } + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/CommunicationObjectManager.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/CommunicationObjectManager.cs new file mode 100644 index 000000000..92ab3d689 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/CommunicationObjectManager.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections.Generic; + +namespace CoreWCF.Channels +{ + internal class CommunicationObjectManager : LifetimeManager where TItemType : class, ICommunicationObject + { + bool _inputClosed; + readonly ISet _itemsSet; + + public CommunicationObjectManager(object mutex) + : base(mutex) + { + _itemsSet = new HashSet(); + } + + public void Add(TItemType item) + { + bool added = false; + + lock (ThisLock) + { + if (State == LifetimeState.Opened && !_inputClosed) + { + if (_itemsSet.Contains(item)) + return; + + _itemsSet.Add(item); + IncrementBusyCountWithoutLock(); + item.Closed += OnItemClosed; + added = true; + } + } + + if (!added) + { + item.Abort(); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ObjectDisposedException(GetType().ToString())); + } + } + + public void CloseInput() + { + //Abort can reenter this call as a result of + //close timeout, Closing input twice is not a + //FailFast case. + _inputClosed = true; + } + + public void DecrementActivityCount() + { + DecrementBusyCount(); + } + + public void IncrementActivityCount() + { + IncrementBusyCount(); + } + + void OnItemClosed(object sender, EventArgs args) + { + Remove((TItemType)sender); + } + + public void Remove(TItemType item) + { + lock (ThisLock) + { + if (!_itemsSet.Contains(item)) + return; + _itemsSet.Remove(item); + } + + item.Closed -= OnItemClosed; + DecrementBusyCount(); + } + + public TItemType[] ToArray() + { + lock (ThisLock) + { + int index = 0; + TItemType[] items = new TItemType[_itemsSet.Count]; + foreach (TItemType item in _itemsSet) + items[index++] = item; + + return items; + } + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/CompressionFormat.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/CompressionFormat.cs new file mode 100644 index 000000000..cf6ced521 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/CompressionFormat.cs @@ -0,0 +1,20 @@ +namespace CoreWCF.Channels +{ + public enum CompressionFormat + { + /// + /// Default to compression off + /// + None, + + /// + /// GZip compression + /// + GZip, + + /// + /// Deflate compression + /// + Deflate, + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/ContentOnlyMessage.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/ContentOnlyMessage.cs new file mode 100644 index 000000000..53e824565 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/ContentOnlyMessage.cs @@ -0,0 +1,100 @@ +using System; +using System.Xml; +using CoreWCF.Diagnostics; + +namespace CoreWCF.Channels +{ + /// + /// Base class for non-SOAP messages + /// + abstract class ContentOnlyMessage : Message + { + MessageHeaders headers; + MessageProperties properties; + + protected ContentOnlyMessage() + { + headers = new MessageHeaders(MessageVersion.None); + } + + public override MessageHeaders Headers + { + get + { + if (IsDisposed) + { + throw TraceUtility.ThrowHelperError(CreateMessageDisposedException(), this); + } + + return headers; + } + } + + public override MessageProperties Properties + { + get + { + if (IsDisposed) + { + throw TraceUtility.ThrowHelperError(CreateMessageDisposedException(), this); + } + + if (properties == null) + { + properties = new MessageProperties(); + } + + return properties; + } + } + + public override MessageVersion Version + { + get + { + return headers.MessageVersion; + } + } + + protected override void OnBodyToString(XmlDictionaryWriter writer) + { + OnWriteBodyContents(writer); + } + } + + class StringMessage : ContentOnlyMessage + { + string data; + + public StringMessage(string data) + : base() + { + this.data = data; + } + + public override bool IsEmpty + { + get + { + return string.IsNullOrEmpty(data); + } + } + + protected override void OnWriteBodyContents(XmlDictionaryWriter writer) + { + if (data != null && data.Length > 0) + { + writer.WriteElementString("BODY", data); + } + } + } + + class NullMessage : StringMessage + { + public NullMessage() + : base(string.Empty) + { + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/CorrelationDataMessageProperty.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/CorrelationDataMessageProperty.cs new file mode 100644 index 000000000..9d51b3ca4 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/CorrelationDataMessageProperty.cs @@ -0,0 +1,165 @@ +using System; +using System.Collections.Generic; +using CoreWCF.Runtime; + +namespace CoreWCF.Channels +{ + internal class CorrelationDataMessageProperty : IMessageProperty + { + const string PropertyName = "CorrelationDataMessageProperty"; + Dictionary dataProviders; + + public CorrelationDataMessageProperty() + { + } + + CorrelationDataMessageProperty(IDictionary dataProviders) + { + if (dataProviders != null && dataProviders.Count > 0) + { + this.dataProviders = new Dictionary(dataProviders); + } + } + + public static string Name + { + get { return PropertyName; } + } + + public void Add(string name, Func dataProvider) + { + if (string.IsNullOrEmpty(name)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("name"); + } + + if (dataProvider == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("dataProvider"); + } + + if (dataProviders == null) + { + dataProviders = new Dictionary(); + } + dataProviders.Add(name, new DataProviderEntry(dataProvider)); + } + + public bool Remove(string name) + { + if (dataProviders != null) + { + return dataProviders.Remove(name); + } + else + { + return false; + } + } + + public bool TryGetValue(string name, out string value) + { + DataProviderEntry entry; + if (dataProviders != null && dataProviders.TryGetValue(name, out entry)) + { + value = entry.Data; + return true; + } + else + { + value = null; + return false; + } + } + + public static bool TryGet(Message message, out CorrelationDataMessageProperty property) + { + if (message == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("message"); + } + return TryGet(message.Properties, out property); + } + + public static bool TryGet(MessageProperties properties, out CorrelationDataMessageProperty property) + { + object value = null; + if (properties.TryGetValue(PropertyName, out value)) + { + property = value as CorrelationDataMessageProperty; + } + else + { + property = null; + } + return property != null; + } + + public static void AddData(Message message, string name, Func dataProvider) + { + if (string.IsNullOrEmpty(name)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("name"); + } + + if (dataProvider == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("dataProvider"); + } + + CorrelationDataMessageProperty data = null; + object value = null; + if (message.Properties.TryGetValue(PropertyName, out value)) + { + data = value as CorrelationDataMessageProperty; + } + + bool addNewProperty = false; + if (data == null) + { + data = new CorrelationDataMessageProperty(); + addNewProperty = true; + } + + data.Add(name, dataProvider); + + if (addNewProperty) + { + message.Properties[PropertyName] = data; + } + } + + public IMessageProperty CreateCopy() + { + return new CorrelationDataMessageProperty(dataProviders); + } + + class DataProviderEntry + { + string resolvedData; + Func dataProvider; + + public DataProviderEntry(Func dataProvider) + { + Fx.Assert(dataProvider != null, "dataProvider required"); + this.dataProvider = dataProvider; + resolvedData = null; + } + + public string Data + { + get + { + if (dataProvider != null) + { + resolvedData = dataProvider(); + dataProvider = null; + } + + return resolvedData; + } + } + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/CustomBinding.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/CustomBinding.cs new file mode 100644 index 000000000..53d29a177 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/CustomBinding.cs @@ -0,0 +1,118 @@ +using System.Collections.Generic; + +namespace CoreWCF.Channels +{ + public class CustomBinding : Binding + { + BindingElementCollection _bindingElements = new BindingElementCollection(); + + public CustomBinding() + { + } + + public CustomBinding(params BindingElement[] bindingElementsInTopDownChannelStackOrder) + { + if (bindingElementsInTopDownChannelStackOrder == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(bindingElementsInTopDownChannelStackOrder)); + } + + foreach (BindingElement element in bindingElementsInTopDownChannelStackOrder) + { + _bindingElements.Add(element); + } + } + + public CustomBinding(string name, string ns, params BindingElement[] bindingElementsInTopDownChannelStackOrder) + : base(name, ns) + { + if (bindingElementsInTopDownChannelStackOrder == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(bindingElementsInTopDownChannelStackOrder)); + } + + foreach (BindingElement element in bindingElementsInTopDownChannelStackOrder) + { + _bindingElements.Add(element); + } + } + + public CustomBinding(IEnumerable bindingElementsInTopDownChannelStackOrder) + { + if (bindingElementsInTopDownChannelStackOrder == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(bindingElementsInTopDownChannelStackOrder)); + } + + foreach (BindingElement element in bindingElementsInTopDownChannelStackOrder) + { + _bindingElements.Add(element); + } + } + + public CustomBinding(Binding binding) + : this(binding, SafeCreateBindingElements(binding)) + { + } + + static BindingElementCollection SafeCreateBindingElements(Binding binding) + { + if (binding == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(binding)); + } + return binding.CreateBindingElements(); + } + + internal CustomBinding(Binding binding, BindingElementCollection elements) + { + if (binding == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(binding)); + } + if (elements == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(elements)); + } + + Name = binding.Name; + Namespace = binding.Namespace; + CloseTimeout = binding.CloseTimeout; + OpenTimeout = binding.OpenTimeout; + ReceiveTimeout = binding.ReceiveTimeout; + SendTimeout = binding.SendTimeout; + + for (int i = 0; i < elements.Count; i++) + { + _bindingElements.Add(elements[i]); + } + } + + public BindingElementCollection Elements + { + get + { + return _bindingElements; + } + } + + public override BindingElementCollection CreateBindingElements() + { + return _bindingElements.Clone(); + } + + public override string Scheme + { + get + { + TransportBindingElement transport = _bindingElements.Find(); + if (transport == null) + { + return string.Empty; + } + + return transport.Scheme; + } + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/EncoderHelpers.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/EncoderHelpers.cs new file mode 100644 index 000000000..7db3d7bb2 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/EncoderHelpers.cs @@ -0,0 +1,58 @@ +using System.Xml; +using CoreWCF.Runtime; + +namespace CoreWCF.Channels +{ + internal static class EncoderHelpers + { + internal static XmlDictionaryReaderQuotas GetBufferedReadQuotas(XmlDictionaryReaderQuotas encoderQuotas) + { + XmlDictionaryReaderQuotas bufferedReadQuotas = new XmlDictionaryReaderQuotas(); + encoderQuotas.CopyTo(bufferedReadQuotas); + + // now we have the quotas from the encoder, we need to update the values with the new quotas from the default read quotas. + if (IsDefaultQuota(bufferedReadQuotas, XmlDictionaryReaderQuotaTypes.MaxStringContentLength)) + { + bufferedReadQuotas.MaxStringContentLength = EncoderDefaults.BufferedReadDefaultMaxStringContentLength; + } + + if (IsDefaultQuota(bufferedReadQuotas, XmlDictionaryReaderQuotaTypes.MaxArrayLength)) + { + bufferedReadQuotas.MaxArrayLength = EncoderDefaults.BufferedReadDefaultMaxArrayLength; + } + + if (IsDefaultQuota(bufferedReadQuotas, XmlDictionaryReaderQuotaTypes.MaxBytesPerRead)) + { + bufferedReadQuotas.MaxBytesPerRead = EncoderDefaults.BufferedReadDefaultMaxBytesPerRead; + } + + if (IsDefaultQuota(bufferedReadQuotas, XmlDictionaryReaderQuotaTypes.MaxNameTableCharCount)) + { + bufferedReadQuotas.MaxNameTableCharCount = EncoderDefaults.BufferedReadDefaultMaxNameTableCharCount; + } + + if (IsDefaultQuota(bufferedReadQuotas, XmlDictionaryReaderQuotaTypes.MaxDepth)) + { + bufferedReadQuotas.MaxDepth = EncoderDefaults.BufferedReadDefaultMaxDepth; + } + + return bufferedReadQuotas; + } + + private static bool IsDefaultQuota(XmlDictionaryReaderQuotas quotas, XmlDictionaryReaderQuotaTypes quotaType) + { + switch (quotaType) + { + case XmlDictionaryReaderQuotaTypes.MaxDepth: + case XmlDictionaryReaderQuotaTypes.MaxStringContentLength: + case XmlDictionaryReaderQuotaTypes.MaxArrayLength: + case XmlDictionaryReaderQuotaTypes.MaxBytesPerRead: + case XmlDictionaryReaderQuotaTypes.MaxNameTableCharCount: + return (quotas.ModifiedQuotas & quotaType) == 0x00; + } + + Fx.Assert("invalid quota type."); + return false; + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/FaultConverter.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/FaultConverter.cs new file mode 100644 index 000000000..9a11e17c9 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/FaultConverter.cs @@ -0,0 +1,239 @@ +using System; +using System.Globalization; + +namespace CoreWCF.Channels +{ + public abstract class FaultConverter + { + public static FaultConverter GetDefaultFaultConverter(MessageVersion version) + { + return new DefaultFaultConverter(version); + } + + protected abstract bool OnTryCreateException(Message message, MessageFault fault, out Exception exception); + protected abstract bool OnTryCreateFaultMessage(Exception exception, out Message message); + + public bool TryCreateException(Message message, MessageFault fault, out Exception exception) + { + if (message == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("message"); + } + if (fault == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("fault"); + } + + bool created = OnTryCreateException(message, fault, out exception); + + if (created) + { + if (exception == null) + { + string text = SR.Format(SR.FaultConverterDidNotCreateException, GetType().Name); + Exception error = new InvalidOperationException(text); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(error); + } + } + else + { + if (exception != null) + { + string text = SR.Format(SR.FaultConverterCreatedException, GetType().Name); + Exception error = new InvalidOperationException(text, exception); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(error); + } + } + + return created; + } + + public bool TryCreateFaultMessage(Exception exception, out Message message) + { + bool created = OnTryCreateFaultMessage(exception, out message); + + if (created) + { + if (message == null) + { + string text = SR.Format(SR.FaultConverterDidNotCreateFaultMessage, GetType().Name); + Exception error = new InvalidOperationException(text); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(error); + } + } + else + { + if (message != null) + { + string text = SR.Format(SR.FaultConverterCreatedFaultMessage, GetType().Name); + Exception error = new InvalidOperationException(text); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(error); + } + } + + return created; + } + + class DefaultFaultConverter : FaultConverter + { + MessageVersion version; + + internal DefaultFaultConverter(MessageVersion version) + { + this.version = version; + } + + protected override bool OnTryCreateException(Message message, MessageFault fault, out Exception exception) + { + exception = null; + + // SOAP MustUnderstand + if (string.Compare(fault.Code.Namespace, version.Envelope.Namespace, StringComparison.Ordinal) == 0 + && string.Compare(fault.Code.Name, MessageStrings.MustUnderstandFault, StringComparison.Ordinal) == 0) + { + exception = new ProtocolException(fault.Reason.GetMatchingTranslation(CultureInfo.CurrentCulture).Text); + return true; + } + + bool checkSender; + bool checkReceiver; + FaultCode code; + + if (version.Envelope == EnvelopeVersion.Soap11) + { + checkSender = true; + checkReceiver = true; + code = fault.Code; + } + else + { + checkSender = fault.Code.IsSenderFault; + checkReceiver = fault.Code.IsReceiverFault; + code = fault.Code.SubCode; + } + + if (code == null) + { + return false; + } + + if (code.Namespace == null) + { + return false; + } + + if (checkSender) + { + // WS-Addressing + if (string.Compare(code.Namespace, version.Addressing.Namespace, StringComparison.Ordinal) == 0) + { + if (string.Compare(code.Name, AddressingStrings.ActionNotSupported, StringComparison.Ordinal) == 0) + { + exception = new ActionNotSupportedException(fault.Reason.GetMatchingTranslation(CultureInfo.CurrentCulture).Text); + return true; + } + else if (string.Compare(code.Name, AddressingStrings.DestinationUnreachable, StringComparison.Ordinal) == 0) + { + exception = new EndpointNotFoundException(fault.Reason.GetMatchingTranslation(CultureInfo.CurrentCulture).Text); + return true; + } + else if (string.Compare(code.Name, Addressing10Strings.InvalidAddressingHeader, StringComparison.Ordinal) == 0) + { + if (code.SubCode != null && string.Compare(code.SubCode.Namespace, version.Addressing.Namespace, StringComparison.Ordinal) == 0 && + string.Compare(code.SubCode.Name, Addressing10Strings.InvalidCardinality, StringComparison.Ordinal) == 0) + { + exception = new MessageHeaderException(fault.Reason.GetMatchingTranslation(CultureInfo.CurrentCulture).Text, true); + return true; + } + } + else if (version.Addressing == AddressingVersion.WSAddressing10) + { + if (string.Compare(code.Name, Addressing10Strings.MessageAddressingHeaderRequired, StringComparison.Ordinal) == 0) + { + exception = new MessageHeaderException(fault.Reason.GetMatchingTranslation(CultureInfo.CurrentCulture).Text); + return true; + } + else if (string.Compare(code.Name, Addressing10Strings.InvalidAddressingHeader, StringComparison.Ordinal) == 0) + { + exception = new ProtocolException(fault.Reason.GetMatchingTranslation(CultureInfo.CurrentCulture).Text); + return true; + } + } + else + { + if (string.Compare(code.Name, Addressing200408Strings.MessageInformationHeaderRequired, StringComparison.Ordinal) == 0) + { + exception = new ProtocolException(fault.Reason.GetMatchingTranslation(CultureInfo.CurrentCulture).Text); + return true; + } + else if (string.Compare(code.Name, Addressing200408Strings.InvalidMessageInformationHeader, StringComparison.Ordinal) == 0) + { + exception = new ProtocolException(fault.Reason.GetMatchingTranslation(CultureInfo.CurrentCulture).Text); + return true; + } + } + } + } + + if (checkReceiver) + { + // WS-Addressing + if (string.Compare(code.Namespace, version.Addressing.Namespace, StringComparison.Ordinal) == 0) + { + if (string.Compare(code.Name, AddressingStrings.EndpointUnavailable, StringComparison.Ordinal) == 0) + { + exception = new ServerTooBusyException(fault.Reason.GetMatchingTranslation(CultureInfo.CurrentCulture).Text); + return true; + } + } + } + + return false; + } + + protected override bool OnTryCreateFaultMessage(Exception exception, out Message message) + { + // WSA + if (version.Addressing == AddressingVersion.WSAddressing10) + { + if (exception is MessageHeaderException) + { + MessageHeaderException mhe = exception as MessageHeaderException; + if (mhe.HeaderNamespace == AddressingVersion.WSAddressing10.Namespace) + { + message = mhe.ProvideFault(version); + return true; + } + } + else if (exception is ActionMismatchAddressingException) + { + ActionMismatchAddressingException amae = exception as ActionMismatchAddressingException; + message = amae.ProvideFault(version); + return true; + } + } + if (version.Addressing != AddressingVersion.None) + { + if (exception is ActionNotSupportedException) + { + ActionNotSupportedException anse = exception as ActionNotSupportedException; + message = anse.ProvideFault(version); + return true; + } + } + + // SOAP + if (exception is MustUnderstandSoapException) + { + MustUnderstandSoapException muse = exception as MustUnderstandSoapException; + message = muse.ProvideFault(version); + return true; + } + + message = null; + return false; + } + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/FramingDecoder.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/FramingDecoder.cs new file mode 100644 index 000000000..6b819eecc --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/FramingDecoder.cs @@ -0,0 +1,921 @@ +using System; +using System.Globalization; +using System.IO; +using System.Text; +using CoreWCF.Runtime; + +namespace CoreWCF.Channels +{ + internal static class DecoderHelper + { + public static void ValidateSize(int size) + { + if (size <= 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("size", size, SR.ValueMustBePositive)); + } + } + } + + internal struct IntDecoder + { + private int _value; + private short _index; + private bool _isValueDecoded; + private const int LastIndex = 4; + + public int Value + { + get + { + if (!_isValueDecoded) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.FramingValueNotAvailable)); + return _value; + } + } + + public bool IsValueDecoded + { + get { return _isValueDecoded; } + } + + public void Reset() + { + _index = 0; + _value = 0; + _isValueDecoded = false; + } + + public int Decode(byte[] buffer, int offset, int size) + { + DecoderHelper.ValidateSize(size); + if (_isValueDecoded) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.FramingValueNotAvailable)); + } + int bytesConsumed = 0; + while (bytesConsumed < size) + { + int next = buffer[offset]; + _value |= (next & 0x7F) << (_index * 7); + bytesConsumed++; + if (_index == LastIndex && (next & 0xF8) != 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataException(SR.FramingSizeTooLarge)); + } + _index++; + if ((next & 0x80) == 0) + { + _isValueDecoded = true; + break; + } + offset++; + } + return bytesConsumed; + } + } + + + internal abstract class StringDecoder + { + private int _encodedSize; + private byte[] _encodedBytes; + private int _bytesNeeded; + private string _value; + private State _currentState; + private IntDecoder _sizeDecoder; + private int _sizeQuota; + private int _valueLengthInBytes; + + public StringDecoder(int sizeQuota) + { + _sizeQuota = sizeQuota; + _sizeDecoder = new IntDecoder(); + _currentState = State.ReadingSize; + Reset(); + } + + public bool IsValueDecoded + { + get { return _currentState == State.Done; } + } + + public string Value + { + get + { + if (_currentState != State.Done) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.FramingValueNotAvailable)); + return _value; + } + } + + public int Decode(byte[] buffer, int offset, int size) + { + DecoderHelper.ValidateSize(size); + + int bytesConsumed; + switch (_currentState) + { + case State.ReadingSize: + bytesConsumed = _sizeDecoder.Decode(buffer, offset, size); + if (_sizeDecoder.IsValueDecoded) + { + _encodedSize = _sizeDecoder.Value; + if (_encodedSize > _sizeQuota) + { + Exception quotaExceeded = OnSizeQuotaExceeded(_encodedSize); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(quotaExceeded); + } + if (_encodedBytes == null || _encodedBytes.Length < _encodedSize) + { + _encodedBytes = Fx.AllocateByteArray(_encodedSize); + _value = null; + } + _currentState = State.ReadingBytes; + _bytesNeeded = _encodedSize; + } + break; + case State.ReadingBytes: + if (_value != null && _valueLengthInBytes == _encodedSize && _bytesNeeded == _encodedSize && + size >= _encodedSize && CompareBuffers(_encodedBytes, buffer, offset)) + { + bytesConsumed = _bytesNeeded; + OnComplete(_value); + } + else + { + bytesConsumed = _bytesNeeded; + if (size < _bytesNeeded) + bytesConsumed = size; + Buffer.BlockCopy(buffer, offset, _encodedBytes, _encodedSize - _bytesNeeded, bytesConsumed); + _bytesNeeded -= bytesConsumed; + if (_bytesNeeded == 0) + { + _value = Encoding.UTF8.GetString(_encodedBytes, 0, _encodedSize); + _valueLengthInBytes = _encodedSize; + OnComplete(_value); + } + } + break; + default: + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataException(SR.InvalidDecoderStateMachine)); + } + + return bytesConsumed; + } + + protected virtual void OnComplete(string value) + { + _currentState = State.Done; + } + + private static bool CompareBuffers(byte[] buffer1, byte[] buffer2, int offset) + { + for (int i = 0; i < buffer1.Length; i++) + { + if (buffer1[i] != buffer2[i + offset]) + { + return false; + } + } + return true; + } + + protected abstract Exception OnSizeQuotaExceeded(int size); + + public void Reset() + { + _currentState = State.ReadingSize; + _sizeDecoder.Reset(); + } + + private enum State + { + ReadingSize, + ReadingBytes, + Done, + } + } + + internal class ViaStringDecoder : StringDecoder + { + private Uri _via; + + public ViaStringDecoder(int sizeQuota) + : base(sizeQuota) + { + } + + protected override Exception OnSizeQuotaExceeded(int size) + { + Exception result = new InvalidDataException(SR.Format(SR.FramingViaTooLong, size)); + FramingEncodingString.AddFaultString(result, FramingEncodingString.ViaTooLongFault); + return result; + } + + protected override void OnComplete(string value) + { + try + { + _via = new Uri(value); + base.OnComplete(value); + } + catch (UriFormatException exception) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataException(SR.Format(SR.FramingViaNotUri, value), exception)); + } + } + + public Uri ValueAsUri + { + get + { + if (!IsValueDecoded) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.FramingValueNotAvailable)); + return _via; + } + } + } + + internal class FaultStringDecoder : StringDecoder + { + internal const int FaultSizeQuota = 256; + + public FaultStringDecoder() + : base(FaultSizeQuota) + { + } + + protected override Exception OnSizeQuotaExceeded(int size) + { + return new InvalidDataException(SR.Format(SR.FramingFaultTooLong, size)); + } + + public static Exception GetFaultException(string faultString, string via, string contentType) + { + if (faultString == FramingEncodingString.EndpointNotFoundFault) + { + return new EndpointNotFoundException(SR.Format(SR.EndpointNotFound, via)); + } + else if (faultString == FramingEncodingString.ContentTypeInvalidFault) + { + return new ProtocolException(SR.Format(SR.FramingContentTypeMismatch, contentType, via)); + } + else if (faultString == FramingEncodingString.ServiceActivationFailedFault) + { + // TODO: Bring in exception or leave as is + //return new ServiceActivationException(SR.Format(SR.Hosting_ServiceActivationFailed, via)); + return new CommunicationException(SR.Format(SR.Hosting_ServiceActivationFailed, via)); + } + else if (faultString == FramingEncodingString.ConnectionDispatchFailedFault) + { + return new CommunicationException(SR.Format(SR.Sharing_ConnectionDispatchFailed, via)); + } + else if (faultString == FramingEncodingString.EndpointUnavailableFault) + { + return new EndpointNotFoundException(SR.Format(SR.Sharing_EndpointUnavailable, via)); + } + else if (faultString == FramingEncodingString.MaxMessageSizeExceededFault) + { + Exception inner = new QuotaExceededException(SR.FramingMaxMessageSizeExceeded); + return new CommunicationException(inner.Message, inner); + } + else if (faultString == FramingEncodingString.UnsupportedModeFault) + { + return new ProtocolException(SR.Format(SR.FramingModeNotSupportedFault, via)); + } + else if (faultString == FramingEncodingString.UnsupportedVersionFault) + { + return new ProtocolException(SR.Format(SR.FramingVersionNotSupportedFault, via)); + } + else if (faultString == FramingEncodingString.ContentTypeTooLongFault) + { + Exception inner = new QuotaExceededException(SR.Format(SR.FramingContentTypeTooLongFault, contentType)); + return new CommunicationException(inner.Message, inner); + } + else if (faultString == FramingEncodingString.ViaTooLongFault) + { + Exception inner = new QuotaExceededException(SR.Format(SR.FramingViaTooLongFault, via)); + return new CommunicationException(inner.Message, inner); + } + else if (faultString == FramingEncodingString.ServerTooBusyFault) + { + return new ServerTooBusyException(SR.Format(SR.ServerTooBusy, via)); + } + else if (faultString == FramingEncodingString.UpgradeInvalidFault) + { + return new ProtocolException(SR.Format(SR.FramingUpgradeInvalid, via)); + } + else + { + return new ProtocolException(SR.Format(SR.FramingFaultUnrecognized, faultString)); + } + } + } + + internal class ContentTypeStringDecoder : StringDecoder + { + public ContentTypeStringDecoder(int sizeQuota) + : base(sizeQuota) + { + } + + protected override Exception OnSizeQuotaExceeded(int size) + { + Exception result = new InvalidDataException(SR.Format(SR.FramingContentTypeTooLong, size)); + FramingEncodingString.AddFaultString(result, FramingEncodingString.ContentTypeTooLongFault); + return result; + } + + public static string GetString(FramingEncodingType type) + { + switch (type) + { + case FramingEncodingType.Soap11Utf8: + return FramingEncodingString.Soap11Utf8; + case FramingEncodingType.Soap11Utf16: + return FramingEncodingString.Soap11Utf16; + case FramingEncodingType.Soap11Utf16FFFE: + return FramingEncodingString.Soap11Utf16FFFE; + case FramingEncodingType.Soap12Utf8: + return FramingEncodingString.Soap12Utf8; + case FramingEncodingType.Soap12Utf16: + return FramingEncodingString.Soap12Utf16; + case FramingEncodingType.Soap12Utf16FFFE: + return FramingEncodingString.Soap12Utf16FFFE; + case FramingEncodingType.MTOM: + return FramingEncodingString.MTOM; + case FramingEncodingType.Binary: + return FramingEncodingString.Binary; + case FramingEncodingType.BinarySession: + return FramingEncodingString.BinarySession; + default: + return "unknown" + ((int)type).ToString(CultureInfo.InvariantCulture); + } + } + } + + internal abstract class FramingDecoder + { + private long _streamPosition; + + protected FramingDecoder() + { + } + + protected FramingDecoder(long streamPosition) + { + _streamPosition = streamPosition; + } + + protected abstract string CurrentStateAsString { get; } + + public long StreamPosition + { + get { return _streamPosition; } + set { _streamPosition = value; } + } + + protected void ValidateFramingMode(FramingMode mode) + { + switch (mode) + { + case FramingMode.Singleton: + case FramingMode.Duplex: + case FramingMode.Simplex: + case FramingMode.SingletonSized: + break; + default: + { + Exception exception = CreateException(new InvalidDataException(SR.Format( + SR.FramingModeNotSupported, mode.ToString())), FramingEncodingString.UnsupportedModeFault); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(exception); + } + } + } + + protected void ValidateRecordType(FramingRecordType expectedType, FramingRecordType foundType) + { + if (foundType != expectedType) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateInvalidRecordTypeException(expectedType, foundType)); + } + } + + // special validation for Preamble Ack for usability purposes (MB#39593) + protected void ValidatePreambleAck(FramingRecordType foundType) + { + if (foundType != FramingRecordType.PreambleAck) + { + Exception inner = CreateInvalidRecordTypeException(FramingRecordType.PreambleAck, foundType); + string exceptionString; + if (((byte)foundType == 'h') || ((byte)foundType == 'H')) + { + exceptionString = SR.PreambleAckIncorrectMaybeHttp; + } + else + { + exceptionString = SR.PreambleAckIncorrect; + } + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ProtocolException(exceptionString, inner)); + } + } + + private Exception CreateInvalidRecordTypeException(FramingRecordType expectedType, FramingRecordType foundType) + { + return new InvalidDataException(SR.Format(SR.FramingRecordTypeMismatch, expectedType.ToString(), foundType.ToString())); + } + + protected void ValidateMajorVersion(int majorVersion) + { + if (majorVersion != FramingVersion.Major) + { + Exception exception = CreateException(new InvalidDataException(SR.Format( + SR.FramingVersionNotSupported, majorVersion)), FramingEncodingString.UnsupportedVersionFault); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(exception); + } + } + + public Exception CreatePrematureEOFException() + { + return CreateException(new InvalidDataException(SR.FramingPrematureEOF)); + } + + protected Exception CreateException(InvalidDataException innerException, string framingFault) + { + Exception result = CreateException(innerException); + FramingEncodingString.AddFaultString(result, framingFault); + return result; + } + + protected Exception CreateException(InvalidDataException innerException) + { + return new ProtocolException(SR.Format(SR.FramingError, StreamPosition, CurrentStateAsString), + innerException); + } + } + + internal class SingletonMessageDecoder : FramingDecoder + { + private IntDecoder _sizeDecoder; + private int _chunkBytesNeeded; + private int _chunkSize; + private State _currentState; + + public SingletonMessageDecoder(long streamPosition) + : base(streamPosition) + { + _sizeDecoder = new IntDecoder(); + _currentState = State.ChunkStart; + } + + public void Reset() + { + _currentState = State.ChunkStart; + } + + public State CurrentState + { + get { return _currentState; } + } + + protected override string CurrentStateAsString + { + get { return _currentState.ToString(); } + } + + public int ChunkSize + { + get + { + if (_currentState < State.ChunkStart) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.FramingValueNotAvailable)); + } + + return _chunkSize; + } + } + + public int Decode(byte[] bytes, int offset, int size) + { + DecoderHelper.ValidateSize(size); + + try + { + int bytesConsumed; + switch (_currentState) + { + case State.ReadingEnvelopeChunkSize: + bytesConsumed = _sizeDecoder.Decode(bytes, offset, size); + if (_sizeDecoder.IsValueDecoded) + { + _chunkSize = _sizeDecoder.Value; + _sizeDecoder.Reset(); + + if (_chunkSize == 0) + { + _currentState = State.EnvelopeEnd; + } + else + { + _currentState = State.ChunkStart; + _chunkBytesNeeded = _chunkSize; + } + } + break; + case State.ChunkStart: + bytesConsumed = 0; + _currentState = State.ReadingEnvelopeBytes; + break; + case State.ReadingEnvelopeBytes: + bytesConsumed = size; + if (bytesConsumed > _chunkBytesNeeded) + { + bytesConsumed = _chunkBytesNeeded; + } + _chunkBytesNeeded -= bytesConsumed; + if (_chunkBytesNeeded == 0) + { + _currentState = State.ChunkEnd; + } + break; + case State.ChunkEnd: + bytesConsumed = 0; + _currentState = State.ReadingEnvelopeChunkSize; + break; + case State.EnvelopeEnd: + ValidateRecordType(FramingRecordType.End, (FramingRecordType)bytes[offset]); + bytesConsumed = 1; + _currentState = State.End; + break; + case State.End: + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + CreateException(new InvalidDataException(SR.FramingAtEnd))); + + default: + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + CreateException(new InvalidDataException(SR.InvalidDecoderStateMachine))); + } + + StreamPosition += bytesConsumed; + return bytesConsumed; + } + catch (InvalidDataException e) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateException(e)); + } + } + + public enum State + { + ReadingEnvelopeChunkSize, + ChunkStart, + ReadingEnvelopeBytes, + ChunkEnd, + EnvelopeEnd, + End, + } + } + + // common set of states used on the client-side. + internal enum ClientFramingDecoderState + { + ReadingUpgradeRecord, + ReadingUpgradeMode, + UpgradeResponse, + ReadingAckRecord, + Start, + ReadingFault, + ReadingFaultString, + Fault, + ReadingEnvelopeRecord, + ReadingEnvelopeSize, + EnvelopeStart, + ReadingEnvelopeBytes, + EnvelopeEnd, + ReadingEndRecord, + End, + } + + internal abstract class ClientFramingDecoder : FramingDecoder + { + private ClientFramingDecoderState _currentState; + + protected ClientFramingDecoder(long streamPosition) + : base(streamPosition) + { + _currentState = ClientFramingDecoderState.ReadingUpgradeRecord; + } + + public ClientFramingDecoderState CurrentState + { + get + { + return _currentState; + } + + protected set + { + _currentState = value; + } + } + + protected override string CurrentStateAsString + { + get { return _currentState.ToString(); } + } + + public abstract string Fault + { + get; + } + + public abstract int Decode(byte[] bytes, int offset, int size); + } + + // Pattern: + // (UpgradeResponse, upgrade-bytes)*, (Ack | Fault), + // ((EnvelopeStart, ReadingEnvelopeBytes*, EnvelopeEnd) | Fault)*, + // End + internal class ClientDuplexDecoder : ClientFramingDecoder + { + private IntDecoder _sizeDecoder; + private FaultStringDecoder _faultDecoder; + private int _envelopeBytesNeeded; + private int _envelopeSize; + + public ClientDuplexDecoder(long streamPosition) + : base(streamPosition) + { + _sizeDecoder = new IntDecoder(); + } + + public int EnvelopeSize + { + get + { + if (CurrentState < ClientFramingDecoderState.EnvelopeStart) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.FramingValueNotAvailable)); + return _envelopeSize; + } + } + + public override string Fault + { + get + { + if (CurrentState < ClientFramingDecoderState.Fault) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.FramingValueNotAvailable)); + return _faultDecoder.Value; + } + } + + public override int Decode(byte[] bytes, int offset, int size) + { + DecoderHelper.ValidateSize(size); + + try + { + int bytesConsumed; + FramingRecordType recordType; + switch (CurrentState) + { + case ClientFramingDecoderState.ReadingUpgradeRecord: + recordType = (FramingRecordType)bytes[offset]; + if (recordType == FramingRecordType.UpgradeResponse) + { + bytesConsumed = 1; + base.CurrentState = ClientFramingDecoderState.UpgradeResponse; + } + else + { + bytesConsumed = 0; + base.CurrentState = ClientFramingDecoderState.ReadingAckRecord; + } + break; + case ClientFramingDecoderState.UpgradeResponse: + bytesConsumed = 0; + base.CurrentState = ClientFramingDecoderState.ReadingUpgradeRecord; + break; + case ClientFramingDecoderState.ReadingAckRecord: + recordType = (FramingRecordType)bytes[offset]; + if (recordType == FramingRecordType.Fault) + { + bytesConsumed = 1; + _faultDecoder = new FaultStringDecoder(); + base.CurrentState = ClientFramingDecoderState.ReadingFaultString; + break; + } + ValidatePreambleAck(recordType); + bytesConsumed = 1; + base.CurrentState = ClientFramingDecoderState.Start; + break; + case ClientFramingDecoderState.Start: + bytesConsumed = 0; + base.CurrentState = ClientFramingDecoderState.ReadingEnvelopeRecord; + break; + case ClientFramingDecoderState.ReadingEnvelopeRecord: + recordType = (FramingRecordType)bytes[offset]; + if (recordType == FramingRecordType.End) + { + bytesConsumed = 1; + base.CurrentState = ClientFramingDecoderState.End; + break; + } + else if (recordType == FramingRecordType.Fault) + { + bytesConsumed = 1; + _faultDecoder = new FaultStringDecoder(); + base.CurrentState = ClientFramingDecoderState.ReadingFaultString; + break; + } + ValidateRecordType(FramingRecordType.SizedEnvelope, recordType); + bytesConsumed = 1; + base.CurrentState = ClientFramingDecoderState.ReadingEnvelopeSize; + _sizeDecoder.Reset(); + break; + case ClientFramingDecoderState.ReadingEnvelopeSize: + bytesConsumed = _sizeDecoder.Decode(bytes, offset, size); + if (_sizeDecoder.IsValueDecoded) + { + base.CurrentState = ClientFramingDecoderState.EnvelopeStart; + _envelopeSize = _sizeDecoder.Value; + _envelopeBytesNeeded = _envelopeSize; + } + break; + case ClientFramingDecoderState.EnvelopeStart: + bytesConsumed = 0; + base.CurrentState = ClientFramingDecoderState.ReadingEnvelopeBytes; + break; + case ClientFramingDecoderState.ReadingEnvelopeBytes: + bytesConsumed = size; + if (bytesConsumed > _envelopeBytesNeeded) + bytesConsumed = _envelopeBytesNeeded; + _envelopeBytesNeeded -= bytesConsumed; + if (_envelopeBytesNeeded == 0) + base.CurrentState = ClientFramingDecoderState.EnvelopeEnd; + break; + case ClientFramingDecoderState.EnvelopeEnd: + bytesConsumed = 0; + base.CurrentState = ClientFramingDecoderState.ReadingEnvelopeRecord; + break; + case ClientFramingDecoderState.ReadingFaultString: + bytesConsumed = _faultDecoder.Decode(bytes, offset, size); + if (_faultDecoder.IsValueDecoded) + { + base.CurrentState = ClientFramingDecoderState.Fault; + } + break; + case ClientFramingDecoderState.Fault: + bytesConsumed = 0; + base.CurrentState = ClientFramingDecoderState.ReadingEndRecord; + break; + case ClientFramingDecoderState.ReadingEndRecord: + ValidateRecordType(FramingRecordType.End, (FramingRecordType)bytes[offset]); + bytesConsumed = 1; + base.CurrentState = ClientFramingDecoderState.End; + break; + case ClientFramingDecoderState.End: + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + CreateException(new InvalidDataException(SR.FramingAtEnd))); + default: + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + CreateException(new InvalidDataException(SR.InvalidDecoderStateMachine))); + } + + StreamPosition += bytesConsumed; + return bytesConsumed; + } + catch (InvalidDataException e) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateException(e)); + } + } + } + + // Pattern: + // (UpgradeResponse, upgrade-bytes)*, (Ack | Fault), + // End + internal class ClientSingletonDecoder : ClientFramingDecoder + { + private FaultStringDecoder _faultDecoder; + + public ClientSingletonDecoder(long streamPosition) + : base(streamPosition) + { + } + + public override string Fault + { + get + { + if (CurrentState < ClientFramingDecoderState.Fault) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.FramingValueNotAvailable)); + return _faultDecoder.Value; + } + } + + public override int Decode(byte[] bytes, int offset, int size) + { + DecoderHelper.ValidateSize(size); + + try + { + int bytesConsumed; + FramingRecordType recordType; + switch (CurrentState) + { + case ClientFramingDecoderState.ReadingUpgradeRecord: + recordType = (FramingRecordType)bytes[offset]; + if (recordType == FramingRecordType.UpgradeResponse) + { + bytesConsumed = 1; + base.CurrentState = ClientFramingDecoderState.UpgradeResponse; + } + else + { + bytesConsumed = 0; + base.CurrentState = ClientFramingDecoderState.ReadingAckRecord; + } + break; + case ClientFramingDecoderState.UpgradeResponse: + bytesConsumed = 0; + base.CurrentState = ClientFramingDecoderState.ReadingUpgradeRecord; + break; + case ClientFramingDecoderState.ReadingAckRecord: + recordType = (FramingRecordType)bytes[offset]; + if (recordType == FramingRecordType.Fault) + { + bytesConsumed = 1; + _faultDecoder = new FaultStringDecoder(); + base.CurrentState = ClientFramingDecoderState.ReadingFaultString; + break; + } + ValidatePreambleAck(recordType); + bytesConsumed = 1; + base.CurrentState = ClientFramingDecoderState.Start; + break; + + case ClientFramingDecoderState.Start: + bytesConsumed = 0; + base.CurrentState = ClientFramingDecoderState.ReadingEnvelopeRecord; + break; + + case ClientFramingDecoderState.ReadingEnvelopeRecord: + recordType = (FramingRecordType)bytes[offset]; + if (recordType == FramingRecordType.End) + { + bytesConsumed = 1; + base.CurrentState = ClientFramingDecoderState.End; + break; + } + else if (recordType == FramingRecordType.Fault) + { + bytesConsumed = 0; + base.CurrentState = ClientFramingDecoderState.ReadingFault; + break; + } + ValidateRecordType(FramingRecordType.UnsizedEnvelope, recordType); + bytesConsumed = 1; + base.CurrentState = ClientFramingDecoderState.EnvelopeStart; + break; + + case ClientFramingDecoderState.EnvelopeStart: + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + CreateException(new InvalidDataException(SR.FramingAtEnd))); + + case ClientFramingDecoderState.ReadingFault: + recordType = (FramingRecordType)bytes[offset]; + ValidateRecordType(FramingRecordType.Fault, recordType); + bytesConsumed = 1; + _faultDecoder = new FaultStringDecoder(); + base.CurrentState = ClientFramingDecoderState.ReadingFaultString; + break; + case ClientFramingDecoderState.ReadingFaultString: + bytesConsumed = _faultDecoder.Decode(bytes, offset, size); + if (_faultDecoder.IsValueDecoded) + { + base.CurrentState = ClientFramingDecoderState.Fault; + } + break; + case ClientFramingDecoderState.Fault: + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + CreateException(new InvalidDataException(SR.FramingAtEnd))); + default: + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + CreateException(new InvalidDataException(SR.InvalidDecoderStateMachine))); + } + + StreamPosition += bytesConsumed; + return bytesConsumed; + } + catch (InvalidDataException e) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateException(e)); + } + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/FramingFormat.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/FramingFormat.cs new file mode 100644 index 000000000..6cb269339 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/FramingFormat.cs @@ -0,0 +1,178 @@ +using System; + +namespace CoreWCF.Channels +{ + /* + Message Framing BNF: + + protocol-stream-a = (singleton-unsized-stream-a | duplex-stream-a | simplex-stream-a | singleton-sized-stream-a)+ + protocol-stream-b = (singleton-unsized-stream-b | duplex-stream-b)+ + + singleton-unsized-stream-a = version-record mode-record-type singleton-unsized-mode via-record encoding-record upgrade-request* preamble-end-record-type singleton-message end-record-type + duplex-stream-a = version-record mode-record-type duplex-mode via-record encoding-record upgrade-request* preamble-end-record-type duplex-message* end-record-type + simplex-stream-a = version-record mode-record-type simplex-mode via-record encoding-record simplex-message* end-record-type + singleton-sized-stream-a = version-record mode-record-type singleton-sized-mode via-record encoding-record octets + + singleton-unsized-stream-b = upgrade-response* preamble-response singleton-message? end-record-type + duplex-stream-b = upgrade-response* preamble-response duplex-message* (fault-message | end-record-type) + + singleton-message = unsized-message + duplex-message = sized-message + simplex-message = sized-message + fault-message = fault-record-type mbint utf8-octets + sized-message = sized-envelope-record-type mbint octets + unsized-message = unsized-envelope-record-type (mbint octets)* octet(0x0) + + preamble-response = preamble-ack-record-type | fault-message + + upgrade-request = upgrade-request-record-type mbint utf8-octets octets + upgrade-response = upgrade-response-record-type octets + + version-record = version-record-type major-version-number minor-version-number + major-version-number = octet(0x1) + minor-version-number = octet(0x0) + + encoding-record = known-encoding-record | extensible-encoding-record + known-encoding-record = known-encoding-record-type known-encoding-type + extensible-encoding-record = extensible-encoding-record-type mbint utf8-octets + + via-record = via-record-type mbint utf8-octets + + singleton-unsized-mode = octet(0x1) + duplex-mode = octet(0x2) + simplex-mode = octet(0x3) + singleton-sized-mode = octet(0x4) + + known-encoding-type = text-encoding | binary-encoding | mtom-encoding + binary-encoding = binary-sessionless-encoding | binary-session-encoding + text-encoding = soap11-text-encoding | soap12-text-encoding + soap11-text-encoding = soap11-utf8-encoding | soap11-utf16-encoding | soap11-unicodeFFFE-encoding + soap12-text-encoding = soap12-utf8-encoding | soap12-utf16-encoding | soap12-unicodeFFFE-encoding + + soap11-utf8-encoding = octet(0x0) + soap11-utf16-encoding = octet(0x1) + soap11-unicodeFFFE-encoding = octet(0x2) + soap12-utf8-encoding = octet(0x3) + soap12-utf16-encoding = octet(0x4) + soap12-unicodeFFFE-encoding = octet(0x5) + mtom-encoding = octet(0x6) + binary-sessionless-encoding = octet(0x7) + binary-session-encoding = octet(0x8) + + version-record-type = octet(0x0) + mode-record-type = octet(0x1) + via-record-type = octet(0x2) + known-encoding-record-type = octet(0x3) + extensible-encoding-record-type = octet(0x4) + unsized-envelope-record-type = octet(0x5) + sized-envelope-record-type = octet(0x6) + end-record-type = octet(0x7) + fault-record-type = octet(0x8) + upgrade-request-record-type = octet(0x9) + upgrade-response-record-type = octet(0xA) + preamble-ack-record-type = octet (0xB) + preamble-end-record-type = octet (0xC) + */ + + enum FramingRecordType + { + Version = 0x0, + Mode = 0x1, + Via = 0x2, + KnownEncoding = 0x3, + ExtensibleEncoding = 0x4, + UnsizedEnvelope = 0x5, + SizedEnvelope = 0x6, + End = 0x7, + Fault = 0x8, + UpgradeRequest = 0x9, + UpgradeResponse = 0xA, + PreambleAck = 0xB, + PreambleEnd = 0xC, + } + + enum FramingMode + { + Singleton = 0x1, + Duplex = 0x2, + Simplex = 0x3, + SingletonSized = 0x4, + } + + static class FramingUpgradeString + { + public const string SslOrTls = "application/ssl-tls"; + public const string Negotiate = "application/negotiate"; + } + + enum FramingEncodingType + { + Soap11Utf8 = 0x0, + Soap11Utf16 = 0x1, + Soap11Utf16FFFE = 0x2, + Soap12Utf8 = 0x3, + Soap12Utf16 = 0x4, + Soap12Utf16FFFE = 0x5, + MTOM = 0x6, + Binary = 0x7, + BinarySession = 0x8, + } + + static class FramingEncodingString + { + public const string Soap11Utf8 = "text/xml; charset=utf-8"; + public const string Soap11Utf16 = "text/xml; charset=utf16"; + public const string Soap11Utf16FFFE = "text/xml; charset=unicodeFFFE"; + public const string Soap12Utf8 = "application/soap+xml; charset=utf-8"; + public const string Soap12Utf16 = "application/soap+xml; charset=utf16"; + public const string Soap12Utf16FFFE = "application/soap+xml; charset=unicodeFFFE"; + public const string MTOM = "multipart/related"; + public const string Binary = "application/soap+msbin1"; + public const string BinarySession = "application/soap+msbinsession1"; + public const string ExtendedBinaryGZip = Binary + "+gzip"; + public const string ExtendedBinarySessionGZip = BinarySession + "+gzip"; + public const string ExtendedBinaryDeflate = Binary + "+deflate"; + public const string ExtendedBinarySessionDeflate = BinarySession + "+deflate"; + public const string NamespaceUri = "http://schemas.microsoft.com/ws/2006/05/framing"; + const string FaultBaseUri = NamespaceUri + "/faults/"; + public const string ContentTypeInvalidFault = FaultBaseUri + "ContentTypeInvalid"; + public const string ContentTypeTooLongFault = FaultBaseUri + "ContentTypeTooLong"; + public const string ConnectionDispatchFailedFault = FaultBaseUri + "ConnectionDispatchFailed"; + public const string EndpointNotFoundFault = FaultBaseUri + "EndpointNotFound"; + public const string EndpointUnavailableFault = FaultBaseUri + "EndpointUnavailable"; + public const string MaxMessageSizeExceededFault = FaultBaseUri + "MaxMessageSizeExceededFault"; + public const string ServerTooBusyFault = FaultBaseUri + "ServerTooBusy"; + public const string ServiceActivationFailedFault = FaultBaseUri + "ServiceActivationFailed"; + public const string UnsupportedModeFault = FaultBaseUri + "UnsupportedMode"; + public const string UnsupportedVersionFault = FaultBaseUri + "UnsupportedVersion"; + public const string UpgradeInvalidFault = FaultBaseUri + "UpgradeInvalid"; + public const string ViaTooLongFault = FaultBaseUri + "ViaTooLong"; + + const string ExceptionKey = "FramingEncodingString"; + public static bool TryGetFaultString(Exception exception, out string framingFault) + { + framingFault = null; + if (exception.Data.Contains(FramingEncodingString.ExceptionKey)) + { + framingFault = exception.Data[FramingEncodingString.ExceptionKey] as string; + if (framingFault != null) + { + return true; + } + } + + return false; + } + + public static void AddFaultString(Exception exception, string framingFault) + { + exception.Data[FramingEncodingString.ExceptionKey] = framingFault; + } + } + + static class FramingVersion + { + public const int Major = 0x1; + public const int Minor = 0x0; + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/IAnonymousUriPrefixMatcher.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/IAnonymousUriPrefixMatcher.cs new file mode 100644 index 000000000..e09a58d8c --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/IAnonymousUriPrefixMatcher.cs @@ -0,0 +1,9 @@ +using System; + +namespace CoreWCF.Channels +{ + public interface IAnonymousUriPrefixMatcher + { + void Register(Uri anonymousUriPrefix); + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/IChannel.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/IChannel.cs new file mode 100644 index 000000000..282222875 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/IChannel.cs @@ -0,0 +1,7 @@ +namespace CoreWCF.Channels +{ + public interface IChannel : ICommunicationObject + { + T GetProperty() where T : class; + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/IChannelAcceptor.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/IChannelAcceptor.cs new file mode 100644 index 000000000..003625f2f --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/IChannelAcceptor.cs @@ -0,0 +1,13 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace CoreWCF.Channels +{ + interface IChannelAcceptor : ICommunicationObject + where TChannel : class, IChannel + { + Task AcceptChannelAsync(CancellationToken token); + Task WaitForChannelAsync(CancellationToken token); + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/IChannelBindingProvider.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/IChannelBindingProvider.cs new file mode 100644 index 000000000..c3c048ad5 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/IChannelBindingProvider.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoreWCF.Channels +{ + // TODO: Make internal again + public interface IChannelBindingProvider + { + void EnableChannelBindingSupport(); + bool IsChannelBindingSupportEnabled { get; } + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/IChannelListener.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/IChannelListener.cs new file mode 100644 index 000000000..4af7f8db3 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/IChannelListener.cs @@ -0,0 +1,21 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace CoreWCF.Channels +{ + public interface IChannelListener : ICommunicationObject + { + Uri Uri { get; } + T GetProperty() where T : class; + // I believe WaitForChannel is only used with the TransactedChannelPump + //Task WaitForChannelAsync(CancellationToken token); + } + + public interface IChannelListener : IChannelListener + where TChannel : class, IChannel + { + Task AcceptChannelAsync(); + Task AcceptChannelAsync(CancellationToken token); + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/ICompressedMessageEncoder.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/ICompressedMessageEncoder.cs new file mode 100644 index 000000000..fdb9b5972 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/ICompressedMessageEncoder.cs @@ -0,0 +1,14 @@ +namespace CoreWCF.Channels +{ + // TODO: Add to contract. This is a feature of an encoder but we don't allow other people writing transports to find out about the + // encoder implementing compression and having it enabled. There's no way to work around this without reflection if not public so it should be public. + // BinaryMessageEncoder is the only in-box encoder which supports this. + public interface ICompressedMessageEncoder + { + bool CompressionEnabled { get; } + + void SetSessionContentType(string contentType); + + void AddCompressedMessageProperties(Message message, string supportedCompressionTypes); + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/ICorrelatorKey.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/ICorrelatorKey.cs new file mode 100644 index 000000000..f81cb4b72 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/ICorrelatorKey.cs @@ -0,0 +1,10 @@ +namespace CoreWCF.Channels +{ + // This interface needs to be implemented by requests that need to save + // the RequestReplyCorrelatorKey into the request during RequestReplyCorrelator.Add + // operation. + internal interface ICorrelatorKey + { + RequestReplyCorrelator.Key RequestCorrelatorKey { get; set; } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/IDuplexChannel.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/IDuplexChannel.cs new file mode 100644 index 000000000..8c77969c8 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/IDuplexChannel.cs @@ -0,0 +1,6 @@ +namespace CoreWCF.Channels +{ + public interface IDuplexChannel : IChannel, IInputChannel, IOutputChannel, ICommunicationObject + { + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/IDuplexSession.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/IDuplexSession.cs new file mode 100644 index 000000000..464a259d8 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/IDuplexSession.cs @@ -0,0 +1,8 @@ +namespace CoreWCF.Channels +{ + public interface IDuplexSession : IInputSession, IOutputSession, ISession + { + System.Threading.Tasks.Task CloseOutputSessionAsync(); + System.Threading.Tasks.Task CloseOutputSessionAsync(System.Threading.CancellationToken token); + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/IDuplexSessionChannel.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/IDuplexSessionChannel.cs new file mode 100644 index 000000000..835b42a31 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/IDuplexSessionChannel.cs @@ -0,0 +1,6 @@ +namespace CoreWCF.Channels +{ + public interface IDuplexSessionChannel : IChannel, IDuplexChannel, IInputChannel, IOutputChannel, ISessionChannel, ICommunicationObject + { + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/IInputChannel.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/IInputChannel.cs new file mode 100644 index 000000000..0fe161fde --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/IInputChannel.cs @@ -0,0 +1,11 @@ +namespace CoreWCF.Channels +{ + public interface IInputChannel : IChannel, ICommunicationObject + { + EndpointAddress LocalAddress { get; } + System.Threading.Tasks.Task ReceiveAsync(); + System.Threading.Tasks.Task ReceiveAsync(System.Threading.CancellationToken token); + System.Threading.Tasks.Task> TryReceiveAsync(System.Threading.CancellationToken token); + System.Threading.Tasks.Task WaitForMessageAsync(System.Threading.CancellationToken token); + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/IInputSession.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/IInputSession.cs new file mode 100644 index 000000000..3b42a08c9 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/IInputSession.cs @@ -0,0 +1,6 @@ +namespace CoreWCF.Channels +{ + public interface IInputSession : ISession + { + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/IInputSessionChannel.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/IInputSessionChannel.cs new file mode 100644 index 000000000..920088d88 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/IInputSessionChannel.cs @@ -0,0 +1,6 @@ +namespace CoreWCF.Channels +{ + public interface IInputSessionChannel : IChannel, IInputChannel, ISessionChannel, ICommunicationObject + { + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/IMessageProperty.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/IMessageProperty.cs new file mode 100644 index 000000000..a0370246a --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/IMessageProperty.cs @@ -0,0 +1,7 @@ +namespace CoreWCF.Channels +{ + public interface IMessageProperty + { + IMessageProperty CreateCopy(); + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/IOutputChannel.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/IOutputChannel.cs new file mode 100644 index 000000000..6f56bd017 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/IOutputChannel.cs @@ -0,0 +1,10 @@ +namespace CoreWCF.Channels +{ + public interface IOutputChannel : IChannel, ICommunicationObject + { + EndpointAddress RemoteAddress { get; } + System.Uri Via { get; } + System.Threading.Tasks.Task SendAsync(Message message); + System.Threading.Tasks.Task SendAsync(Message message, System.Threading.CancellationToken token); + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/IOutputSession.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/IOutputSession.cs new file mode 100644 index 000000000..a14428ee6 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/IOutputSession.cs @@ -0,0 +1,6 @@ +namespace CoreWCF.Channels +{ + public interface IOutputSession : ISession + { + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/IOutputSessionChannel.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/IOutputSessionChannel.cs new file mode 100644 index 000000000..fc7f57e2d --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/IOutputSessionChannel.cs @@ -0,0 +1,6 @@ +namespace CoreWCF.Channels +{ + public interface IOutputSessionChannel : IChannel, IOutputChannel, ISessionChannel, ICommunicationObject + { + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/IReplyChannel.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/IReplyChannel.cs new file mode 100644 index 000000000..eea90d9ff --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/IReplyChannel.cs @@ -0,0 +1,14 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace CoreWCF.Channels +{ + public interface IReplyChannel : IChannel + { + EndpointAddress LocalAddress { get; } + Task ReceiveRequestAsync(); + Task ReceiveRequestAsync(CancellationToken token); + Task> TryReceiveRequestAsync(CancellationToken token); + Task WaitForRequestAsync(CancellationToken token); + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/IReplySessionChannel.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/IReplySessionChannel.cs new file mode 100644 index 000000000..0816da284 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/IReplySessionChannel.cs @@ -0,0 +1,6 @@ +namespace CoreWCF.Channels +{ + public interface IReplySessionChannel : IReplyChannel, ISessionChannel + { + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/IRequestChannel.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/IRequestChannel.cs new file mode 100644 index 000000000..c86c731d0 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/IRequestChannel.cs @@ -0,0 +1,10 @@ +namespace CoreWCF.Channels +{ + public interface IRequestChannel : IChannel, ICommunicationObject + { + EndpointAddress RemoteAddress { get; } + System.Uri Via { get; } + System.Threading.Tasks.Task RequestAsync(Message message); + System.Threading.Tasks.Task RequestAsync(Message message, System.Threading.CancellationToken token); + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/IRequestReplyCorrelator.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/IRequestReplyCorrelator.cs new file mode 100644 index 000000000..e048ad6f6 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/IRequestReplyCorrelator.cs @@ -0,0 +1,15 @@ +namespace CoreWCF.Channels +{ + // All implementations of this interface are required to be thread-safe + + // TODO: Either this interface needs to be public or it needs to be in some internal shared contract + public interface IRequestReplyCorrelator + { + // throws if another object of the same type has been added for the same message + // null is not a valid value for state. + void Add(Message request, T state); + + // returns null if no state is found. + T Find(Message reply, bool remove); + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/IRequestSessionChannel.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/IRequestSessionChannel.cs new file mode 100644 index 000000000..8d6c38e30 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/IRequestSessionChannel.cs @@ -0,0 +1,6 @@ +namespace CoreWCF.Channels +{ + public interface IRequestSessionChannel : IChannel, IRequestChannel, ISessionChannel, ICommunicationObject + { + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/ISecurityCapabilities.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/ISecurityCapabilities.cs new file mode 100644 index 000000000..f7b69151d --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/ISecurityCapabilities.cs @@ -0,0 +1,13 @@ +using System.Net.Security; + +namespace CoreWCF.Channels +{ + public interface ISecurityCapabilities + { + ProtectionLevel SupportedRequestProtectionLevel { get; } + ProtectionLevel SupportedResponseProtectionLevel { get; } + bool SupportsClientAuthentication { get; } + bool SupportsClientWindowsIdentity { get; } + bool SupportsServerAuthentication { get; } + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/ISecuritySession.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/ISecuritySession.cs new file mode 100644 index 000000000..385487f25 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/ISecuritySession.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoreWCF.Channels +{ + public interface ISecuritySession : ISession + { + EndpointIdentity RemoteIdentity { get; } + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/ISession.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/ISession.cs new file mode 100644 index 000000000..d01a2d832 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/ISession.cs @@ -0,0 +1,7 @@ +namespace CoreWCF.Channels +{ + public interface ISession + { + string Id { get; } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/ISessionChannel.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/ISessionChannel.cs new file mode 100644 index 000000000..2ca447369 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/ISessionChannel.cs @@ -0,0 +1,7 @@ +namespace CoreWCF.Channels +{ + public interface ISessionChannel where TSession : ISession + { + TSession Session { get; } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/IStreamUpgradeChannelBindingProvider.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/IStreamUpgradeChannelBindingProvider.cs new file mode 100644 index 000000000..f779c0622 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/IStreamUpgradeChannelBindingProvider.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Security.Authentication.ExtendedProtection; +using System.Text; + +namespace CoreWCF.Channels +{ + interface IStreamUpgradeChannelBindingProvider : IChannelBindingProvider + { + ChannelBinding GetChannelBinding(StreamUpgradeAcceptor upgradeAcceptor, ChannelBindingKind kind); + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/ITransportFactorySettings.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/ITransportFactorySettings.cs new file mode 100644 index 000000000..f543bae64 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/ITransportFactorySettings.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoreWCF.Channels +{ + // TODO: Make this internal again or get rid of it, probably through DI or Features + public interface ITransportFactorySettings : IDefaultCommunicationTimeouts + { + bool ManualAddressing { get; } + BufferManager BufferManager { get; } + long MaxReceivedMessageSize { get; } + MessageEncoderFactory MessageEncoderFactory { get; } + MessageVersion MessageVersion { get; } + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/ITransportTokenAssertionProvider.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/ITransportTokenAssertionProvider.cs new file mode 100644 index 000000000..397771f8a --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/ITransportTokenAssertionProvider.cs @@ -0,0 +1,9 @@ +using System.Xml; + +namespace CoreWCF.Channels +{ + internal interface ITransportTokenAssertionProvider + { + XmlElement GetTransportTokenAssertion(); + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/InputChannel.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/InputChannel.cs new file mode 100644 index 000000000..8e1a84acd --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/InputChannel.cs @@ -0,0 +1,97 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using CoreWCF.Runtime; + +namespace CoreWCF.Channels +{ + class InputChannel : InputQueueChannel, IInputChannel + { + EndpointAddress localAddress; + + public InputChannel(ChannelManagerBase channelManager, EndpointAddress localAddress) + : base(channelManager) + { + this.localAddress = localAddress; + } + + public EndpointAddress LocalAddress + { + get { return localAddress; } + } + + public override T GetProperty() + { + if (typeof(T) == typeof(IInputChannel)) + { + return (T)(object)this; + } + + T baseProperty = base.GetProperty(); + if (baseProperty != null) + { + return baseProperty; + } + + return default(T); + } + + protected override Task OnOpenAsync(CancellationToken token) + { + return Task.CompletedTask; + } + + public virtual Task ReceiveAsync() + { + TimeoutHelper helper = new TimeoutHelper(DefaultReceiveTimeout); + return ReceiveAsync(helper.GetCancellationToken()); + } + + public virtual Task ReceiveAsync(CancellationToken token) + { + ThrowPending(); + return InputChannel.HelpReceiveAsync(this, token); + } + + public virtual Task> TryReceiveAsync(CancellationToken token) + { + ThrowPending(); + return base.DequeueAsync(token); + } + + public Task WaitForMessageAsync(CancellationToken token) + { + ThrowPending(); + return base.WaitForItemAsync(token); + } + + #region static Helpers to convert TryReceive to Receive + internal static async Task HelpReceiveAsync(IInputChannel channel, CancellationToken token) + { + var result = await channel.TryReceiveAsync(token); + if (result.Success) + { + return result.Result; + } + else + { + // TODO: Derive CancellationToken to carry timeout + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateReceiveTimedOutException(channel, TimeSpan.Zero)); + } + } + + static Exception CreateReceiveTimedOutException(IInputChannel channel, TimeSpan timeout) + { + if (channel.LocalAddress != null) + { + return new TimeoutException(SR.Format(SR.ReceiveTimedOut, channel.LocalAddress.Uri.AbsoluteUri, timeout)); + } + else + { + return new TimeoutException(SR.Format(SR.ReceiveTimedOutNoLocalAddress, timeout)); + } + } + #endregion + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/InputQueueChannel.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/InputQueueChannel.cs new file mode 100644 index 000000000..a43f35e9d --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/InputQueueChannel.cs @@ -0,0 +1,142 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using CoreWCF.Runtime; +using CoreWCF.Diagnostics; + +namespace CoreWCF.Channels +{ + public abstract class InputQueueChannel : ChannelBase + where TDisposable : class, IDisposable + { + InputQueue inputQueue; + + protected InputQueueChannel(ChannelManagerBase channelManager) + : base(channelManager) + { + inputQueue = new InputQueue(); + } + + public int InternalPendingItems + { + get + { + return inputQueue.PendingCount; + } + } + + public int PendingItems + { + get + { + ThrowIfDisposedOrNotOpen(); + return InternalPendingItems; + } + } + + public void EnqueueAndDispatch(TDisposable item) + { + EnqueueAndDispatch(item, null); + } + + public void EnqueueAndDispatch(TDisposable item, Action dequeuedCallback, bool canDispatchOnThisThread) + { + OnEnqueueItem(item); + + // NOTE: don't need to check IsDisposed here: InputQueue will handle dispose + inputQueue.EnqueueAndDispatch(item, dequeuedCallback, canDispatchOnThisThread); + } + + public void EnqueueAndDispatch(Exception exception, Action dequeuedCallback, bool canDispatchOnThisThread) + { + // NOTE: don't need to check IsDisposed here: InputQueue will handle dispose + inputQueue.EnqueueAndDispatch(exception, dequeuedCallback, canDispatchOnThisThread); + } + + public void EnqueueAndDispatch(TDisposable item, Action dequeuedCallback) + { + OnEnqueueItem(item); + + // NOTE: don't need to check IsDisposed here: InputQueue will handle dispose + inputQueue.EnqueueAndDispatch(item, dequeuedCallback); + } + + public bool EnqueueWithoutDispatch(Exception exception, Action dequeuedCallback) + { + // NOTE: don't need to check IsDisposed here: InputQueue will handle dispose + return inputQueue.EnqueueWithoutDispatch(exception, dequeuedCallback); + } + + public bool EnqueueWithoutDispatch(TDisposable item, Action dequeuedCallback) + { + OnEnqueueItem(item); + + // NOTE: don't need to check IsDisposed here: InputQueue will handle dispose + return inputQueue.EnqueueWithoutDispatch(item, dequeuedCallback); + } + + public void Dispatch() + { + // NOTE: don't need to check IsDisposed here: InputQueue will handle dispose + inputQueue.Dispatch(); + } + + public void Shutdown() + { + inputQueue.Shutdown(); + } + + protected override void OnFaulted() + { + base.OnFaulted(); + inputQueue.Shutdown(() => GetPendingException()); + } + + protected virtual void OnEnqueueItem(TDisposable item) + { + } + + protected async Task> DequeueAsync(CancellationToken token) + { + ThrowIfNotOpened(); + var result = await inputQueue.TryDequeueAsync(token); + bool dequeued = result.Success; + + if (result.Result == null) + { + ThrowIfFaulted(); + ThrowIfAborted(); + } + + return result; + } + + protected async Task WaitForItemAsync(CancellationToken token) + { + ThrowIfNotOpened(); + bool dequeued = await inputQueue.WaitForItemAsync(token); + + ThrowIfFaulted(); + ThrowIfAborted(); + + return dequeued; + } + + protected override void OnClosing() + { + base.OnClosing(); + inputQueue.Shutdown(() => GetPendingException()); + } + + protected override void OnAbort() + { + inputQueue.Close(); + } + + protected override Task OnCloseAsync(CancellationToken token) + { + inputQueue.Close(); + return Task.CompletedTask; + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/InputQueueChannelAcceptor.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/InputQueueChannelAcceptor.cs new file mode 100644 index 000000000..ef1d79875 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/InputQueueChannelAcceptor.cs @@ -0,0 +1,99 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using CoreWCF.Runtime; +using CoreWCF.Diagnostics; + +namespace CoreWCF.Channels +{ + // TODO: Consider making common code compiled into each assembly. These are private on the full framework and are now public. + public class InputQueueChannelAcceptor : ChannelAcceptor + where TChannel : class, IChannel + { + InputQueue channelQueue; + + public InputQueueChannelAcceptor(ChannelManagerBase channelManager) + : base(channelManager) + { + channelQueue = new InputQueue() + { + DisposeItemCallback = value => + { + if (value is ICommunicationObject) + { + ((ICommunicationObject)value).Abort(); + } + } + }; + } + + public int PendingCount + { + get { return channelQueue.PendingCount; } + } + + public override Task AcceptChannelAsync(CancellationToken token) + { + ThrowIfNotOpened(); + return channelQueue.DequeueAsync(token); + } + + public void Dispatch() + { + channelQueue.Dispatch(); + } + + public void EnqueueAndDispatch(TChannel channel) + { + channelQueue.EnqueueAndDispatch(channel); + } + + public void EnqueueAndDispatch(TChannel channel, Action dequeuedCallback) + { + channelQueue.EnqueueAndDispatch(channel, dequeuedCallback); + } + + public bool EnqueueWithoutDispatch(TChannel channel, Action dequeuedCallback) + { + return channelQueue.EnqueueWithoutDispatch(channel, dequeuedCallback); + } + + public virtual bool EnqueueWithoutDispatch(Exception exception, Action dequeuedCallback) + { + return channelQueue.EnqueueWithoutDispatch(exception, dequeuedCallback); + } + + public void EnqueueAndDispatch(TChannel channel, Action dequeuedCallback, bool canDispatchOnThisThread) + { + channelQueue.EnqueueAndDispatch(channel, dequeuedCallback, canDispatchOnThisThread); + } + + public virtual void EnqueueAndDispatch(Exception exception, Action dequeuedCallback, bool canDispatchOnThisThread) + { + channelQueue.EnqueueAndDispatch(exception, dequeuedCallback, canDispatchOnThisThread); + } + + public void FaultQueue() + { + Fault(); + } + + protected override void OnClosed() + { + base.OnClosed(); + channelQueue.Dispose(); + } + + protected override void OnFaulted() + { + channelQueue.Shutdown(() => ChannelManager.GetPendingException()); + base.OnFaulted(); + } + + public override Task WaitForChannelAsync(CancellationToken token) + { + ThrowIfNotOpened(); + return channelQueue.WaitForItemAsync(token); + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/IntEncoder.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/IntEncoder.cs new file mode 100644 index 000000000..f03c16a0b --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/IntEncoder.cs @@ -0,0 +1,41 @@ +using System; +using System.Text; +using CoreWCF.Runtime; + +namespace CoreWCF.Channels +{ + internal static class IntEncoder + { + public const int MaxEncodedSize = 5; + + public static int Encode(int value, byte[] bytes, int offset) + { + int count = 1; + while ((value & 0xFFFFFF80) != 0) + { + bytes[offset++] = (byte)((value & 0x7F) | 0x80); + count++; + value >>= 7; + } + bytes[offset] = (byte)value; + return count; + } + + public static int GetEncodedSize(int value) + { + if (value < 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value, + SR.ValueMustBeNonNegative)); + } + + int count = 1; + while ((value & 0xFFFFFF80) != 0) + { + count++; + value >>= 7; + } + return count; + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/LayeredChannelListener.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/LayeredChannelListener.cs new file mode 100644 index 000000000..4e1246d14 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/LayeredChannelListener.cs @@ -0,0 +1,222 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace CoreWCF.Channels +{ + abstract class LayeredChannelListener + : ChannelListenerBase + where TChannel : class, IChannel + { + IChannelListener innerChannelListener; + bool sharedInnerListener; + EventHandler onInnerListenerFaulted; + + protected LayeredChannelListener(IDefaultCommunicationTimeouts timeouts, IChannelListener innerChannelListener) + : this(false, timeouts, innerChannelListener) + { + } + + protected LayeredChannelListener(bool sharedInnerListener) + : this(sharedInnerListener, null, null) + { + } + + protected LayeredChannelListener(bool sharedInnerListener, IDefaultCommunicationTimeouts timeouts) + : this(sharedInnerListener, timeouts, null) + { + } + + protected LayeredChannelListener(bool sharedInnerListener, IDefaultCommunicationTimeouts timeouts, IChannelListener innerChannelListener) + : base(timeouts) + { + this.sharedInnerListener = sharedInnerListener; + this.innerChannelListener = innerChannelListener; + onInnerListenerFaulted = new EventHandler(OnInnerListenerFaulted); + if (this.innerChannelListener != null) + { + this.innerChannelListener.Faulted += onInnerListenerFaulted; + } + } + + internal virtual IChannelListener InnerChannelListener + { + get + { + return innerChannelListener; + } + set + { + lock (ThisLock) + { + ThrowIfDisposedOrImmutable(); + if (innerChannelListener != null) + { + innerChannelListener.Faulted -= onInnerListenerFaulted; + } + innerChannelListener = value; + if (innerChannelListener != null) + { + innerChannelListener.Faulted += onInnerListenerFaulted; + } + } + } + } + + internal bool SharedInnerListener + { + get { return sharedInnerListener; } + } + + public override Uri Uri + { + get { return GetInnerListenerSnapshot().Uri; } + } + + public override T GetProperty() + { + T baseProperty = base.GetProperty(); + if (baseProperty != null) + { + return baseProperty; + } + + IChannelListener channelListener = InnerChannelListener; + if (channelListener != null) + { + return channelListener.GetProperty(); + } + else + { + return default(T); + } + } + + protected override void OnAbort() + { + lock (ThisLock) + { + OnCloseOrAbort(); + } + IChannelListener channelListener = InnerChannelListener; + if (channelListener != null && !sharedInnerListener) + { + channelListener.Abort(); + } + } + + protected override Task OnCloseAsync(CancellationToken token) + { + OnCloseOrAbort(); + if (InnerChannelListener != null && !sharedInnerListener) + { + return InnerChannelListener.CloseAsync(token); + } + + return Task.CompletedTask; + } + + void OnCloseOrAbort() + { + IChannelListener channelListener = InnerChannelListener; + if (channelListener != null) + { + channelListener.Faulted -= onInnerListenerFaulted; + } + } + + protected override Task OnOpenAsync(CancellationToken token) + { + if (InnerChannelListener != null && !sharedInnerListener) + return InnerChannelListener.OpenAsync(token); + + return Task.CompletedTask; + } + + protected override void OnOpening() + { + base.OnOpening(); + ThrowIfInnerListenerNotSet(); + } + + void OnInnerListenerFaulted(object sender, EventArgs e) + { + // if our inner listener faulted, we should fault as well + Fault(); + } + + internal void ThrowIfInnerListenerNotSet() + { + if (InnerChannelListener == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.InnerListenerFactoryNotSet, GetType().ToString()))); + } + } + + internal IChannelListener GetInnerListenerSnapshot() + { + IChannelListener innerChannelListener = InnerChannelListener; + + if (innerChannelListener == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.InnerListenerFactoryNotSet, GetType().ToString()))); + } + + return innerChannelListener; + } + } + + //abstract class LayeredChannelAcceptor : ChannelAcceptor + // where TChannel : class, IChannel + // where TInnerChannel : class, IChannel + //{ + // IChannelListener innerListener; + + // protected LayeredChannelAcceptor(ChannelManagerBase channelManager, IChannelListener innerListener) + // : base(channelManager) + // { + // this.innerListener = innerListener; + // } + + // protected abstract TChannel OnAcceptChannel(TInnerChannel innerChannel); + + // public override async Task AcceptChannelAsync(CancellationToken token) + // { + // TInnerChannel innerChannel = await this.innerListener.AcceptChannelAsync(token); + // if (innerChannel == null) + // return null; + // else + // return OnAcceptChannel(innerChannel); + // } + + // public override IAsyncResult BeginAcceptChannel(TimeSpan timeout, AsyncCallback callback, object state) + // { + // return this.innerListener.BeginAcceptChannel(timeout, callback, state); + // } + + // public override TChannel EndAcceptChannel(IAsyncResult result) + // { + // TInnerChannel innerChannel = this.innerListener.EndAcceptChannel(result); + // if (innerChannel == null) + // return null; + // else + // return OnAcceptChannel(innerChannel); + // } + + // public override bool WaitForChannel(TimeSpan timeout) + // { + // return this.innerListener.WaitForChannel(timeout); + // } + + // public override IAsyncResult BeginWaitForChannel(TimeSpan timeout, AsyncCallback callback, object state) + // { + // return this.innerListener.BeginWaitForChannel(timeout, callback, state); + // } + + // public override bool EndWaitForChannel(IAsyncResult result) + // { + // return this.innerListener.EndWaitForChannel(result); + // } + //} + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/LifetimeManager.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/LifetimeManager.cs new file mode 100644 index 000000000..a543d0bab --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/LifetimeManager.cs @@ -0,0 +1,329 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using CoreWCF.Runtime; + +namespace CoreWCF.Channels +{ + enum LifetimeState + { + Opened, + Closing, + Closed + } + + class LifetimeManager + { + bool _aborted; + int _busyCount; + ICommunicationWaiter _busyWaiter; + int _busyWaiterCount; + object _mutex; + LifetimeState _state; + + public LifetimeManager(object mutex) + { + _mutex = mutex; + _state = LifetimeState.Opened; + } + + public int BusyCount + { + get { return _busyCount; } + } + + protected LifetimeState State + { + get { return _state; } + } + + protected object ThisLock + { + get { return _mutex; } + } + + public void Abort() + { + lock (ThisLock) + { + if (State == LifetimeState.Closed || _aborted) + return; + _aborted = true; + _state = LifetimeState.Closing; + } + + OnAbort(); + _state = LifetimeState.Closed; + } + + void ThrowIfNotOpened() + { + if (!_aborted && _state != LifetimeState.Opened) + { + } + } + + public async Task CloseAsync(CancellationToken token) + { + token.ThrowIfCancellationRequested(); + lock (ThisLock) + { + ThrowIfNotOpened(); + _state = LifetimeState.Closing; + } + + await OnCloseAsync(token); + _state = LifetimeState.Closed; + } + + protected virtual async Task OnCloseAsync(CancellationToken token) + { + switch (await CloseCoreAsync(false, token)) + { + case CommunicationWaitResult.Expired: + // TODO: Derive CancellationToken so that the original timeout can be stored inside + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new TimeoutException(SR.Format(SR.SFxCloseTimedOut1, null))); + case CommunicationWaitResult.Aborted: + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ObjectDisposedException(GetType().ToString())); + } + } + + public async Task CloseCoreAsync(bool aborting, CancellationToken token) + { + token.ThrowIfCancellationRequested(); + ICommunicationWaiter busyWaiter = null; + CommunicationWaitResult result = CommunicationWaitResult.Succeeded; + + lock (ThisLock) + { + if (_busyCount > 0) + { + if (_busyWaiter != null) + { + if (!aborting && _aborted) + return CommunicationWaitResult.Aborted; + busyWaiter = _busyWaiter; + } + else + { + busyWaiter = new AsyncCommunicationWaiter(ThisLock); + _busyWaiter = busyWaiter; + } + Interlocked.Increment(ref _busyWaiterCount); + } + } + + if (busyWaiter != null) + { + result = await busyWaiter.WaitAsync(aborting, token); + if (Interlocked.Decrement(ref _busyWaiterCount) == 0) + { + busyWaiter.Dispose(); + _busyWaiter = null; + } + } + + return result; + } + + CommunicationWaitResult AbortCore(CancellationToken token) + { + ICommunicationWaiter busyWaiter = null; + CommunicationWaitResult result = CommunicationWaitResult.Succeeded; + + lock (ThisLock) + { + if (_busyCount > 0) + { + if (_busyWaiter != null) + { + busyWaiter = _busyWaiter; + } + else + { + busyWaiter = new AsyncCommunicationWaiter(ThisLock); + _busyWaiter = busyWaiter; + } + Interlocked.Increment(ref _busyWaiterCount); + } + } + + if (busyWaiter != null) + { + result = busyWaiter.Wait(true, token); + if (Interlocked.Decrement(ref _busyWaiterCount) == 0) + { + busyWaiter.Dispose(); + _busyWaiter = null; + } + } + + return result; + } + + protected void DecrementBusyCount() + { + ICommunicationWaiter busyWaiter = null; + bool empty = false; + + lock (ThisLock) + { + if (_busyCount <= 0) + { + throw Fx.AssertAndThrow("LifetimeManager.DecrementBusyCount: (this.busyCount > 0)"); + } + if (--_busyCount == 0) + { + if (_busyWaiter != null) + { + busyWaiter = _busyWaiter; + Interlocked.Increment(ref _busyWaiterCount); + } + empty = true; + } + } + + if (busyWaiter != null) + { + busyWaiter.Signal(); + if (Interlocked.Decrement(ref _busyWaiterCount) == 0) + { + busyWaiter.Dispose(); + _busyWaiter = null; + } + } + + if (empty && State == LifetimeState.Opened) + OnEmpty(); + } + + protected virtual void IncrementBusyCount() + { + lock (ThisLock) + { + Fx.Assert(State == LifetimeState.Opened, "LifetimeManager.IncrementBusyCount: (this.State == LifetimeState.Opened)"); + _busyCount++; + } + } + + protected virtual void IncrementBusyCountWithoutLock() + { + Fx.Assert(State == LifetimeState.Opened, "LifetimeManager.IncrementBusyCountWithoutLock: (this.State == LifetimeState.Opened)"); + _busyCount++; + } + + protected virtual void OnAbort() + { + // We have decided not to make this configurable + AbortCore(new CancellationTokenSource(TimeSpan.FromSeconds(1)).Token); + } + + protected virtual void OnEmpty() + { + } + } + + enum CommunicationWaitResult + { + Waiting, + Succeeded, + Expired, + Aborted + } + + interface ICommunicationWaiter : IDisposable + { + void Signal(); + Task WaitAsync(bool aborting, CancellationToken token); + CommunicationWaitResult Wait(bool aborting, CancellationToken token); + } + + internal class AsyncCommunicationWaiter : ICommunicationWaiter + { + private bool _closed; + private object _mutex; + private CommunicationWaitResult _result; + + private TaskCompletionSource _tcs; + + internal AsyncCommunicationWaiter(object mutex) + { + _mutex = mutex; + _tcs = new TaskCompletionSource(); + } + + private object ThisLock + { + get { return _mutex; } + } + + public void Dispose() + { + lock (ThisLock) + { + if (_closed) + return; + _closed = true; + _tcs?.TrySetResult(false); + } + } + + public void Signal() + { + lock (ThisLock) + { + if (_closed) + return; + _tcs.TrySetResult(true); + } + } + + public async Task WaitAsync(bool aborting, CancellationToken token) + { + Fx.Assert(token.CanBeCanceled, "CancellationToken must be cancellable"); + + if (_closed) + { + return CommunicationWaitResult.Aborted; + } + + if (token.IsCancellationRequested) + { + return CommunicationWaitResult.Expired; + } + + if (aborting) + { + _result = CommunicationWaitResult.Aborted; + } + + _tcs = new TaskCompletionSource(); + using (token.Register(WaiterTimeout, _tcs)) + { + await _tcs.Task; + bool expired = token.IsCancellationRequested; + + lock (ThisLock) + { + if (_result == CommunicationWaitResult.Waiting) + { + _result = (expired ? CommunicationWaitResult.Expired : CommunicationWaitResult.Succeeded); + } + } + + return _result; + } + } + + public CommunicationWaitResult Wait(bool aborting, CancellationToken token) + { + return WaitAsync(aborting, token).GetAwaiter().GetResult(); + } + + internal static void WaiterTimeout(object state) + { + var tcs = state as TaskCompletionSource; + tcs?.TrySetResult(false); + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/Message.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/Message.cs new file mode 100644 index 000000000..992198278 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/Message.cs @@ -0,0 +1,2039 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Runtime.Serialization; +using System.Threading.Tasks; +using System.Xml; +using CoreWCF.Runtime; +using CoreWCF.Diagnostics; +using CoreWCF.Dispatcher; + +namespace CoreWCF.Channels +{ + public abstract class Message : System.IDisposable + { + MessageState state; + //SeekableMessageNavigator messageNavigator; + internal const int InitialBufferSize = 1024; + + public abstract MessageHeaders Headers { get; } // must never return null + + protected bool IsDisposed + { + get { return state == MessageState.Closed; } + } + + public virtual bool IsFault + { + get + { + if (IsDisposed) + throw TraceUtility.ThrowHelperError(CreateMessageDisposedException(), this); + + return false; + } + } + + public virtual bool IsEmpty + { + get + { + if (IsDisposed) + throw TraceUtility.ThrowHelperError(CreateMessageDisposedException(), this); + + return false; + } + } + + public abstract MessageProperties Properties { get; } + + public abstract MessageVersion Version { get; } // must never return null + + internal virtual RecycledMessageState RecycledMessageState + { + get { return null; } + } + + public MessageState State + { + get { return state; } + } + + internal void BodyToString(XmlDictionaryWriter writer) + { + OnBodyToString(writer); + } + + public void Close() + { + if (state != MessageState.Closed) + { + state = MessageState.Closed; + OnClose(); + //if (DiagnosticUtility.ShouldTraceVerbose) + //{ + // TraceUtility.TraceEvent(TraceEventType.Verbose, TraceCode.MessageClosed, + // SR.TraceCodeMessageClosed, this); + //} + } + else + { + //if (DiagnosticUtility.ShouldTraceVerbose) + //{ + // TraceUtility.TraceEvent(TraceEventType.Verbose, TraceCode.MessageClosedAgain, + // SR.TraceCodeMessageClosedAgain, this); + //} + } + } + + public MessageBuffer CreateBufferedCopy(int maxBufferSize) + { + if (maxBufferSize < 0) + throw TraceUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(maxBufferSize), maxBufferSize, + SR.ValueMustBeNonNegative), this); + switch (state) + { + case MessageState.Created: + state = MessageState.Copied; + //if (DiagnosticUtility.ShouldTraceVerbose) + //{ + // TraceUtility.TraceEvent(TraceEventType.Verbose, TraceCode.MessageCopied, + // SR.TraceCodeMessageCopied, this, this); + //} + break; + case MessageState.Closed: + throw TraceUtility.ThrowHelperError(CreateMessageDisposedException(), this); + case MessageState.Copied: + throw TraceUtility.ThrowHelperError(new InvalidOperationException(SR.MessageHasBeenCopied), this); + case MessageState.Read: + throw TraceUtility.ThrowHelperError(new InvalidOperationException(SR.MessageHasBeenRead), this); + case MessageState.Written: + throw TraceUtility.ThrowHelperError(new InvalidOperationException(SR.MessageHasBeenWritten), this); + default: + Fx.Assert(SR.InvalidMessageState); + throw TraceUtility.ThrowHelperError(new InvalidOperationException(SR.InvalidMessageState), this); + } + return OnCreateBufferedCopy(maxBufferSize); + } + + internal static Type GetObjectType(object value) + { + return (value == null) ? typeof(object) : value.GetType(); + } + + public static Message CreateMessage(MessageVersion version, string action, object body) + { + return CreateMessage(version, action, body, DataContractSerializerDefaults.CreateSerializer(GetObjectType(body), int.MaxValue/*maxItems*/)); + } + + public static Message CreateMessage(MessageVersion version, string action, object body, XmlObjectSerializer serializer) + { + if (version == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("version"); + if (serializer == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("serializer"); + return new BodyWriterMessage(version, action, new XmlObjectSerializerBodyWriter(body, serializer)); + } + + public static Message CreateMessage(MessageVersion version, string action, XmlReader body) + { + return CreateMessage(version, action, XmlDictionaryReader.CreateDictionaryReader(body)); + } + + public static Message CreateMessage(MessageVersion version, string action, XmlDictionaryReader body) + { + if (body == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("body"); + if (version == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("version"); + + return CreateMessage(version, action, new XmlReaderBodyWriter(body, version.Envelope)); + } + + public static Message CreateMessage(MessageVersion version, string action, BodyWriter body) + { + if (version == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("version"); + if (body == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("body"); + return new BodyWriterMessage(version, action, body); + } + + internal static Message CreateMessage(MessageVersion version, ActionHeader actionHeader, BodyWriter body) + { + if (version == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(version)); + if (body == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(body)); + return new BodyWriterMessage(version, actionHeader, body); + } + + public static Message CreateMessage(MessageVersion version, string action) + { + if (version == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("version"); + return new BodyWriterMessage(version, action, EmptyBodyWriter.Value); + } + + //static internal Message CreateMessage(MessageVersion version, ActionHeader actionHeader) + //{ + // if (version == null) + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("version")); + // return new BodyWriterMessage(version, actionHeader, EmptyBodyWriter.Value); + //} + + public static Message CreateMessage(XmlReader envelopeReader, int maxSizeOfHeaders, MessageVersion version) + { + return CreateMessage(XmlDictionaryReader.CreateDictionaryReader(envelopeReader), maxSizeOfHeaders, version); + } + + public static Message CreateMessage(XmlDictionaryReader envelopeReader, int maxSizeOfHeaders, MessageVersion version) + { + if (envelopeReader == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("envelopeReader"); + if (version == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("version"); + Message message = new StreamedMessage(envelopeReader, maxSizeOfHeaders, version); + return message; + } + + internal static Message CreateMessage(MessageVersion version, FaultCode faultCode, string reason, string action) + { + if (version == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(version)); + if (faultCode == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(faultCode)); + if (reason == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(reason)); + + return CreateMessage(version, MessageFault.CreateFault(faultCode, reason), action); + } + + //public static Message CreateMessage(MessageVersion version, FaultCode faultCode, string reason, object detail, string action) + //{ + // if (version == null) + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("version")); + // if (faultCode == null) + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("faultCode")); + // if (reason == null) + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("reason")); + + // return CreateMessage(version, MessageFault.CreateFault(faultCode, new FaultReason(reason), detail), action); + //} + + // TODO: This method SHOULD be made public in the contract as without it, you can't create a Message from a MessageFault without duplicating a LOT of code + public static Message CreateMessage(MessageVersion version, MessageFault fault, string action) + { + if (fault == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(fault)); + if (version == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(version)); + return new BodyWriterMessage(version, action, new FaultBodyWriter(fault, version.Envelope)); + } + + internal Exception CreateMessageDisposedException() + { + return new ObjectDisposedException("", SR.MessageClosed); + } + + void IDisposable.Dispose() + { + Close(); + } + + public T GetBody() + { + XmlDictionaryReader reader = GetReaderAtBodyContents(); // This call will change the message state to Read. + return OnGetBody(reader); + } + + protected virtual T OnGetBody(XmlDictionaryReader reader) + { + return GetBodyCore(reader, DataContractSerializerDefaults.CreateSerializer(typeof(T), int.MaxValue/*maxItems*/)); + } + + public T GetBody(XmlObjectSerializer serializer) + { + if (serializer == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("serializer"); + return GetBodyCore(GetReaderAtBodyContents(), serializer); + } + + T GetBodyCore(XmlDictionaryReader reader, XmlObjectSerializer serializer) + { + T value; + using (reader) + { + value = (T)serializer.ReadObject(reader); + ReadFromBodyContentsToEnd(reader); + } + return value; + } + + internal virtual XmlDictionaryReader GetReaderAtHeader() + { + XmlBuffer buffer = new XmlBuffer(int.MaxValue); + XmlDictionaryWriter writer = buffer.OpenSection(XmlDictionaryReaderQuotas.Max); + WriteStartEnvelope(writer); + MessageHeaders headers = Headers; + for (int i = 0; i < headers.Count; i++) + headers.WriteHeader(i, writer); + writer.WriteEndElement(); + writer.WriteEndElement(); + buffer.CloseSection(); + buffer.Close(); + XmlDictionaryReader reader = buffer.GetReader(0); + reader.ReadStartElement(); + reader.MoveToStartElement(); + return reader; + } + + public XmlDictionaryReader GetReaderAtBodyContents() + { + EnsureReadMessageState(); + if (IsEmpty) + throw TraceUtility.ThrowHelperError(new InvalidOperationException(SR.MessageIsEmpty), this); + return OnGetReaderAtBodyContents(); + } + + internal void EnsureReadMessageState() + { + switch (state) + { + case MessageState.Created: + state = MessageState.Read; + //if (DiagnosticUtility.ShouldTraceVerbose) + //{ + // TraceUtility.TraceEvent(TraceEventType.Verbose, TraceCode.MessageRead, SR.Format(SR.TraceCodeMessageRead), this); + //} + break; + case MessageState.Copied: + throw TraceUtility.ThrowHelperError(new InvalidOperationException(SR.MessageHasBeenCopied), this); + case MessageState.Read: + throw TraceUtility.ThrowHelperError(new InvalidOperationException(SR.MessageHasBeenRead), this); + case MessageState.Written: + throw TraceUtility.ThrowHelperError(new InvalidOperationException(SR.MessageHasBeenWritten), this); + case MessageState.Closed: + throw TraceUtility.ThrowHelperError(CreateMessageDisposedException(), this); + default: + Fx.Assert(SR.InvalidMessageState); + throw TraceUtility.ThrowHelperError(new InvalidOperationException(SR.InvalidMessageState), this); + } + } + + //internal SeekableMessageNavigator GetNavigator(bool navigateBody, int maxNodes) + //{ + // if (IsDisposed) + // throw TraceUtility.ThrowHelperError(CreateMessageDisposedException(), this); + // if (null == this.messageNavigator) + // { + // this.messageNavigator = new SeekableMessageNavigator(this, maxNodes, XmlSpace.Default, navigateBody, false); + // } + // else + // { + // this.messageNavigator.ForkNodeCount(maxNodes); + // } + + // return this.messageNavigator; + //} + + //internal void InitializeReply(Message request) + //{ + // UniqueId requestMessageID = request.Headers.MessageId; + // if (requestMessageID == null) + // throw TraceUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.RequestMessageDoesNotHaveAMessageID)), request); + // Headers.RelatesTo = requestMessageID; + //} + + static internal bool IsFaultStartElement(XmlDictionaryReader reader, EnvelopeVersion version) + { + return reader.IsStartElement(XD.MessageDictionary.Fault, version.DictionaryNamespace); + } + + protected virtual void OnBodyToString(XmlDictionaryWriter writer) + { + writer.WriteString(SR.MessageBodyIsUnknown); + } + + protected virtual MessageBuffer OnCreateBufferedCopy(int maxBufferSize) + { + return OnCreateBufferedCopy(maxBufferSize, XmlDictionaryReaderQuotas.Max); + } + + internal MessageBuffer OnCreateBufferedCopy(int maxBufferSize, XmlDictionaryReaderQuotas quotas) + { + XmlBuffer msgBuffer = new XmlBuffer(maxBufferSize); + XmlDictionaryWriter writer = msgBuffer.OpenSection(quotas); + OnWriteMessage(writer); + msgBuffer.CloseSection(); + msgBuffer.Close(); + return new DefaultMessageBuffer(this, msgBuffer); + } + + protected virtual void OnClose() + { + } + + protected virtual XmlDictionaryReader OnGetReaderAtBodyContents() + { + XmlBuffer bodyBuffer = new XmlBuffer(int.MaxValue); + XmlDictionaryWriter writer = bodyBuffer.OpenSection(XmlDictionaryReaderQuotas.Max); + if (Version.Envelope != EnvelopeVersion.None) + { + OnWriteStartEnvelope(writer); + OnWriteStartBody(writer); + } + OnWriteBodyContents(writer); + if (Version.Envelope != EnvelopeVersion.None) + { + writer.WriteEndElement(); + writer.WriteEndElement(); + } + bodyBuffer.CloseSection(); + bodyBuffer.Close(); + XmlDictionaryReader reader = bodyBuffer.GetReader(0); + if (Version.Envelope != EnvelopeVersion.None) + { + reader.ReadStartElement(); + reader.ReadStartElement(); + } + reader.MoveToContent(); + return reader; + } + + protected virtual void OnWriteStartBody(XmlDictionaryWriter writer) + { + MessageDictionary messageDictionary = XD.MessageDictionary; + writer.WriteStartElement(messageDictionary.Prefix.Value, messageDictionary.Body, Version.Envelope.DictionaryNamespace); + } + + public void WriteBodyContents(XmlDictionaryWriter writer) + { + EnsureWriteMessageState(writer); + OnWriteBodyContents(writer); + } + + //public IAsyncResult BeginWriteBodyContents(XmlDictionaryWriter writer, AsyncCallback callback, object state) + //{ + // EnsureWriteMessageState(writer); + // return this.OnBeginWriteBodyContents(writer, callback, state); + //} + + //public void EndWriteBodyContents(IAsyncResult result) + //{ + // this.OnEndWriteBodyContents(result); + //} + + protected abstract void OnWriteBodyContents(XmlDictionaryWriter writer); + + internal virtual Task OnWriteBodyContentsAsync(XmlDictionaryWriter writer) + { + OnWriteBodyContents(writer); + return Task.CompletedTask; + } + + //protected virtual IAsyncResult OnBeginWriteBodyContents(XmlDictionaryWriter writer, AsyncCallback callback, object state) + //{ + // return new OnWriteBodyContentsAsyncResult(writer, this, callback, state); + //} + + //protected virtual void OnEndWriteBodyContents(IAsyncResult result) + //{ + // OnWriteBodyContentsAsyncResult.End(result); + //} + + public void WriteStartEnvelope(XmlDictionaryWriter writer) + { + if (writer == null) + throw TraceUtility.ThrowHelperError(new ArgumentNullException(nameof(writer)), this); + + OnWriteStartEnvelope(writer); + } + + protected virtual void OnWriteStartEnvelope(XmlDictionaryWriter writer) + { + EnvelopeVersion envelopeVersion = Version.Envelope; + if (envelopeVersion != EnvelopeVersion.None) + { + MessageDictionary messageDictionary = XD.MessageDictionary; + writer.WriteStartElement(messageDictionary.Prefix.Value, messageDictionary.Envelope, envelopeVersion.DictionaryNamespace); + WriteSharedHeaderPrefixes(writer); + } + } + + protected virtual void OnWriteStartHeaders(XmlDictionaryWriter writer) + { + EnvelopeVersion envelopeVersion = Version.Envelope; + if (envelopeVersion != EnvelopeVersion.None) + { + MessageDictionary messageDictionary = XD.MessageDictionary; + writer.WriteStartElement(messageDictionary.Prefix.Value, messageDictionary.Header, envelopeVersion.DictionaryNamespace); + } + } + + public override string ToString() + { + if (IsDisposed) + { + return base.ToString(); + } + + XmlWriterSettings settings = new XmlWriterSettings(); + settings.Indent = true; + + using (StringWriter stringWriter = new StringWriter(CultureInfo.InvariantCulture)) + { + using (XmlWriter textWriter = XmlWriter.Create(stringWriter, settings)) + { + using (XmlDictionaryWriter writer = XmlDictionaryWriter.CreateDictionaryWriter(textWriter)) + { + try + { + ToString(writer); + writer.Flush(); + return stringWriter.ToString(); + } + catch (XmlException e) + { + return SR.Format(SR.MessageBodyToStringError, e.GetType().ToString(), e.Message); + } + } + } + } + } + + internal void ToString(XmlDictionaryWriter writer) + { + if (IsDisposed) + { + throw TraceUtility.ThrowHelperError(CreateMessageDisposedException(), this); + } + + if (Version.Envelope != EnvelopeVersion.None) + { + WriteStartEnvelope(writer); + WriteStartHeaders(writer); + MessageHeaders headers = Headers; + for (int i = 0; i < headers.Count; i++) + { + headers.WriteHeader(i, writer); + } + + writer.WriteEndElement(); + MessageDictionary messageDictionary = XD.MessageDictionary; + WriteStartBody(writer); + } + + BodyToString(writer); + + if (Version.Envelope != EnvelopeVersion.None) + { + writer.WriteEndElement(); + writer.WriteEndElement(); + } + } + + public string GetBodyAttribute(string localName, string ns) + { + if (localName == null) + throw TraceUtility.ThrowHelperError(new ArgumentNullException(nameof(localName)), this); + if (ns == null) + throw TraceUtility.ThrowHelperError(new ArgumentNullException(nameof(ns)), this); + switch (state) + { + case MessageState.Created: + break; + case MessageState.Copied: + throw TraceUtility.ThrowHelperError(new InvalidOperationException(SR.MessageHasBeenCopied), this); + case MessageState.Read: + throw TraceUtility.ThrowHelperError(new InvalidOperationException(SR.MessageHasBeenRead), this); + case MessageState.Written: + throw TraceUtility.ThrowHelperError(new InvalidOperationException(SR.MessageHasBeenWritten), this); + case MessageState.Closed: + throw TraceUtility.ThrowHelperError(CreateMessageDisposedException(), this); + default: + Fx.Assert(SR.InvalidMessageState); + throw TraceUtility.ThrowHelperError(new InvalidOperationException(SR.InvalidMessageState), this); + } + return OnGetBodyAttribute(localName, ns); + } + + protected virtual string OnGetBodyAttribute(string localName, string ns) + { + return null; + } + + internal void ReadFromBodyContentsToEnd(XmlDictionaryReader reader) + { + Message.ReadFromBodyContentsToEnd(reader, Version.Envelope); + } + + static void ReadFromBodyContentsToEnd(XmlDictionaryReader reader, EnvelopeVersion envelopeVersion) + { + if (envelopeVersion != EnvelopeVersion.None) + { + reader.ReadEndElement(); // + reader.ReadEndElement(); // + } + reader.MoveToContent(); + } + + internal static bool ReadStartBody(XmlDictionaryReader reader, EnvelopeVersion envelopeVersion, out bool isFault, out bool isEmpty) + { + if (reader.IsEmptyElement) + { + reader.Read(); + isEmpty = true; + isFault = false; + reader.ReadEndElement(); + return false; + } + else + { + reader.Read(); + if (reader.NodeType != XmlNodeType.Element) + reader.MoveToContent(); + if (reader.NodeType == XmlNodeType.Element) + { + isFault = IsFaultStartElement(reader, envelopeVersion); + isEmpty = false; + } + else if (reader.NodeType == XmlNodeType.EndElement) + { + isEmpty = true; + isFault = false; + Message.ReadFromBodyContentsToEnd(reader, envelopeVersion); + return false; + } + else + { + isEmpty = false; + isFault = false; + } + + return true; + } + } + + public void WriteBody(XmlWriter writer) + { + WriteBody(XmlDictionaryWriter.CreateDictionaryWriter(writer)); + } + + public void WriteBody(XmlDictionaryWriter writer) + { + WriteStartBody(writer); + WriteBodyContents(writer); + writer.WriteEndElement(); + } + + public void WriteStartBody(XmlWriter writer) + { + WriteStartBody(XmlDictionaryWriter.CreateDictionaryWriter(writer)); + } + + public void WriteStartBody(XmlDictionaryWriter writer) + { + if (writer == null) + throw TraceUtility.ThrowHelperError(new ArgumentNullException(nameof(writer)), this); + OnWriteStartBody(writer); + } + + internal void WriteStartHeaders(XmlDictionaryWriter writer) + { + OnWriteStartHeaders(writer); + } + + public void WriteMessage(XmlWriter writer) + { + WriteMessage(XmlDictionaryWriter.CreateDictionaryWriter(writer)); + } + + public void WriteMessage(XmlDictionaryWriter writer) + { + EnsureWriteMessageState(writer); + OnWriteMessage(writer); + } + + public virtual Task WriteMessageAsync(XmlWriter writer) + { + WriteMessage(writer); + return TaskHelpers.CompletedTask(); + } + + public virtual async Task WriteMessageAsync(XmlDictionaryWriter writer) + { + EnsureWriteMessageState(writer); + await OnWriteMessageAsync(writer); + } + + public virtual async Task OnWriteMessageAsync(XmlDictionaryWriter writer) + { + WriteMessagePreamble(writer); + + // We should call OnWriteBodyContentsAsync instead of WriteBodyContentsAsync here, + // otherwise EnsureWriteMessageState would get called twice. Also see OnWriteMessage() + // for the example. + await OnWriteBodyContentsAsync(writer); + WriteMessagePostamble(writer); + } + + void EnsureWriteMessageState(XmlDictionaryWriter writer) + { + if (writer == null) + throw TraceUtility.ThrowHelperError(new ArgumentNullException(nameof(writer)), this); + switch (state) + { + case MessageState.Created: + state = MessageState.Written; + //if (DiagnosticUtility.ShouldTraceVerbose) + //{ + // TraceUtility.TraceEvent(TraceEventType.Verbose, TraceCode.MessageWritten, SR.TraceCodeMessageWritten, this); + //} + break; + case MessageState.Copied: + throw TraceUtility.ThrowHelperError(new InvalidOperationException(SR.MessageHasBeenCopied), this); + case MessageState.Read: + throw TraceUtility.ThrowHelperError(new InvalidOperationException(SR.MessageHasBeenRead), this); + case MessageState.Written: + throw TraceUtility.ThrowHelperError(new InvalidOperationException(SR.MessageHasBeenWritten), this); + case MessageState.Closed: + throw TraceUtility.ThrowHelperError(CreateMessageDisposedException(), this); + default: + Fx.Assert(SR.InvalidMessageState); + throw TraceUtility.ThrowHelperError(new InvalidOperationException(SR.InvalidMessageState), this); + } + } + + //public IAsyncResult BeginWriteMessage(XmlDictionaryWriter writer, AsyncCallback callback, object state) + //{ + // EnsureWriteMessageState(writer); + // return OnBeginWriteMessage(writer, callback, state); + //} + + //public void EndWriteMessage(IAsyncResult result) + //{ + // OnEndWriteMessage(result); + //} + + protected virtual void OnWriteMessage(XmlDictionaryWriter writer) + { + WriteMessagePreamble(writer); + OnWriteBodyContents(writer); + WriteMessagePostamble(writer); + } + + internal void WriteMessagePreamble(XmlDictionaryWriter writer) + { + if (Version.Envelope != EnvelopeVersion.None) + { + OnWriteStartEnvelope(writer); + + MessageHeaders headers = Headers; + int headersCount = headers.Count; + if (headersCount > 0) + { + OnWriteStartHeaders(writer); + for (int i = 0; i < headersCount; i++) + { + headers.WriteHeader(i, writer); + } + writer.WriteEndElement(); + } + + OnWriteStartBody(writer); + } + } + + internal void WriteMessagePostamble(XmlDictionaryWriter writer) + { + if (Version.Envelope != EnvelopeVersion.None) + { + writer.WriteEndElement(); + writer.WriteEndElement(); + } + } + + private void WriteSharedHeaderPrefixes(XmlDictionaryWriter writer) + { + MessageHeaders headers = Headers; + int count = headers.Count; + int prefixesWritten = 0; + for (int i = 0; i < count; i++) + { + if (Version.Addressing == AddressingVersion.None && headers[i].Namespace == AddressingVersion.None.Namespace) + { + continue; + } + + IMessageHeaderWithSharedNamespace headerWithSharedNamespace = headers[i] as IMessageHeaderWithSharedNamespace; + if (headerWithSharedNamespace != null) + { + XmlDictionaryString prefix = headerWithSharedNamespace.SharedPrefix; + string prefixString = prefix.Value; + if (!((prefixString.Length == 1))) + { + Fx.Assert("Message.WriteSharedHeaderPrefixes: (prefixString.Length == 1) -- IMessageHeaderWithSharedNamespace must use a single lowercase letter prefix."); + throw TraceUtility.ThrowHelperError(new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "IMessageHeaderWithSharedNamespace must use a single lowercase letter prefix.")), this); + } + + int prefixIndex = prefixString[0] - 'a'; + if (!((prefixIndex >= 0 && prefixIndex < 26))) + { + Fx.Assert("Message.WriteSharedHeaderPrefixes: (prefixIndex >= 0 && prefixIndex < 26) -- IMessageHeaderWithSharedNamespace must use a single lowercase letter prefix."); + throw TraceUtility.ThrowHelperError(new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "IMessageHeaderWithSharedNamespace must use a single lowercase letter prefix.")), this); + } + int prefixBit = 1 << prefixIndex; + if ((prefixesWritten & prefixBit) == 0) + { + writer.WriteXmlnsAttribute(prefixString, headerWithSharedNamespace.SharedNamespace); + prefixesWritten |= prefixBit; + } + } + } + } + } + + class EmptyBodyWriter : BodyWriter + { + static EmptyBodyWriter value; + + EmptyBodyWriter() + : base(true) + { + } + + public static EmptyBodyWriter Value + { + get + { + if (value == null) + value = new EmptyBodyWriter(); + return value; + } + } + + internal override bool IsEmpty + { + get { return true; } + } + + protected override void OnWriteBodyContents(XmlDictionaryWriter writer) + { + } + } + + class FaultBodyWriter : BodyWriter + { + MessageFault fault; + EnvelopeVersion version; + + public FaultBodyWriter(MessageFault fault, EnvelopeVersion version) + : base(true) + { + this.fault = fault; + this.version = version; + } + + internal override bool IsFault + { + get { return true; } + } + + protected override void OnWriteBodyContents(XmlDictionaryWriter writer) + { + fault.WriteTo(writer, version); + } + } + + class XmlObjectSerializerBodyWriter : BodyWriter + { + object body; + XmlObjectSerializer serializer; + + public XmlObjectSerializerBodyWriter(object body, XmlObjectSerializer serializer) + : base(true) + { + this.body = body; + this.serializer = serializer; + } + + object ThisLock + { + get { return this; } + } + + protected override void OnWriteBodyContents(XmlDictionaryWriter writer) + { + lock (ThisLock) + { + serializer.WriteObject(writer, body); + } + } + } + + class XmlReaderBodyWriter : BodyWriter + { + XmlDictionaryReader reader; + bool isFault; + + public XmlReaderBodyWriter(XmlDictionaryReader reader, EnvelopeVersion version) + : base(false) + { + this.reader = reader; + if (reader.MoveToContent() != XmlNodeType.Element) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.InvalidReaderPositionOnCreateMessage, nameof(reader))); + + isFault = Message.IsFaultStartElement(reader, version); + } + + internal override bool IsFault + { + get + { + return isFault; + } + } + + protected override BodyWriter OnCreateBufferedCopy(int maxBufferSize) + { + return OnCreateBufferedCopy(maxBufferSize, reader.Quotas); + } + + protected override void OnWriteBodyContents(XmlDictionaryWriter writer) + { + using (reader) + { + XmlNodeType type = reader.MoveToContent(); + while (!reader.EOF && type != XmlNodeType.EndElement) + { + if (type != XmlNodeType.Element) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.InvalidReaderPositionOnCreateMessage, "reader")); + writer.WriteNode(reader, false); + + type = reader.MoveToContent(); + } + } + } + } + + class BodyWriterMessage : Message + { + MessageProperties properties; + MessageHeaders headers; + BodyWriter bodyWriter; + + BodyWriterMessage(BodyWriter bodyWriter) + { + this.bodyWriter = bodyWriter; + } + + public BodyWriterMessage(MessageVersion version, string action, BodyWriter bodyWriter) + : this(bodyWriter) + { + headers = new MessageHeaders(version); + headers.Action = action; + } + + public BodyWriterMessage(MessageVersion version, ActionHeader actionHeader, BodyWriter bodyWriter) + : this(bodyWriter) + { + headers = new MessageHeaders(version); + headers.SetActionHeader(actionHeader); + } + + public BodyWriterMessage(MessageHeaders headers, KeyValuePair[] properties, BodyWriter bodyWriter) + : this(bodyWriter) + { + this.headers = new MessageHeaders(headers); + this.properties = new MessageProperties(properties); + } + + public override bool IsFault + { + get + { + if (IsDisposed) + throw TraceUtility.ThrowHelperError(CreateMessageDisposedException(), this); + return bodyWriter.IsFault; + } + } + + public override bool IsEmpty + { + get + { + if (IsDisposed) + throw TraceUtility.ThrowHelperError(CreateMessageDisposedException(), this); + return bodyWriter.IsEmpty; + } + } + + public override MessageHeaders Headers + { + get + { + if (IsDisposed) + throw TraceUtility.ThrowHelperError(CreateMessageDisposedException(), this); + return headers; + } + } + + public override MessageProperties Properties + { + get + { + if (IsDisposed) + throw TraceUtility.ThrowHelperError(CreateMessageDisposedException(), this); + if (properties == null) + properties = new MessageProperties(); + return properties; + } + } + + public override MessageVersion Version + { + get + { + if (IsDisposed) + throw TraceUtility.ThrowHelperError(CreateMessageDisposedException(), this); + return headers.MessageVersion; + } + } + + protected override MessageBuffer OnCreateBufferedCopy(int maxBufferSize) + { + BodyWriter bufferedBodyWriter; + if (bodyWriter.IsBuffered) + { + bufferedBodyWriter = bodyWriter; + } + else + { + bufferedBodyWriter = bodyWriter.CreateBufferedCopy(maxBufferSize); + } + KeyValuePair[] properties = new KeyValuePair[Properties.Count]; + ((ICollection>)Properties).CopyTo(properties, 0); + return new BodyWriterMessageBuffer(headers, properties, bufferedBodyWriter); + } + + protected override void OnClose() + { + Exception ex = null; + try + { + base.OnClose(); + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + throw; + ex = e; + } + + try + { + if (properties != null) + properties.Dispose(); + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + throw; + if (ex == null) + ex = e; + } + + if (ex != null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(ex); + + bodyWriter = null; + } + + protected override void OnWriteBodyContents(XmlDictionaryWriter writer) + { + bodyWriter.WriteBodyContents(writer); + } + + public override async Task OnWriteMessageAsync(XmlDictionaryWriter writer) + { + await OnWriteBodyContentsAsync(writer); + WriteMessagePostamble(writer); + } + + internal override Task OnWriteBodyContentsAsync(XmlDictionaryWriter writer) + { + return bodyWriter.WriteBodyContentsAsync(writer); + } + + protected override void OnBodyToString(XmlDictionaryWriter writer) + { + if (bodyWriter.IsBuffered) + { + bodyWriter.WriteBodyContents(writer); + } + else + { + writer.WriteString(SR.MessageBodyIsStream); + } + } + + protected internal BodyWriter BodyWriter + { + get + { + return bodyWriter; + } + } + } + + abstract class ReceivedMessage : Message + { + bool isFault; + bool isEmpty; + + public override bool IsEmpty + { + get { return isEmpty; } + } + + public override bool IsFault + { + get { return isFault; } + } + + protected static bool HasHeaderElement(XmlDictionaryReader reader, EnvelopeVersion envelopeVersion) + { + return reader.IsStartElement(XD.MessageDictionary.Header, envelopeVersion.DictionaryNamespace); + } + + protected override void OnWriteBodyContents(XmlDictionaryWriter writer) + { + if (!isEmpty) + { + using (XmlDictionaryReader bodyReader = OnGetReaderAtBodyContents()) + { + if (bodyReader.ReadState == ReadState.Error || bodyReader.ReadState == ReadState.Closed) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.MessageBodyReaderInvalidReadState, bodyReader.ReadState.ToString()))); + + while (bodyReader.NodeType != XmlNodeType.EndElement && !bodyReader.EOF) + { + writer.WriteNode(bodyReader, false); + } + + ReadFromBodyContentsToEnd(bodyReader); + } + } + } + + protected bool ReadStartBody(XmlDictionaryReader reader) + { + return Message.ReadStartBody(reader, Version.Envelope, out isFault, out isEmpty); + } + + protected static EnvelopeVersion ReadStartEnvelope(XmlDictionaryReader reader) + { + EnvelopeVersion envelopeVersion; + + if (reader.IsStartElement(XD.MessageDictionary.Envelope, XD.Message12Dictionary.Namespace)) + envelopeVersion = EnvelopeVersion.Soap12; + else if (reader.IsStartElement(XD.MessageDictionary.Envelope, XD.Message11Dictionary.Namespace)) + envelopeVersion = EnvelopeVersion.Soap11; + else + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(SR.MessageVersionUnknown)); + if (reader.IsEmptyElement) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(SR.MessageBodyMissing)); + reader.Read(); + return envelopeVersion; + } + + protected static void VerifyStartBody(XmlDictionaryReader reader, EnvelopeVersion version) + { + if (!reader.IsStartElement(XD.MessageDictionary.Body, version.DictionaryNamespace)) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(SR.MessageBodyMissing)); + } + } + + sealed class StreamedMessage : ReceivedMessage + { + MessageHeaders headers; + XmlAttributeHolder[] envelopeAttributes; + XmlAttributeHolder[] headerAttributes; + XmlAttributeHolder[] bodyAttributes; + string envelopePrefix; + string headerPrefix; + string bodyPrefix; + MessageProperties properties; + XmlDictionaryReader reader; + XmlDictionaryReaderQuotas quotas; + + public StreamedMessage(XmlDictionaryReader reader, int maxSizeOfHeaders, MessageVersion desiredVersion) + { + properties = new MessageProperties(); + if (reader.NodeType != XmlNodeType.Element) + reader.MoveToContent(); + + if (desiredVersion.Envelope == EnvelopeVersion.None) + { + this.reader = reader; + headerAttributes = XmlAttributeHolder.emptyArray; + headers = new MessageHeaders(desiredVersion); + } + else + { + envelopeAttributes = XmlAttributeHolder.ReadAttributes(reader, ref maxSizeOfHeaders); + envelopePrefix = reader.Prefix; + EnvelopeVersion envelopeVersion = ReadStartEnvelope(reader); + if (desiredVersion.Envelope != envelopeVersion) + { + Exception versionMismatchException = new ArgumentException(SR.Format(SR.EncoderEnvelopeVersionMismatch, envelopeVersion, desiredVersion.Envelope), nameof(reader)); + throw TraceUtility.ThrowHelperError( + new CommunicationException(versionMismatchException.Message, versionMismatchException), + this); + } + + if (HasHeaderElement(reader, envelopeVersion)) + { + headerPrefix = reader.Prefix; + headerAttributes = XmlAttributeHolder.ReadAttributes(reader, ref maxSizeOfHeaders); + headers = new MessageHeaders(desiredVersion, reader, envelopeAttributes, headerAttributes, ref maxSizeOfHeaders); + } + else + { + headerAttributes = XmlAttributeHolder.emptyArray; + headers = new MessageHeaders(desiredVersion); + } + + if (reader.NodeType != XmlNodeType.Element) + reader.MoveToContent(); + bodyPrefix = reader.Prefix; + VerifyStartBody(reader, envelopeVersion); + bodyAttributes = XmlAttributeHolder.ReadAttributes(reader, ref maxSizeOfHeaders); + if (ReadStartBody(reader)) + { + this.reader = reader; + } + else + { + quotas = new XmlDictionaryReaderQuotas(); + reader.Quotas.CopyTo(quotas); + reader.Dispose(); + } + } + } + + public override MessageHeaders Headers + { + get + { + if (IsDisposed) + throw TraceUtility.ThrowHelperError(CreateMessageDisposedException(), this); + return headers; + } + } + + public override MessageVersion Version + { + get + { + return headers.MessageVersion; + } + } + + public override MessageProperties Properties + { + get + { + return properties; + } + } + + protected override void OnBodyToString(XmlDictionaryWriter writer) + { + writer.WriteString(SR.MessageBodyIsStream); + } + + protected override void OnClose() + { + Exception ex = null; + try + { + base.OnClose(); + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + throw; + ex = e; + } + + try + { + properties.Dispose(); + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + throw; + if (ex == null) + ex = e; + } + + try + { + if (reader != null) + { + reader.Dispose(); + } + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + throw; + if (ex == null) + ex = e; + } + + if (ex != null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(ex); + } + + protected override XmlDictionaryReader OnGetReaderAtBodyContents() + { + XmlDictionaryReader reader = this.reader; + this.reader = null; + return reader; + } + + protected override MessageBuffer OnCreateBufferedCopy(int maxBufferSize) + { + if (reader != null) + return OnCreateBufferedCopy(maxBufferSize, reader.Quotas); + return OnCreateBufferedCopy(maxBufferSize, quotas); + } + + protected override void OnWriteStartBody(XmlDictionaryWriter writer) + { + writer.WriteStartElement(bodyPrefix, MessageStrings.Body, Version.Envelope.Namespace); + XmlAttributeHolder.WriteAttributes(bodyAttributes, writer); + } + + protected override void OnWriteStartEnvelope(XmlDictionaryWriter writer) + { + EnvelopeVersion envelopeVersion = Version.Envelope; + writer.WriteStartElement(envelopePrefix, MessageStrings.Envelope, envelopeVersion.Namespace); + XmlAttributeHolder.WriteAttributes(envelopeAttributes, writer); + } + + protected override void OnWriteStartHeaders(XmlDictionaryWriter writer) + { + EnvelopeVersion envelopeVersion = Version.Envelope; + writer.WriteStartElement(headerPrefix, MessageStrings.Header, envelopeVersion.Namespace); + XmlAttributeHolder.WriteAttributes(headerAttributes, writer); + } + + protected override string OnGetBodyAttribute(string localName, string ns) + { + return XmlAttributeHolder.GetAttribute(bodyAttributes, localName, ns); + } + } + + interface IBufferedMessageData + { + MessageEncoder MessageEncoder { get; } + ArraySegment Buffer { get; } + XmlDictionaryReaderQuotas Quotas { get; } + void Close(); + void EnableMultipleUsers(); + XmlDictionaryReader GetMessageReader(); + void Open(); + void ReturnMessageState(RecycledMessageState messageState); + RecycledMessageState TakeMessageState(); + } + + sealed class BufferedMessage : ReceivedMessage + { + MessageHeaders headers; + MessageProperties properties; + IBufferedMessageData messageData; + RecycledMessageState recycledMessageState; + XmlDictionaryReader reader; + XmlAttributeHolder[] bodyAttributes; + + public BufferedMessage(IBufferedMessageData messageData, RecycledMessageState recycledMessageState) + : this(messageData, recycledMessageState, null, false) + { + } + + public BufferedMessage(IBufferedMessageData messageData, RecycledMessageState recycledMessageState, bool[] understoodHeaders, bool understoodHeadersModified) + { + //bool throwing = true; + //try + //{ + this.recycledMessageState = recycledMessageState; + this.messageData = messageData; + properties = recycledMessageState.TakeProperties(); + if (properties == null) + properties = new MessageProperties(); + XmlDictionaryReader reader = messageData.GetMessageReader(); + MessageVersion desiredVersion = messageData.MessageEncoder.MessageVersion; + + if (desiredVersion.Envelope == EnvelopeVersion.None) + { + this.reader = reader; + headers = new MessageHeaders(desiredVersion); + } + else + { + EnvelopeVersion envelopeVersion = ReadStartEnvelope(reader); + if (desiredVersion.Envelope != envelopeVersion) + { + Exception versionMismatchException = new ArgumentException(SR.Format(SR.EncoderEnvelopeVersionMismatch, envelopeVersion, desiredVersion.Envelope), "reader"); + throw TraceUtility.ThrowHelperError( + new CommunicationException(versionMismatchException.Message, versionMismatchException), + this); + } + + if (HasHeaderElement(reader, envelopeVersion)) + { + headers = recycledMessageState.TakeHeaders(); + if (headers == null) + { + headers = new MessageHeaders(desiredVersion, reader, messageData, recycledMessageState, understoodHeaders, understoodHeadersModified); + } + else + { + headers.Init(desiredVersion, reader, messageData, recycledMessageState, understoodHeaders, understoodHeadersModified); + } + } + else + { + headers = new MessageHeaders(desiredVersion); + } + + VerifyStartBody(reader, envelopeVersion); + + int maxSizeOfAttributes = int.MaxValue; + bodyAttributes = XmlAttributeHolder.ReadAttributes(reader, ref maxSizeOfAttributes); + if (maxSizeOfAttributes < int.MaxValue - 4096) + bodyAttributes = null; + if (ReadStartBody(reader)) + { + this.reader = reader; + } + else + { + reader.Dispose(); + } + } + //throwing = false; + //} + //finally + //{ + // if (throwing && MessageLogger.LoggingEnabled) + // { + // MessageLogger.LogMessage(messageData.Buffer, MessageLoggingSource.Malformed); + // } + //} + } + + public override MessageHeaders Headers + { + get + { + if (IsDisposed) + throw TraceUtility.ThrowHelperError(CreateMessageDisposedException(), this); + return headers; + } + } + + internal IBufferedMessageData MessageData + { + get + { + return messageData; + } + } + + public override MessageProperties Properties + { + get + { + if (IsDisposed) + throw TraceUtility.ThrowHelperError(CreateMessageDisposedException(), this); + return properties; + } + } + + internal override RecycledMessageState RecycledMessageState + { + get { return recycledMessageState; } + } + + public override MessageVersion Version + { + get + { + return headers.MessageVersion; + } + } + + protected override XmlDictionaryReader OnGetReaderAtBodyContents() + { + XmlDictionaryReader reader = this.reader; + this.reader = null; + return reader; + } + + internal override XmlDictionaryReader GetReaderAtHeader() + { + if (!headers.ContainsOnlyBufferedMessageHeaders) + return base.GetReaderAtHeader(); + XmlDictionaryReader reader = messageData.GetMessageReader(); + if (reader.NodeType != XmlNodeType.Element) + reader.MoveToContent(); + reader.Read(); + if (HasHeaderElement(reader, headers.MessageVersion.Envelope)) + return reader; + return base.GetReaderAtHeader(); + } + + public XmlDictionaryReader GetBufferedReaderAtBody() + { + XmlDictionaryReader reader = messageData.GetMessageReader(); + if (reader.NodeType != XmlNodeType.Element) + reader.MoveToContent(); + if (Version.Envelope != EnvelopeVersion.None) + { + reader.Read(); + if (HasHeaderElement(reader, headers.MessageVersion.Envelope)) + reader.Skip(); + if (reader.NodeType != XmlNodeType.Element) + reader.MoveToContent(); + } + return reader; + } + + public XmlDictionaryReader GetMessageReader() + { + return messageData.GetMessageReader(); + } + + protected override void OnBodyToString(XmlDictionaryWriter writer) + { + using (XmlDictionaryReader reader = GetBufferedReaderAtBody()) + { + if (Version == MessageVersion.None) + { + writer.WriteNode(reader, false); + } + else + { + if (!reader.IsEmptyElement) + { + reader.ReadStartElement(); + while (reader.NodeType != XmlNodeType.EndElement) + writer.WriteNode(reader, false); + } + } + } + } + + protected override void OnClose() + { + Exception ex = null; + try + { + base.OnClose(); + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + throw; + ex = e; + } + + try + { + properties.Dispose(); + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + throw; + if (ex == null) + ex = e; + } + + try + { + if (reader != null) + { + reader.Dispose(); + } + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + throw; + if (ex == null) + ex = e; + } + + try + { + recycledMessageState.ReturnHeaders(headers); + recycledMessageState.ReturnProperties(properties); + messageData.ReturnMessageState(recycledMessageState); + recycledMessageState = null; + messageData.Close(); + messageData = null; + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + throw; + if (ex == null) + ex = e; + } + + if (ex != null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(ex); + } + + protected override void OnWriteStartEnvelope(XmlDictionaryWriter writer) + { + using (XmlDictionaryReader reader = GetMessageReader()) + { + reader.MoveToContent(); + EnvelopeVersion envelopeVersion = Version.Envelope; + writer.WriteStartElement(reader.Prefix, MessageStrings.Envelope, envelopeVersion.Namespace); + writer.WriteAttributes(reader, false); + } + } + + protected override void OnWriteStartHeaders(XmlDictionaryWriter writer) + { + using (XmlDictionaryReader reader = GetMessageReader()) + { + reader.MoveToContent(); + EnvelopeVersion envelopeVersion = Version.Envelope; + reader.Read(); + if (HasHeaderElement(reader, envelopeVersion)) + { + writer.WriteStartElement(reader.Prefix, MessageStrings.Header, envelopeVersion.Namespace); + writer.WriteAttributes(reader, false); + } + else + { + writer.WriteStartElement(MessageStrings.Prefix, MessageStrings.Header, envelopeVersion.Namespace); + } + } + } + + protected override void OnWriteStartBody(XmlDictionaryWriter writer) + { + using (XmlDictionaryReader reader = GetBufferedReaderAtBody()) + { + writer.WriteStartElement(reader.Prefix, MessageStrings.Body, Version.Envelope.Namespace); + writer.WriteAttributes(reader, false); + } + } + + protected override MessageBuffer OnCreateBufferedCopy(int maxBufferSize) + { + if (headers.ContainsOnlyBufferedMessageHeaders) + { + KeyValuePair[] properties = new KeyValuePair[Properties.Count]; + ((ICollection>)Properties).CopyTo(properties, 0); + messageData.EnableMultipleUsers(); + bool[] understoodHeaders = null; + if (headers.HasMustUnderstandBeenModified) + { + understoodHeaders = new bool[headers.Count]; + for (int i = 0; i < headers.Count; i++) + { + understoodHeaders[i] = headers.IsUnderstood(i); + } + } + return new BufferedMessageBuffer(messageData, properties, understoodHeaders, headers.HasMustUnderstandBeenModified); + } + else + { + if (reader != null) + return OnCreateBufferedCopy(maxBufferSize, reader.Quotas); + return OnCreateBufferedCopy(maxBufferSize, XmlDictionaryReaderQuotas.Max); + } + } + + protected override string OnGetBodyAttribute(string localName, string ns) + { + if (bodyAttributes != null) + return XmlAttributeHolder.GetAttribute(bodyAttributes, localName, ns); + using (XmlDictionaryReader reader = GetBufferedReaderAtBody()) + { + return reader.GetAttribute(localName, ns); + } + } + } + + struct XmlAttributeHolder + { + string prefix; + string ns; + string localName; + string value; + + public static XmlAttributeHolder[] emptyArray = new XmlAttributeHolder[0]; + + public XmlAttributeHolder(string prefix, string localName, string ns, string value) + { + this.prefix = prefix; + this.localName = localName; + this.ns = ns; + this.value = value; + } + + public string Prefix + { + get { return prefix; } + } + + public string NamespaceUri + { + get { return ns; } + } + + public string LocalName + { + get { return localName; } + } + + public string Value + { + get { return value; } + } + + public void WriteTo(XmlWriter writer) + { + writer.WriteStartAttribute(prefix, localName, ns); + writer.WriteString(value); + writer.WriteEndAttribute(); + } + + public static void WriteAttributes(XmlAttributeHolder[] attributes, XmlWriter writer) + { + for (int i = 0; i < attributes.Length; i++) + attributes[i].WriteTo(writer); + } + + public static XmlAttributeHolder[] ReadAttributes(XmlDictionaryReader reader) + { + int maxSizeOfHeaders = int.MaxValue; + return ReadAttributes(reader, ref maxSizeOfHeaders); + } + + public static XmlAttributeHolder[] ReadAttributes(XmlDictionaryReader reader, ref int maxSizeOfHeaders) + { + if (reader.AttributeCount == 0) + return emptyArray; + XmlAttributeHolder[] attributes = new XmlAttributeHolder[reader.AttributeCount]; + reader.MoveToFirstAttribute(); + for (int i = 0; i < attributes.Length; i++) + { + string ns = reader.NamespaceURI; + string localName = reader.LocalName; + string prefix = reader.Prefix; + string value = string.Empty; + while (reader.ReadAttributeValue()) + { + if (value.Length == 0) + value = reader.Value; + else + value += reader.Value; + } + Deduct(prefix, ref maxSizeOfHeaders); + Deduct(localName, ref maxSizeOfHeaders); + Deduct(ns, ref maxSizeOfHeaders); + Deduct(value, ref maxSizeOfHeaders); + attributes[i] = new XmlAttributeHolder(prefix, localName, ns, value); + reader.MoveToNextAttribute(); + } + reader.MoveToElement(); + return attributes; + } + + static void Deduct(string s, ref int maxSizeOfHeaders) + { + int byteCount = s.Length * sizeof(char); + if (byteCount > maxSizeOfHeaders) + { + string message = SR.XmlBufferQuotaExceeded; + Exception inner = new QuotaExceededException(message); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(message, inner)); + } + maxSizeOfHeaders -= byteCount; + } + + public static string GetAttribute(XmlAttributeHolder[] attributes, string localName, string ns) + { + for (int i = 0; i < attributes.Length; i++) + if (attributes[i].LocalName == localName && attributes[i].NamespaceUri == ns) + return attributes[i].Value; + return null; + } + } + + class RecycledMessageState + { + MessageHeaders recycledHeaders; + MessageProperties recycledProperties; + UriCache uriCache; + HeaderInfoCache headerInfoCache; + + public HeaderInfoCache HeaderInfoCache + { + get + { + if (headerInfoCache == null) + { + headerInfoCache = new HeaderInfoCache(); + } + return headerInfoCache; + } + } + + public UriCache UriCache + { + get + { + if (uriCache == null) + uriCache = new UriCache(); + return uriCache; + } + } + + public MessageProperties TakeProperties() + { + MessageProperties taken = recycledProperties; + recycledProperties = null; + return taken; + } + + public void ReturnProperties(MessageProperties properties) + { + if (properties.CanRecycle) + { + properties.Recycle(); + recycledProperties = properties; + } + } + + public MessageHeaders TakeHeaders() + { + MessageHeaders taken = recycledHeaders; + recycledHeaders = null; + return taken; + } + + public void ReturnHeaders(MessageHeaders headers) + { + if (headers.CanRecycle) + { + headers.Recycle(HeaderInfoCache); + recycledHeaders = headers; + } + } + } + + class HeaderInfoCache + { + const int maxHeaderInfos = 4; + HeaderInfo[] headerInfos; + int index; + + public MessageHeaderInfo TakeHeaderInfo(XmlDictionaryReader reader, string actor, bool mustUnderstand, bool relay, bool isRefParam) + { + if (headerInfos != null) + { + int i = index; + for (;;) + { + HeaderInfo headerInfo = headerInfos[i]; + if (headerInfo != null) + { + if (headerInfo.Matches(reader, actor, mustUnderstand, relay, isRefParam)) + { + headerInfos[i] = null; + index = (i + 1) % maxHeaderInfos; + return headerInfo; + } + } + i = (i + 1) % maxHeaderInfos; + if (i == index) + { + break; + } + } + } + + return new HeaderInfo(reader, actor, mustUnderstand, relay, isRefParam); + } + + public void ReturnHeaderInfo(MessageHeaderInfo headerInfo) + { + HeaderInfo headerInfoToReturn = headerInfo as HeaderInfo; + if (headerInfoToReturn != null) + { + if (headerInfos == null) + { + headerInfos = new HeaderInfo[maxHeaderInfos]; + } + int i = index; + for (;;) + { + if (headerInfos[i] == null) + { + break; + } + i = (i + 1) % maxHeaderInfos; + if (i == index) + { + break; + } + } + headerInfos[i] = headerInfoToReturn; + index = (i + 1) % maxHeaderInfos; + } + } + + class HeaderInfo : MessageHeaderInfo + { + string name; + string ns; + string actor; + bool isReferenceParameter; + bool mustUnderstand; + bool relay; + + public HeaderInfo(XmlDictionaryReader reader, string actor, bool mustUnderstand, bool relay, bool isReferenceParameter) + { + this.actor = actor; + this.mustUnderstand = mustUnderstand; + this.relay = relay; + this.isReferenceParameter = isReferenceParameter; + name = reader.LocalName; + ns = reader.NamespaceURI; + } + + public override string Name + { + get { return name; } + } + + public override string Namespace + { + get { return ns; } + } + + public override bool IsReferenceParameter + { + get { return isReferenceParameter; } + } + + public override string Actor + { + get { return actor; } + } + + public override bool MustUnderstand + { + get { return mustUnderstand; } + } + + public override bool Relay + { + get { return relay; } + } + + public bool Matches(XmlDictionaryReader reader, string actor, bool mustUnderstand, bool relay, bool isRefParam) + { + return reader.IsStartElement(name, ns) && + this.actor == actor && this.mustUnderstand == mustUnderstand && this.relay == relay && isReferenceParameter == isRefParam; + } + } + } + + class UriCache + { + const int MaxKeyLength = 128; + const int MaxEntries = 8; + Entry[] entries; + int count; + + public UriCache() + { + entries = new Entry[MaxEntries]; + } + + public Uri CreateUri(string uriString) + { + Uri uri = Get(uriString); + if (uri == null) + { + uri = new Uri(uriString); + Set(uriString, uri); + } + return uri; + } + + Uri Get(string key) + { + if (key.Length > MaxKeyLength) + return null; + for (int i = count - 1; i >= 0; i--) + if (entries[i].Key == key) + return entries[i].Value; + return null; + } + + void Set(string key, Uri value) + { + if (key.Length > MaxKeyLength) + return; + if (count < entries.Length) + { + entries[count++] = new Entry(key, value); + } + else + { + Array.Copy(entries, 1, entries, 0, entries.Length - 1); + entries[count - 1] = new Entry(key, value); + } + } + + struct Entry + { + string key; + Uri value; + + public Entry(string key, Uri value) + { + this.key = key; + this.value = value; + } + + public string Key + { + get { return key; } + } + + public Uri Value + { + get { return value; } + } + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/MessageBuffer.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/MessageBuffer.cs new file mode 100644 index 000000000..39ec8a66f --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/MessageBuffer.cs @@ -0,0 +1,336 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Xml; +using CoreWCF; +using CoreWCF.Channels; + +namespace CoreWCF.Channels +{ + public abstract class MessageBuffer : System.IDisposable + { + public abstract int BufferSize { get; } + + void IDisposable.Dispose() + { + Close(); + } + + public abstract void Close(); + + public virtual void WriteMessage(Stream stream) + { + if (stream == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("stream"); + Message message = CreateMessage(); + using (message) + { + XmlDictionaryWriter writer = XmlDictionaryWriter.CreateBinaryWriter(stream, XD.Dictionary, null, false); + using (writer) + { + message.WriteMessage(writer); + } + } + } + + public virtual string MessageContentType + { + get { return FramingEncodingString.Binary; } + } + + public abstract Message CreateMessage(); + + internal Exception CreateBufferDisposedException() + { + return new ObjectDisposedException("", SR.MessageBufferIsClosed); + } + + //public XPathNavigator CreateNavigator() + //{ + // return CreateNavigator(int.MaxValue, XmlSpace.None); + //} + + //public XPathNavigator CreateNavigator(int nodeQuota) + //{ + // return CreateNavigator(nodeQuota, XmlSpace.None); + //} + + //public XPathNavigator CreateNavigator(XmlSpace space) + //{ + // return CreateNavigator(int.MaxValue, space); + //} + + //public XPathNavigator CreateNavigator(int nodeQuota, XmlSpace space) + //{ + // if (nodeQuota <= 0) + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("nodeQuota", SR.Format(SR.FilterQuotaRange))); + + // return new SeekableMessageNavigator(this.CreateMessage(), nodeQuota, space, true, true); + //} + } + + + internal class DefaultMessageBuffer : MessageBuffer + { + private XmlBuffer msgBuffer; + private KeyValuePair[] properties; + private bool[] understoodHeaders; + private bool closed; + private MessageVersion version; + private Uri to; + private string action; + private bool isNullMessage; + + public DefaultMessageBuffer(Message message, XmlBuffer msgBuffer) + { + this.msgBuffer = msgBuffer; + version = message.Version; + isNullMessage = message is NullMessage; + + properties = new KeyValuePair[message.Properties.Count]; + ((ICollection>) message.Properties).CopyTo(properties, 0); + understoodHeaders = new bool[message.Headers.Count]; + for (int i = 0; i < understoodHeaders.Length; ++i) + understoodHeaders[i] = message.Headers.IsUnderstood(i); + + //CSDMain 17837: CreateBufferedCopy should have code to copy over the To and Action headers + if (version == MessageVersion.None) + { + to = message.Headers.To; + action = message.Headers.Action; + } + } + + private object ThisLock + { + get { return msgBuffer; } + } + + public override int BufferSize + { + get { return msgBuffer.BufferSize; } + } + + public override void Close() + { + lock (ThisLock) + { + if (closed) + return; + + closed = true; + for (int i = 0; i < properties.Length; i++) + { + IDisposable disposable = properties[i].Value as IDisposable; + if (disposable != null) + disposable.Dispose(); + } + } + } + + public override Message CreateMessage() + { + if (closed) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateBufferDisposedException()); + + Message msg; + if (isNullMessage) + { + msg = new NullMessage(); + } + else + { + msg = Message.CreateMessage(msgBuffer.GetReader(0), int.MaxValue, version); + } + + lock (ThisLock) + { + msg.Properties.CopyProperties(properties); + } + + for (int i = 0; i < understoodHeaders.Length; ++i) + { + if (understoodHeaders[i]) + msg.Headers.AddUnderstood(i); + } + + if (to != null) + { + msg.Headers.To = to; + } + + if (action != null) + { + msg.Headers.Action = action; + } + + return msg; + } + } + + class BufferedMessageBuffer : MessageBuffer + { + IBufferedMessageData messageData; + KeyValuePair[] properties; + bool closed; + object thisLock = new object(); + bool[] understoodHeaders; + bool understoodHeadersModified; + + public BufferedMessageBuffer(IBufferedMessageData messageData, + KeyValuePair[] properties, bool[] understoodHeaders, bool understoodHeadersModified) + { + this.messageData = messageData; + this.properties = properties; + this.understoodHeaders = understoodHeaders; + this.understoodHeadersModified = understoodHeadersModified; + messageData.Open(); + } + + public override int BufferSize + { + get + { + lock (ThisLock) + { + if (closed) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateBufferDisposedException()); + return messageData.Buffer.Count; + } + } + } + + public override void WriteMessage(Stream stream) + { + if (stream == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(stream)); + lock (ThisLock) + { + if (closed) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateBufferDisposedException()); + ArraySegment buffer = messageData.Buffer; + stream.Write(buffer.Array, buffer.Offset, buffer.Count); + } + } + + public override string MessageContentType + { + get + { + lock (ThisLock) + { + if (closed) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateBufferDisposedException()); + return messageData.MessageEncoder.ContentType; + } + } + } + + object ThisLock + { + get { return thisLock; } + } + + public override void Close() + { + lock (ThisLock) + { + if (!closed) + { + closed = true; + messageData.Close(); + messageData = null; + } + } + } + + public override Message CreateMessage() + { + lock (ThisLock) + { + if (closed) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateBufferDisposedException()); + RecycledMessageState recycledMessageState = messageData.TakeMessageState(); + if (recycledMessageState == null) + recycledMessageState = new RecycledMessageState(); + BufferedMessage bufferedMessage = new BufferedMessage(messageData, recycledMessageState, understoodHeaders, understoodHeadersModified); + bufferedMessage.Properties.CopyProperties(properties); + messageData.Open(); + return bufferedMessage; + } + } + } + + class BodyWriterMessageBuffer : MessageBuffer + { + BodyWriter bodyWriter; + KeyValuePair[] properties; + MessageHeaders headers; + bool closed; + object thisLock = new object(); + + public BodyWriterMessageBuffer(MessageHeaders headers, + KeyValuePair[] properties, BodyWriter bodyWriter) + { + this.bodyWriter = bodyWriter; + this.headers = new MessageHeaders(headers); + this.properties = properties; + } + + protected object ThisLock + { + get { return thisLock; } + } + + public override int BufferSize + { + get { return 0; } + } + + public override void Close() + { + lock (ThisLock) + { + if (!closed) + { + closed = true; + bodyWriter = null; + headers = null; + properties = null; + } + } + } + + public override Message CreateMessage() + { + lock (ThisLock) + { + if (closed) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateBufferDisposedException()); + return new BodyWriterMessage(headers, properties, bodyWriter); + } + } + + protected BodyWriter BodyWriter + { + get { return bodyWriter; } + } + + protected MessageHeaders Headers + { + get { return headers; } + } + + protected KeyValuePair[] Properties + { + get { return properties; } + } + + protected bool Closed + { + get { return closed; } + } + } + +} + diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/MessageEncoder.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/MessageEncoder.cs new file mode 100644 index 000000000..dbe5c6a93 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/MessageEncoder.cs @@ -0,0 +1,204 @@ +using System; +using System.IO; +using System.Net.Http.Headers; +using System.Threading.Tasks; +using CoreWCF.Diagnostics; + +namespace CoreWCF.Channels +{ + public abstract class MessageEncoder + { + public abstract string ContentType { get; } + + public abstract string MediaType { get; } + + public abstract MessageVersion MessageVersion { get; } + + public virtual T GetProperty() where T : class + { + if (typeof (T) == typeof (FaultConverter)) + { + return (T) (object) FaultConverter.GetDefaultFaultConverter(MessageVersion); + } + + return null; + } + + public Message ReadMessage(Stream stream, int maxSizeOfHeaders) + { + return ReadMessage(stream, maxSizeOfHeaders, null); + } + + public abstract Message ReadMessage(Stream stream, int maxSizeOfHeaders, string contentType); + + public Message ReadMessage(ArraySegment buffer, BufferManager bufferManager) + { + Message message = ReadMessage(buffer, bufferManager, null); + return message; + } + + public abstract Message ReadMessage(ArraySegment buffer, BufferManager bufferManager, string contentType); + + // used for buffered streaming + internal ArraySegment BufferMessageStream(Stream stream, BufferManager bufferManager, int maxBufferSize) + { + byte[] buffer = bufferManager.TakeBuffer(ConnectionOrientedTransportDefaults.ConnectionBufferSize); + int offset = 0; + int currentBufferSize = Math.Min(buffer.Length, maxBufferSize); + + while (offset < currentBufferSize) + { + int count = stream.Read(buffer, offset, currentBufferSize - offset); + if (count == 0) + { + stream.Dispose(); + break; + } + + offset += count; + if (offset == currentBufferSize) + { + if (currentBufferSize >= maxBufferSize) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + MaxMessageSizeStream.CreateMaxReceivedMessageSizeExceededException(maxBufferSize)); + } + + currentBufferSize = Math.Min(currentBufferSize*2, maxBufferSize); + byte[] temp = bufferManager.TakeBuffer(currentBufferSize); + Buffer.BlockCopy(buffer, 0, temp, 0, offset); + bufferManager.ReturnBuffer(buffer); + buffer = temp; + } + } + + return new ArraySegment(buffer, 0, offset); + } + + // used for buffered streaming + internal virtual Message ReadMessage(Stream stream, BufferManager bufferManager, int maxBufferSize, + string contentType) + { + return ReadMessage(BufferMessageStream(stream, bufferManager, maxBufferSize), bufferManager, contentType); + } + + public override string ToString() + { + return ContentType; + } + + public abstract void WriteMessage(Message message, Stream stream); + + // TODO: Work out what to do about the async message writing, add to contract + public virtual Task WriteMessageAsync(Message message, Stream stream) + { + return Task.Run(() => WriteMessage(message, stream)); + } + + public ArraySegment WriteMessage(Message message, int maxMessageSize, BufferManager bufferManager) + { + ArraySegment arraySegment = WriteMessage(message, maxMessageSize, bufferManager, 0); + return arraySegment; + } + + public abstract ArraySegment WriteMessage(Message message, int maxMessageSize, + BufferManager bufferManager, int messageOffset); + + public virtual bool IsContentTypeSupported(string contentType) + { + if (contentType == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("contentType")); + + return IsContentTypeSupported(contentType, ContentType, MediaType); + } + + internal bool IsContentTypeSupported(string contentType, string supportedContentType, string supportedMediaType) + { + if (supportedContentType == contentType) + return true; + + if (contentType.Length > supportedContentType.Length && + contentType.StartsWith(supportedContentType, StringComparison.Ordinal) && + contentType[supportedContentType.Length] == ';') + return true; + + // now check case-insensitively + if (contentType.StartsWith(supportedContentType, StringComparison.OrdinalIgnoreCase)) + { + if (contentType.Length == supportedContentType.Length) + { + return true; + } + else if (contentType.Length > supportedContentType.Length) + { + char ch = contentType[supportedContentType.Length]; + + // Linear Whitespace is allowed to appear between the end of one property and the semicolon. + // LWS = [CRLF]? (SP | HT)+ + if (ch == ';') + { + return true; + } + + // Consume the [CRLF]? + int i = supportedContentType.Length; + if (ch == '\r' && contentType.Length > supportedContentType.Length + 1 && contentType[i + 1] == '\n') + { + i += 2; + ch = contentType[i]; + } + + // Look for a ';' or nothing after (SP | HT)+ + if (ch == ' ' || ch == '\t') + { + i++; + while (i < contentType.Length) + { + ch = contentType[i]; + if (ch != ' ' && ch != '\t') + break; + ++i; + } + } + if (ch == ';' || i == contentType.Length) + return true; + } + } + + // sometimes we get a contentType that has parameters, but our encoders + // merely expose the base content-type, so we will check a stripped version + try + { + MediaTypeHeaderValue parsedContentType = MediaTypeHeaderValue.Parse(contentType); + + if (supportedMediaType.Length > 0 && !supportedMediaType.Equals(parsedContentType.MediaType, StringComparison.OrdinalIgnoreCase)) + return false; + + if (!IsCharSetSupported(parsedContentType.CharSet)) + return false; + } + catch (FormatException) + { + // bad content type, so we definitely don't support it! + return false; + } + + return true; + } + + internal virtual bool IsCharSetSupported(string charset) + { + return false; + } + + internal void ThrowIfMismatchedMessageVersion(Message message) + { + if (message.Version != MessageVersion) + { + throw TraceUtility.ThrowHelperError( + new ProtocolException(SR.Format(SR.EncoderMessageVersionMismatch, message.Version, MessageVersion)), + message); + } + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/MessageEncoderCompressionHandler.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/MessageEncoderCompressionHandler.cs new file mode 100644 index 000000000..54226fa65 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/MessageEncoderCompressionHandler.cs @@ -0,0 +1,89 @@ +using System; +using System.IO; +using System.IO.Compression; + +namespace CoreWCF.Channels +{ + internal static class MessageEncoderCompressionHandler + { + internal const string GZipContentEncoding = "gzip"; + internal const string DeflateContentEncoding = "deflate"; + private const int DecompressBlockSize = 1024; + + internal static void DecompressBuffer(ref ArraySegment buffer, BufferManager bufferManager, CompressionFormat compressionFormat, long maxReceivedMessageSize) + { + MemoryStream memoryStream = new MemoryStream(buffer.Array, buffer.Offset, buffer.Count); + int maxDecompressedSize = (int)Math.Min(maxReceivedMessageSize, int.MaxValue); + + using (BufferManagerOutputStream bufferedOutStream = new BufferManagerOutputStream(SR.MaxReceivedMessageSizeExceeded, 1024, maxDecompressedSize, bufferManager)) + { + bufferedOutStream.Write(buffer.Array, 0, buffer.Offset); + + byte[] tempBuffer = bufferManager.TakeBuffer(DecompressBlockSize); + try + { + using (Stream ds = compressionFormat == CompressionFormat.GZip ? + (Stream)new GZipStream(memoryStream, CompressionMode.Decompress) : + (Stream)new DeflateStream(memoryStream, CompressionMode.Decompress)) + { + while (true) + { + int bytesRead = ds.Read(tempBuffer, 0, DecompressBlockSize); + if (bytesRead > 0) + { + bufferedOutStream.Write(tempBuffer, 0, bytesRead); + } + else + { + break; + } + } + } + } + finally + { + bufferManager.ReturnBuffer(tempBuffer); + } + + int length = 0; + byte[] decompressedBytes = bufferedOutStream.ToArray(out length); + bufferManager.ReturnBuffer(buffer.Array); + buffer = new ArraySegment(decompressedBytes, buffer.Offset, length - buffer.Offset); + } + } + + internal static void CompressBuffer(ref ArraySegment buffer, BufferManager bufferManager, CompressionFormat compressionFormat) + { + using (BufferManagerOutputStream bufferedOutStream = new BufferManagerOutputStream(SR.MaxSentMessageSizeExceeded, 1024, int.MaxValue, bufferManager)) + { + bufferedOutStream.Write(buffer.Array, 0, buffer.Offset); + + using (Stream ds = compressionFormat == CompressionFormat.GZip ? + (Stream)new GZipStream(bufferedOutStream, CompressionMode.Compress, true) : + (Stream)new DeflateStream(bufferedOutStream, CompressionMode.Compress, true)) + { + ds.Write(buffer.Array, buffer.Offset, buffer.Count); + } + + int length = 0; + byte[] compressedBytes = bufferedOutStream.ToArray(out length); + bufferManager.ReturnBuffer(buffer.Array); + buffer = new ArraySegment(compressedBytes, buffer.Offset, length - buffer.Offset); + } + } + + internal static Stream GetDecompressStream(Stream compressedStream, CompressionFormat compressionFormat) + { + return compressionFormat == CompressionFormat.GZip ? + (Stream)new GZipStream(compressedStream, CompressionMode.Decompress, false) : + (Stream)new DeflateStream(compressedStream, CompressionMode.Decompress, false); + } + + internal static Stream GetCompressStream(Stream uncompressedStream, CompressionFormat compressionFormat) + { + return compressionFormat == CompressionFormat.GZip ? + (Stream)new GZipStream(uncompressedStream, CompressionMode.Compress, true) : + (Stream)new DeflateStream(uncompressedStream, CompressionMode.Compress, true); + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/MessageEncoderFactory.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/MessageEncoderFactory.cs new file mode 100644 index 000000000..256adcec1 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/MessageEncoderFactory.cs @@ -0,0 +1,24 @@ +namespace CoreWCF.Channels +{ + public abstract class MessageEncoderFactory + { + protected MessageEncoderFactory() + { + } + + public abstract MessageEncoder Encoder + { + get; + } + + public abstract MessageVersion MessageVersion + { + get; + } + + public virtual MessageEncoder CreateSessionEncoder() + { + return Encoder; + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/MessageEncodingBindingElement.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/MessageEncodingBindingElement.cs new file mode 100644 index 000000000..1de437794 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/MessageEncodingBindingElement.cs @@ -0,0 +1,99 @@ +using System; + +namespace CoreWCF.Channels +{ + public abstract class MessageEncodingBindingElement : BindingElement + { + protected MessageEncodingBindingElement() + { + } + + protected MessageEncodingBindingElement(MessageEncodingBindingElement elementToBeCloned) + : base(elementToBeCloned) + { + } + + public abstract MessageVersion MessageVersion { get; set; } + + // internal IChannelFactory InternalBuildChannelFactory(BindingContext context) + // { + // if (context == null) + // { + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("context")); + // } + + //#pragma warning suppress 56506 // brianmcn, BindingContext.BindingParameters never be null + // context.BindingParameters.Add(this); + // return context.BuildInnerChannelFactory(); + // } + + // internal bool InternalCanBuildChannelFactory(BindingContext context) + // { + // if (context == null) + // { + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("context")); + // } + + //#pragma warning suppress 56506 // brianmcn, BindingContext.BindingParameters never be null + // context.BindingParameters.Add(this); + // return context.CanBuildInnerChannelFactory(); + // } + + internal IChannelListener InternalBuildChannelListener(BindingContext context) + where TChannel : class, IChannel + { + if (context == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(context)); + } + + context.BindingParameters.Add(this); + return context.BuildInnerChannelListener(); + } + + internal bool InternalCanBuildChannelListener(BindingContext context) + where TChannel : class, IChannel + { + if (context == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(context)); + } + + context.BindingParameters.Add(this); + return context.CanBuildInnerChannelListener(); + } + + public abstract MessageEncoderFactory CreateMessageEncoderFactory(); + + public override T GetProperty(BindingContext context) + { + if (context == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("context"); + } + if (typeof(T) == typeof(MessageVersion)) + { + return (T)(object)MessageVersion; + } + else + { + return context.GetInnerProperty(); + } + } + + internal virtual bool CheckEncodingVersion(EnvelopeVersion version) + { + return false; + } + + protected override bool IsMatch(BindingElement b) + { + MessageEncodingBindingElement encoding = b as MessageEncodingBindingElement; + if (encoding == null) + return false; + + return true; + } + + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/MessageFault.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/MessageFault.cs new file mode 100644 index 000000000..7ae529c74 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/MessageFault.cs @@ -0,0 +1,650 @@ +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; +using System.Xml; +using CoreWCF.Diagnostics; +using CoreWCF.Dispatcher; + +namespace CoreWCF.Channels +{ + public abstract class MessageFault + { + internal static MessageFault CreateFault(FaultCode code, string reason) + { + return CreateFault(code, new FaultReason(reason)); + } + + internal static MessageFault CreateFault(FaultCode code, FaultReason reason) + { + return CreateFault(code, reason, null, null, "", ""); + } + + internal static MessageFault CreateFault(FaultCode code, FaultReason reason, object detail) + { + return CreateFault(code, reason, detail, DataContractSerializerDefaults.CreateSerializer( + (detail == null ? typeof(object) : detail.GetType()), int.MaxValue/*maxItems*/), "", ""); + } + + public static MessageFault CreateFault(FaultCode code, FaultReason reason, object detail, XmlObjectSerializer serializer, string actor, string node) + { + if (code == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(code)); + if (reason == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(reason)); + if (actor == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(actor)); + if (node == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(node)); + return new XmlObjectSerializerFault(code, reason, detail, serializer, actor, node); + } + + public static MessageFault CreateFault(Message message, int maxBufferSize) + { + if (message == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(message)); + XmlDictionaryReader reader = message.GetReaderAtBodyContents(); + using (reader) + { + try + { + EnvelopeVersion envelopeVersion = message.Version.Envelope; + MessageFault fault; + if (envelopeVersion == EnvelopeVersion.Soap12) + { + fault = ReceivedFault.CreateFault12(reader, maxBufferSize); + } + else if (envelopeVersion == EnvelopeVersion.Soap11) + { + fault = ReceivedFault.CreateFault11(reader, maxBufferSize); + } + else if (envelopeVersion == EnvelopeVersion.None) + { + fault = ReceivedFault.CreateFaultNone(reader, maxBufferSize); + } + else + { + throw TraceUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.EnvelopeVersionUnknown, envelopeVersion.ToString())), message); + } + message.ReadFromBodyContentsToEnd(reader); + return fault; + } + catch (InvalidOperationException e) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException( + SR.SFxErrorDeserializingFault, e)); + } + catch (FormatException e) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException( + SR.SFxErrorDeserializingFault, e)); + } + catch (XmlException e) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException( + SR.SFxErrorDeserializingFault, e)); + } + } + } + + public virtual string Actor + { + get + { + return ""; + } + } + + public abstract FaultCode Code { get; } + public virtual string Node + { + get + { + return ""; + } + } + + public abstract bool HasDetail { get; } + + public abstract FaultReason Reason { get; } + + public T GetDetail() + { + return GetDetail(DataContractSerializerDefaults.CreateSerializer(typeof(T), int.MaxValue/*maxItems*/)); + } + + public T GetDetail(XmlObjectSerializer serializer) + { + if (serializer == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(serializer)); + XmlDictionaryReader reader = GetReaderAtDetailContents(); + T value = (T)serializer.ReadObject(reader); + if (!reader.EOF) + { + reader.MoveToContent(); + if (reader.NodeType != XmlNodeType.EndElement && !reader.EOF) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.ExtraContentIsPresentInFaultDetail)); + } + return value; + } + + public XmlDictionaryReader GetReaderAtDetailContents() + { + if (!HasDetail) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.FaultDoesNotHaveAnyDetail)); + return OnGetReaderAtDetailContents(); + } + + protected virtual void OnWriteDetail(XmlDictionaryWriter writer, EnvelopeVersion version) + { + OnWriteStartDetail(writer, version); + OnWriteDetailContents(writer); + writer.WriteEndElement(); + } + + protected virtual void OnWriteStartDetail(XmlDictionaryWriter writer, EnvelopeVersion version) + { + if (version == EnvelopeVersion.Soap12) + writer.WriteStartElement(XD.Message12Dictionary.FaultDetail, XD.Message12Dictionary.Namespace); + else if (version == EnvelopeVersion.Soap11) + writer.WriteStartElement(XD.Message11Dictionary.FaultDetail, XD.Message11Dictionary.FaultNamespace); + else + writer.WriteStartElement(XD.Message12Dictionary.FaultDetail, XD.MessageDictionary.Namespace); + } + + protected abstract void OnWriteDetailContents(XmlDictionaryWriter writer); + + protected virtual XmlDictionaryReader OnGetReaderAtDetailContents() + { + XmlBuffer detailBuffer = new XmlBuffer(int.MaxValue); + XmlDictionaryWriter writer = detailBuffer.OpenSection(XmlDictionaryReaderQuotas.Max); + OnWriteDetail(writer, EnvelopeVersion.Soap12); // Wrap in soap 1.2 by default + detailBuffer.CloseSection(); + detailBuffer.Close(); + XmlDictionaryReader reader = detailBuffer.GetReader(0); + reader.Read(); // Skip the detail element + return reader; + } + + internal void WriteTo(XmlWriter writer, EnvelopeVersion version) + { + WriteTo(XmlDictionaryWriter.CreateDictionaryWriter(writer), version); + } + + internal void WriteTo(XmlDictionaryWriter writer, EnvelopeVersion version) + { + if (writer == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer"); + } + + if (version == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("version"); + } + + if (version == EnvelopeVersion.Soap12) + { + WriteTo12(writer); + } + else if (version == EnvelopeVersion.Soap11) + { + WriteTo11(writer); + } + else if (version == EnvelopeVersion.None) + { + WriteToNone(writer); + } + else + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.EnvelopeVersionUnknown, version.ToString()))); + } + } + + void WriteToNone(XmlDictionaryWriter writer) + { + WriteTo12Driver(writer, EnvelopeVersion.None); + } + + void WriteTo12Driver(XmlDictionaryWriter writer, EnvelopeVersion version) + { + writer.WriteStartElement(XD.MessageDictionary.Fault, version.DictionaryNamespace); + writer.WriteStartElement(XD.Message12Dictionary.FaultCode, version.DictionaryNamespace); + WriteFaultCode12Driver(writer, Code, version); + writer.WriteEndElement(); + writer.WriteStartElement(XD.Message12Dictionary.FaultReason, version.DictionaryNamespace); + FaultReason reason = Reason; + for (int i = 0; i < reason.Translations.Count; i++) + { + FaultReasonText text = reason.Translations[i]; + writer.WriteStartElement(XD.Message12Dictionary.FaultText, version.DictionaryNamespace); + writer.WriteAttributeString("xml", "lang", XmlUtil.XmlNs, text.XmlLang); + writer.WriteString(text.Text); + writer.WriteEndElement(); + } + writer.WriteEndElement(); + if (Node.Length > 0) + writer.WriteElementString(XD.Message12Dictionary.FaultNode, version.DictionaryNamespace, Node); + if (Actor.Length > 0) + writer.WriteElementString(XD.Message12Dictionary.FaultRole, version.DictionaryNamespace, Actor); + if (HasDetail) + { + OnWriteDetail(writer, version); + } + writer.WriteEndElement(); + } + + void WriteFaultCode12Driver(XmlDictionaryWriter writer, FaultCode faultCode, EnvelopeVersion version) + { + writer.WriteStartElement(XD.Message12Dictionary.FaultValue, version.DictionaryNamespace); + string name; + if (faultCode.IsSenderFault) + name = version.SenderFaultName; + else if (faultCode.IsReceiverFault) + name = version.ReceiverFaultName; + else + name = faultCode.Name; + string ns; + if (faultCode.IsPredefinedFault) + ns = version.Namespace; + else + ns = faultCode.Namespace; + string prefix = writer.LookupPrefix(ns); + if (prefix == null) + writer.WriteAttributeString("xmlns", "a", XmlUtil.XmlNsNs, ns); + writer.WriteQualifiedName(name, ns); + writer.WriteEndElement(); + + if (faultCode.SubCode != null) + { + writer.WriteStartElement(XD.Message12Dictionary.FaultSubcode, version.DictionaryNamespace); + WriteFaultCode12Driver(writer, faultCode.SubCode, version); + writer.WriteEndElement(); + } + } + + void WriteTo12(XmlDictionaryWriter writer) + { + WriteTo12Driver(writer, EnvelopeVersion.Soap12); + } + + void WriteTo11(XmlDictionaryWriter writer) + { + writer.WriteStartElement(XD.MessageDictionary.Fault, XD.Message11Dictionary.Namespace); + writer.WriteStartElement(XD.Message11Dictionary.FaultCode, XD.Message11Dictionary.FaultNamespace); + + FaultCode faultCode = Code; + if (faultCode.SubCode != null) + faultCode = faultCode.SubCode; + + string name; + if (faultCode.IsSenderFault) + name = "Client"; + else if (faultCode.IsReceiverFault) + name = "Server"; + else + name = faultCode.Name; + string ns; + if (faultCode.IsPredefinedFault) + ns = Message11Strings.Namespace; + else + ns = faultCode.Namespace; + string prefix = writer.LookupPrefix(ns); + if (prefix == null) + writer.WriteAttributeString("xmlns", "a", XmlUtil.XmlNsNs, ns); + writer.WriteQualifiedName(name, ns); + writer.WriteEndElement(); + FaultReasonText translation = Reason.Translations[0]; + writer.WriteStartElement(XD.Message11Dictionary.FaultString, XD.Message11Dictionary.FaultNamespace); + if (translation.XmlLang.Length > 0) + writer.WriteAttributeString("xml", "lang", XmlUtil.XmlNs, translation.XmlLang); + writer.WriteString(translation.Text); + writer.WriteEndElement(); + if (Actor.Length > 0) + writer.WriteElementString(XD.Message11Dictionary.FaultActor, XD.Message11Dictionary.FaultNamespace, Actor); + if (HasDetail) + { + OnWriteDetail(writer, EnvelopeVersion.Soap11); + } + writer.WriteEndElement(); + } + + } + + class XmlObjectSerializerFault : MessageFault + { + FaultCode code; + FaultReason reason; + string actor; + string node; + object detail; + XmlObjectSerializer serializer; + + public XmlObjectSerializerFault(FaultCode code, FaultReason reason, object detail, XmlObjectSerializer serializer, string actor, string node) + { + this.code = code; + this.reason = reason; + this.detail = detail; + this.serializer = serializer; + this.actor = actor; + this.node = node; + } + + public override string Actor + { + get + { + return actor; + } + } + + public override FaultCode Code + { + get + { + return code; + } + } + + public override bool HasDetail + { + get + { + return serializer != null; + } + } + + public override string Node + { + get + { + return node; + } + } + + public override FaultReason Reason + { + get + { + return reason; + } + } + + object ThisLock + { + get + { + return code; + } + } + + protected override void OnWriteDetailContents(XmlDictionaryWriter writer) + { + if (serializer != null) + { + lock (ThisLock) + { + serializer.WriteObject(writer, detail); + } + } + } + } + + class ReceivedFault : MessageFault + { + FaultCode code; + FaultReason reason; + string actor; + string node; + XmlBuffer detail; + bool hasDetail; + EnvelopeVersion receivedVersion; + + ReceivedFault(FaultCode code, FaultReason reason, string actor, string node, XmlBuffer detail, EnvelopeVersion version) + { + this.code = code; + this.reason = reason; + this.actor = actor; + this.node = node; + receivedVersion = version; + hasDetail = InferHasDetail(detail); + this.detail = hasDetail ? detail : null; + } + + public override string Actor + { + get + { + return actor; + } + } + + public override FaultCode Code + { + get + { + return code; + } + } + + public override bool HasDetail + { + get + { + return hasDetail; + } + } + + public override string Node + { + get + { + return node; + } + } + + public override FaultReason Reason + { + get + { + return reason; + } + } + + bool InferHasDetail(XmlBuffer detail) + { + bool hasDetail = false; + if (detail != null) + { + XmlDictionaryReader reader = detail.GetReader(0); + if (!reader.IsEmptyElement && reader.Read()) // check if the detail element contains data + hasDetail = (reader.MoveToContent() != XmlNodeType.EndElement); + reader.Dispose(); + } + return hasDetail; + } + + protected override void OnWriteDetail(XmlDictionaryWriter writer, EnvelopeVersion version) + { + using (XmlReader r = detail.GetReader(0)) + { + // Start the element + base.OnWriteStartDetail(writer, version); + + // Copy the attributes + while (r.MoveToNextAttribute()) + { + if (ShouldWriteDetailAttribute(version, r.Prefix, r.LocalName, r.Value)) + { + writer.WriteAttributeString(r.Prefix, r.LocalName, r.NamespaceURI, r.Value); + } + } + r.MoveToElement(); + + r.Read(); + + // Copy the contents + while (r.NodeType != XmlNodeType.EndElement) + writer.WriteNode(r, false); + + // End the element + writer.WriteEndElement(); + } + } + + protected override void OnWriteStartDetail(XmlDictionaryWriter writer, EnvelopeVersion version) + { + using (XmlReader r = detail.GetReader(0)) + { + // Start the element + base.OnWriteStartDetail(writer, version); + + // Copy the attributes + while (r.MoveToNextAttribute()) + { + if (ShouldWriteDetailAttribute(version, r.Prefix, r.LocalName, r.Value)) + { + writer.WriteAttributeString(r.Prefix, r.LocalName, r.NamespaceURI, r.Value); + } + } + } + } + + protected override void OnWriteDetailContents(XmlDictionaryWriter writer) + { + using (XmlReader r = detail.GetReader(0)) + { + r.Read(); + while (r.NodeType != XmlNodeType.EndElement) + writer.WriteNode(r, false); + } + } + + protected override XmlDictionaryReader OnGetReaderAtDetailContents() + { + XmlDictionaryReader reader = detail.GetReader(0); + reader.Read(); // Skip the detail element + return reader; + } + + bool ShouldWriteDetailAttribute(EnvelopeVersion targetVersion, string prefix, string localName, string attributeValue) + { + // Handle fault detail version conversion from Soap12 to Soap11 -- scope tightly to only conversion from Soap12 -> Soap11 + // SOAP 1.1 specifications allow an arbitrary element within , hence: + // transform this IFF the SOAP namespace specified will affect the namespace of the element, + // AND the namespace specified is exactly the Soap12 Namespace. + bool shouldSkip = receivedVersion == EnvelopeVersion.Soap12 // original incoming version + && targetVersion == EnvelopeVersion.Soap11 // version to serialize to + && string.IsNullOrEmpty(prefix) // attribute prefix + && localName == "xmlns" // only transform namespace attributes, don't care about others + && attributeValue == XD.Message12Dictionary.Namespace.Value; + + return !shouldSkip; + } + + public static ReceivedFault CreateFaultNone(XmlDictionaryReader reader, int maxBufferSize) + { + return CreateFault12Driver(reader, maxBufferSize, EnvelopeVersion.None); + } + + static ReceivedFault CreateFault12Driver(XmlDictionaryReader reader, int maxBufferSize, EnvelopeVersion version) + { + reader.ReadStartElement(XD.MessageDictionary.Fault, version.DictionaryNamespace); + reader.ReadStartElement(XD.Message12Dictionary.FaultCode, version.DictionaryNamespace); + FaultCode code = ReadFaultCode12Driver(reader, version); + reader.ReadEndElement(); + List translations = new List(); + if (reader.IsEmptyElement) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.AtLeastOneFaultReasonMustBeSpecified)); + } + else + { + reader.ReadStartElement(XD.Message12Dictionary.FaultReason, version.DictionaryNamespace); + while (reader.IsStartElement(XD.Message12Dictionary.FaultText, version.DictionaryNamespace)) + translations.Add(ReadTranslation12(reader)); + reader.ReadEndElement(); + } + + string actor = ""; + string node = ""; + if (reader.IsStartElement(XD.Message12Dictionary.FaultNode, version.DictionaryNamespace)) + node = reader.ReadElementContentAsString(); + if (reader.IsStartElement(XD.Message12Dictionary.FaultRole, version.DictionaryNamespace)) + actor = reader.ReadElementContentAsString(); + XmlBuffer detail = null; + if (reader.IsStartElement(XD.Message12Dictionary.FaultDetail, version.DictionaryNamespace)) + { + detail = new XmlBuffer(maxBufferSize); + XmlDictionaryWriter writer = detail.OpenSection(reader.Quotas); + writer.WriteNode(reader, false); + detail.CloseSection(); + detail.Close(); + } + reader.ReadEndElement(); + FaultReason reason = new FaultReason(translations); + return new ReceivedFault(code, reason, actor, node, detail, version); + } + + static FaultCode ReadFaultCode12Driver(XmlDictionaryReader reader, EnvelopeVersion version) + { + string localName; + string ns; + FaultCode subCode = null; + reader.ReadStartElement(XD.Message12Dictionary.FaultValue, version.DictionaryNamespace); + XmlUtil.ReadContentAsQName(reader, out localName, out ns); + reader.ReadEndElement(); + if (reader.IsStartElement(XD.Message12Dictionary.FaultSubcode, version.DictionaryNamespace)) + { + reader.ReadStartElement(); + subCode = ReadFaultCode12Driver(reader, version); + reader.ReadEndElement(); + return new FaultCode(localName, ns, subCode); + } + return new FaultCode(localName, ns); + } + + public static ReceivedFault CreateFault12(XmlDictionaryReader reader, int maxBufferSize) + { + return CreateFault12Driver(reader, maxBufferSize, EnvelopeVersion.Soap12); + } + + static FaultReasonText ReadTranslation12(XmlDictionaryReader reader) + { + string xmlLang = XmlUtil.GetXmlLangAttribute(reader); + string text = reader.ReadElementContentAsString(); + return new FaultReasonText(text, xmlLang); + } + + public static ReceivedFault CreateFault11(XmlDictionaryReader reader, int maxBufferSize) + { + reader.ReadStartElement(XD.MessageDictionary.Fault, XD.Message11Dictionary.Namespace); + string ns; + string name; + reader.ReadStartElement(XD.Message11Dictionary.FaultCode, XD.Message11Dictionary.FaultNamespace); + XmlUtil.ReadContentAsQName(reader, out name, out ns); + FaultCode code = new FaultCode(name, ns); + reader.ReadEndElement(); + + string xmlLang = reader.XmlLang; + reader.MoveToContent(); // Don't do IsStartElement. FaultString is required, so let the reader throw. + string text = reader.ReadElementContentAsString(XD.Message11Dictionary.FaultString.Value, XD.Message11Dictionary.FaultNamespace.Value); + FaultReasonText translation = new FaultReasonText(text, xmlLang); + + string actor = ""; + if (reader.IsStartElement(XD.Message11Dictionary.FaultActor, XD.Message11Dictionary.FaultNamespace)) + actor = reader.ReadElementContentAsString(); + XmlBuffer detail = null; + if (reader.IsStartElement(XD.Message11Dictionary.FaultDetail, XD.Message11Dictionary.FaultNamespace)) + { + detail = new XmlBuffer(maxBufferSize); + XmlDictionaryWriter writer = detail.OpenSection(reader.Quotas); + writer.WriteNode(reader, false); + detail.CloseSection(); + detail.Close(); + } + reader.ReadEndElement(); + FaultReason reason = new FaultReason(translation); + return new ReceivedFault(code, reason, actor, actor, detail, EnvelopeVersion.Soap11); + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/MessageHeader.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/MessageHeader.cs new file mode 100644 index 000000000..3b212ec96 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/MessageHeader.cs @@ -0,0 +1,595 @@ +using System; +using System.Globalization; +using System.IO; +using System.Runtime.Serialization; +using System.Xml; +using CoreWCF.Runtime; +using CoreWCF.Description; +using CoreWCF.Dispatcher; + +namespace CoreWCF.Channels +{ + public abstract class MessageHeader : MessageHeaderInfo + { + const bool DefaultRelayValue = false; + const bool DefaultMustUnderstandValue = false; + const string DefaultActorValue = ""; + + public override string Actor + { + get { return ""; } + } + + public override bool IsReferenceParameter + { + get { return false; } + } + + public override bool MustUnderstand + { + get { return DefaultMustUnderstandValue; } + } + + public override bool Relay + { + get { return DefaultRelayValue; } + } + + public virtual bool IsMessageVersionSupported(MessageVersion messageVersion) + { + if (messageVersion == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("messageVersion"); + } + + return true; + } + + public override string ToString() + { + XmlWriterSettings xmlSettings = new XmlWriterSettings() { Indent = true }; + using (StringWriter stringWriter = new StringWriter(CultureInfo.InvariantCulture)) + { + using (XmlWriter textWriter = XmlWriter.Create(stringWriter, xmlSettings)) + { + using (XmlDictionaryWriter writer = XmlDictionaryWriter.CreateDictionaryWriter(textWriter)) + { + if (IsMessageVersionSupported(MessageVersion.Soap12WSAddressing10)) + { + WriteHeader(writer, MessageVersion.Soap12WSAddressing10); + } + //else if (IsMessageVersionSupported(MessageVersion.Soap12WSAddressingAugust2004)) + //{ + // WriteHeader(writer, MessageVersion.Soap12WSAddressingAugust2004); + //} + //else if (IsMessageVersionSupported(MessageVersion.Soap11WSAddressing10)) + //{ + // WriteHeader(writer, MessageVersion.Soap11WSAddressing10); + //} + //else if (IsMessageVersionSupported(MessageVersion.Soap11WSAddressingAugust2004)) + //{ + // WriteHeader(writer, MessageVersion.Soap11WSAddressingAugust2004); + //} + //else if (IsMessageVersionSupported(MessageVersion.Soap12)) + //{ + // WriteHeader(writer, MessageVersion.Soap12); + //} + else if (IsMessageVersionSupported(MessageVersion.Soap11)) + { + WriteHeader(writer, MessageVersion.Soap11); + } + else + { + WriteHeader(writer, MessageVersion.None); + } + + writer.Flush(); + return stringWriter.ToString(); + } + } + } + } + + public void WriteHeader(XmlWriter writer, MessageVersion messageVersion) + { + WriteHeader(XmlDictionaryWriter.CreateDictionaryWriter(writer), messageVersion); + } + + public void WriteHeader(XmlDictionaryWriter writer, MessageVersion messageVersion) + { + if (writer == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer"); + if (messageVersion == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("messageVersion"); + OnWriteStartHeader(writer, messageVersion); + OnWriteHeaderContents(writer, messageVersion); + writer.WriteEndElement(); + } + + public void WriteStartHeader(XmlDictionaryWriter writer, MessageVersion messageVersion) + { + if (writer == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer"); + if (messageVersion == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("messageVersion"); + OnWriteStartHeader(writer, messageVersion); + } + + protected virtual void OnWriteStartHeader(XmlDictionaryWriter writer, MessageVersion messageVersion) + { + writer.WriteStartElement(Name, Namespace); + WriteHeaderAttributes(writer, messageVersion); + } + + public void WriteHeaderContents(XmlDictionaryWriter writer, MessageVersion messageVersion) + { + if (writer == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer"); + if (messageVersion == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("messageVersion"); + OnWriteHeaderContents(writer, messageVersion); + } + + protected abstract void OnWriteHeaderContents(XmlDictionaryWriter writer, MessageVersion messageVersion); + + protected void WriteHeaderAttributes(XmlDictionaryWriter writer, MessageVersion messageVersion) + { + string actor = Actor; + if (actor.Length > 0) + writer.WriteAttributeString(messageVersion.Envelope.DictionaryActor, messageVersion.Envelope.DictionaryNamespace, actor); + if (MustUnderstand) + writer.WriteAttributeString(XD.MessageDictionary.MustUnderstand, messageVersion.Envelope.DictionaryNamespace, "1"); + if (Relay && messageVersion.Envelope == EnvelopeVersion.Soap12) + writer.WriteAttributeString(XD.Message12Dictionary.Relay, XD.Message12Dictionary.Namespace, "1"); + } + + public static MessageHeader CreateHeader(string name, string ns, object value) + { + return CreateHeader(name, ns, value, DefaultMustUnderstandValue, DefaultActorValue, DefaultRelayValue); + } + + public static MessageHeader CreateHeader(string name, string ns, object value, bool mustUnderstand) + { + return CreateHeader(name, ns, value, mustUnderstand, DefaultActorValue, DefaultRelayValue); + } + + public static MessageHeader CreateHeader(string name, string ns, object value, bool mustUnderstand, string actor) + { + return CreateHeader(name, ns, value, mustUnderstand, actor, DefaultRelayValue); + } + + public static MessageHeader CreateHeader(string name, string ns, object value, bool mustUnderstand, string actor, bool relay) + { + return new XmlObjectSerializerHeader(name, ns, value, null, mustUnderstand, actor, relay); + } + + public static MessageHeader CreateHeader(string name, string ns, object value, XmlObjectSerializer serializer) + { + return CreateHeader(name, ns, value, serializer, DefaultMustUnderstandValue, DefaultActorValue, DefaultRelayValue); + } + + public static MessageHeader CreateHeader(string name, string ns, object value, XmlObjectSerializer serializer, bool mustUnderstand) + { + return CreateHeader(name, ns, value, serializer, mustUnderstand, DefaultActorValue, DefaultRelayValue); + } + + public static MessageHeader CreateHeader(string name, string ns, object value, XmlObjectSerializer serializer, bool mustUnderstand, string actor) + { + return CreateHeader(name, ns, value, serializer, mustUnderstand, actor, DefaultRelayValue); + } + + public static MessageHeader CreateHeader(string name, string ns, object value, XmlObjectSerializer serializer, bool mustUnderstand, string actor, bool relay) + { + if (serializer == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("serializer"); + + return new XmlObjectSerializerHeader(name, ns, value, serializer, mustUnderstand, actor, relay); + } + + internal static void GetHeaderAttributes(XmlDictionaryReader reader, MessageVersion version, + out string actor, out bool mustUnderstand, out bool relay, out bool isReferenceParameter) + { + int attributeCount = reader.AttributeCount; + + if (attributeCount == 0) + { + mustUnderstand = false; + actor = string.Empty; + relay = false; + isReferenceParameter = false; + } + else + { + string mustUnderstandString = reader.GetAttribute(XD.MessageDictionary.MustUnderstand, version.Envelope.DictionaryNamespace); + if (mustUnderstandString != null && ToBoolean(mustUnderstandString)) + mustUnderstand = true; + else + mustUnderstand = false; + + if (mustUnderstand && attributeCount == 1) + { + actor = string.Empty; + relay = false; + } + else + { + actor = reader.GetAttribute(version.Envelope.DictionaryActor, version.Envelope.DictionaryNamespace); + if (actor == null) + actor = ""; + + if (version.Envelope == EnvelopeVersion.Soap12) + { + string relayString = reader.GetAttribute(XD.Message12Dictionary.Relay, version.Envelope.DictionaryNamespace); + if (relayString != null && ToBoolean(relayString)) + relay = true; + else + relay = false; + } + else + { + relay = false; + } + } + + isReferenceParameter = false; + if (version.Addressing == AddressingVersion.WSAddressing10) + { + string refParam = reader.GetAttribute(XD.AddressingDictionary.IsReferenceParameter, version.Addressing.DictionaryNamespace); + if (refParam != null) + isReferenceParameter = ToBoolean(refParam); + } + } + } + + static bool ToBoolean(string value) + { + if (value.Length == 1) + { + char ch = value[0]; + if (ch == '1') + { + return true; + } + if (ch == '0') + { + return false; + } + } + else + { + if (value == "true") + { + return true; + } + else if (value == "false") + { + return false; + } + } + try + { + return XmlConvert.ToBoolean(value); + } + catch (FormatException exception) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(exception.Message, null)); + } + } + } + + // TODO: This needed to be made public for NetTcp, investigate making it internal again + public abstract class DictionaryHeader : MessageHeader + { + public override string Name + { + get { return DictionaryName.Value; } + } + + public override string Namespace + { + get { return DictionaryNamespace.Value; } + } + + public abstract XmlDictionaryString DictionaryName { get; } + public abstract XmlDictionaryString DictionaryNamespace { get; } + + protected override void OnWriteStartHeader(XmlDictionaryWriter writer, MessageVersion messageVersion) + { + writer.WriteStartElement(DictionaryName, DictionaryNamespace); + WriteHeaderAttributes(writer, messageVersion); + } + } + + class XmlObjectSerializerHeader : MessageHeader + { + XmlObjectSerializer serializer; + bool mustUnderstand; + bool relay; + bool isOneTwoSupported; + bool isOneOneSupported; + bool isNoneSupported; + object objectToSerialize; + string name; + string ns; + string actor; + object syncRoot = new object(); + + XmlObjectSerializerHeader(XmlObjectSerializer serializer, bool mustUnderstand, string actor, bool relay) + { + if (actor == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("actor"); + } + + this.mustUnderstand = mustUnderstand; + this.relay = relay; + this.serializer = serializer; + this.actor = actor; + if (actor == EnvelopeVersion.Soap12.UltimateDestinationActor) + { + isOneOneSupported = false; + isOneTwoSupported = true; + } + else if (actor == EnvelopeVersion.Soap12.NextDestinationActorValue) + { + isOneOneSupported = false; + isOneTwoSupported = true; + } + else if (actor == EnvelopeVersion.Soap11.NextDestinationActorValue) + { + isOneOneSupported = true; + isOneTwoSupported = false; + } + else + { + isOneOneSupported = true; + isOneTwoSupported = true; + isNoneSupported = true; + } + } + + public XmlObjectSerializerHeader(string name, string ns, object objectToSerialize, XmlObjectSerializer serializer, bool mustUnderstand, string actor, bool relay) + : this(serializer, mustUnderstand, actor, relay) + { + if (null == name) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("name"); + } + + if (name.Length == 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.SFXHeaderNameCannotBeNullOrEmpty, nameof(name))); + } + + if (ns == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("ns"); + } + if (ns.Length > 0) + { + NamingHelper.CheckUriParameter(ns, "ns"); + } + this.objectToSerialize = objectToSerialize; + this.name = name; + this.ns = ns; + } + + public override bool IsMessageVersionSupported(MessageVersion messageVersion) + { + if (messageVersion == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("messageVersion"); + } + + if (messageVersion.Envelope == EnvelopeVersion.Soap12) + { + return isOneTwoSupported; + } + else if (messageVersion.Envelope == EnvelopeVersion.Soap11) + { + return isOneOneSupported; + } + else if (messageVersion.Envelope == EnvelopeVersion.None) + { + return isNoneSupported; + } + else + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.EnvelopeVersionUnknown, messageVersion.Envelope.ToString()))); + } + } + + public override string Name + { + get { return name; } + } + + public override string Namespace + { + get { return ns; } + } + + public override bool MustUnderstand + { + get { return mustUnderstand; } + } + + public override bool Relay + { + get { return relay; } + } + + public override string Actor + { + get { return actor; } + } + + protected override void OnWriteHeaderContents(XmlDictionaryWriter writer, MessageVersion messageVersion) + { + lock (syncRoot) + { + if (serializer == null) + { + serializer = DataContractSerializerDefaults.CreateSerializer( + (objectToSerialize == null ? typeof(object) : objectToSerialize.GetType()), Name, Namespace, int.MaxValue/*maxItems*/); + } + + serializer.WriteObjectContent(writer, objectToSerialize); + } + } + } + + abstract class ReadableMessageHeader : MessageHeader + { + public abstract XmlDictionaryReader GetHeaderReader(); + + protected override void OnWriteStartHeader(XmlDictionaryWriter writer, MessageVersion messageVersion) + { + if (!IsMessageVersionSupported(messageVersion)) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.Format(SR.MessageHeaderVersionNotSupported, GetType().FullName, messageVersion.ToString()), "version")); + XmlDictionaryReader reader = GetHeaderReader(); + writer.WriteStartElement(reader.Prefix, reader.LocalName, reader.NamespaceURI); + writer.WriteAttributes(reader, false); + reader.Dispose(); + } + + protected override void OnWriteHeaderContents(XmlDictionaryWriter writer, MessageVersion messageVersion) + { + XmlDictionaryReader reader = GetHeaderReader(); + reader.ReadStartElement(); + while (reader.NodeType != XmlNodeType.EndElement) + writer.WriteNode(reader, false); + reader.ReadEndElement(); + reader.Dispose(); + } + } + + internal interface IMessageHeaderWithSharedNamespace + { + XmlDictionaryString SharedNamespace { get; } + XmlDictionaryString SharedPrefix { get; } + } + + class BufferedHeader : ReadableMessageHeader + { + MessageVersion version; + XmlBuffer buffer; + int bufferIndex; + string actor; + bool relay; + bool mustUnderstand; + string name; + string ns; + bool streamed; + bool isRefParam; + + public BufferedHeader(MessageVersion version, XmlBuffer buffer, int bufferIndex, string name, string ns, bool mustUnderstand, string actor, bool relay, bool isRefParam) + { + this.version = version; + this.buffer = buffer; + this.bufferIndex = bufferIndex; + this.name = name; + this.ns = ns; + this.mustUnderstand = mustUnderstand; + this.actor = actor; + this.relay = relay; + this.isRefParam = isRefParam; + } + + public BufferedHeader(MessageVersion version, XmlBuffer buffer, int bufferIndex, MessageHeaderInfo headerInfo) + { + this.version = version; + this.buffer = buffer; + this.bufferIndex = bufferIndex; + actor = headerInfo.Actor; + relay = headerInfo.Relay; + name = headerInfo.Name; + ns = headerInfo.Namespace; + isRefParam = headerInfo.IsReferenceParameter; + mustUnderstand = headerInfo.MustUnderstand; + } + + public BufferedHeader(MessageVersion version, XmlBuffer buffer, XmlDictionaryReader reader, XmlAttributeHolder[] envelopeAttributes, XmlAttributeHolder[] headerAttributes) + { + streamed = true; + this.buffer = buffer; + this.version = version; + GetHeaderAttributes(reader, version, out actor, out mustUnderstand, out relay, out isRefParam); + name = reader.LocalName; + ns = reader.NamespaceURI; + Fx.Assert(name != null, ""); + Fx.Assert(ns != null, ""); + bufferIndex = buffer.SectionCount; + XmlDictionaryWriter writer = buffer.OpenSection(reader.Quotas); + + // Write an enclosing Envelope tag + writer.WriteStartElement(MessageStrings.Envelope); + if (envelopeAttributes != null) + XmlAttributeHolder.WriteAttributes(envelopeAttributes, writer); + + // Write and enclosing Header tag + writer.WriteStartElement(MessageStrings.Header); + if (headerAttributes != null) + XmlAttributeHolder.WriteAttributes(headerAttributes, writer); + + writer.WriteNode(reader, false); + + writer.WriteEndElement(); + writer.WriteEndElement(); + + buffer.CloseSection(); + } + + public override string Actor + { + get { return actor; } + } + + public override bool IsReferenceParameter + { + get { return isRefParam; } + } + + public override string Name + { + get { return name; } + } + + public override string Namespace + { + get { return ns; } + } + + public override bool MustUnderstand + { + get { return mustUnderstand; } + } + + public override bool Relay + { + get { return relay; } + } + + public override bool IsMessageVersionSupported(MessageVersion messageVersion) + { + if (messageVersion == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(messageVersion)); + return messageVersion == version; + } + + public override XmlDictionaryReader GetHeaderReader() + { + XmlDictionaryReader reader = buffer.GetReader(bufferIndex); + // See if we need to move past the enclosing envelope/header + if (streamed) + { + reader.MoveToContent(); + reader.Read(); // Envelope + reader.Read(); // Header + reader.MoveToContent(); + } + return reader; + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/MessageHeaderInfo.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/MessageHeaderInfo.cs new file mode 100644 index 000000000..fd6d35b36 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/MessageHeaderInfo.cs @@ -0,0 +1,13 @@ +namespace CoreWCF.Channels +{ + public abstract class MessageHeaderInfo + { + protected MessageHeaderInfo() { } + public abstract string Actor { get; } + public abstract bool IsReferenceParameter { get; } + public abstract bool MustUnderstand { get; } + public abstract string Name { get; } + public abstract string Namespace { get; } + public abstract bool Relay { get; } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/MessageHeaders.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/MessageHeaders.cs new file mode 100644 index 000000000..80edd612c --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/MessageHeaders.cs @@ -0,0 +1,1776 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Runtime.Serialization; +using System.Threading; +using System.Xml; +using CoreWCF.Runtime; +using CoreWCF.Diagnostics; +using CoreWCF.Dispatcher; + +namespace CoreWCF.Channels +{ + public sealed class MessageHeaders : IEnumerable + { + int collectionVersion; + int headerCount; + Header[] headers; + MessageVersion version; + IBufferedMessageData bufferedMessageData; + UnderstoodHeaders understoodHeaders; + const int InitialHeaderCount = 4; + const int MaxRecycledArrayLength = 8; + static XmlDictionaryString[] localNames; + + internal const string WildcardAction = "*"; + + // The highest node and attribute counts reached by the BVTs were 1829 and 667 respectively. + const int MaxBufferedHeaderNodes = 4096; + const int MaxBufferedHeaderAttributes = 2048; + int nodeCount = 0; + int attrCount = 0; + bool understoodHeadersModified; + + public MessageHeaders(MessageVersion version, int initialSize) + { + Init(version, initialSize); + } + + public MessageHeaders(MessageVersion version) + : this(version, InitialHeaderCount) + { + } + + internal MessageHeaders(MessageVersion version, XmlDictionaryReader reader, XmlAttributeHolder[] envelopeAttributes, XmlAttributeHolder[] headerAttributes, ref int maxSizeOfHeaders) + : this(version) + { + if (maxSizeOfHeaders < 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new ArgumentOutOfRangeException(nameof(maxSizeOfHeaders), maxSizeOfHeaders, + SR.ValueMustBeNonNegative)); + } + + if (version == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("version"); + if (reader == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader"); + if (reader.IsEmptyElement) + { + reader.Read(); + return; + } + XmlBuffer xmlBuffer = null; + EnvelopeVersion envelopeVersion = version.Envelope; + reader.ReadStartElement(XD.MessageDictionary.Header, envelopeVersion.DictionaryNamespace); + while (reader.IsStartElement()) + { + if (xmlBuffer == null) + xmlBuffer = new XmlBuffer(maxSizeOfHeaders); + BufferedHeader bufferedHeader = new BufferedHeader(version, xmlBuffer, reader, envelopeAttributes, headerAttributes); + HeaderProcessing processing = bufferedHeader.MustUnderstand ? HeaderProcessing.MustUnderstand : 0; + HeaderKind kind = GetHeaderKind(bufferedHeader); + if (kind != HeaderKind.Unknown) + { + processing |= HeaderProcessing.Understood; + MessageHeaders.TraceUnderstood(bufferedHeader); + } + Header newHeader = new Header(kind, bufferedHeader, processing); + AddHeader(newHeader); + } + if (xmlBuffer != null) + { + xmlBuffer.Close(); + maxSizeOfHeaders -= xmlBuffer.BufferSize; + } + reader.ReadEndElement(); + collectionVersion = 0; + } + + internal MessageHeaders(MessageVersion version, XmlDictionaryReader reader, IBufferedMessageData bufferedMessageData, RecycledMessageState recycledMessageState, bool[] understoodHeaders, bool understoodHeadersModified) + { + headers = new Header[InitialHeaderCount]; + Init(version, reader, bufferedMessageData, recycledMessageState, understoodHeaders, understoodHeadersModified); + } + + internal MessageHeaders(MessageVersion version, MessageHeaders headers, IBufferedMessageData bufferedMessageData) + { + this.version = version; + this.bufferedMessageData = bufferedMessageData; + headerCount = headers.headerCount; + this.headers = new Header[headerCount]; + Array.Copy(headers.headers, this.headers, headerCount); + collectionVersion = 0; + } + + public MessageHeaders(MessageHeaders collection) + { + if (collection == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("collection"); + + Init(collection.version, collection.headers.Length); + CopyHeadersFrom(collection); + collectionVersion = 0; + } + + public string Action + { + get + { + int index = FindHeaderProperty(HeaderKind.Action); + if (index < 0) + return null; + ActionHeader actionHeader = headers[index].HeaderInfo as ActionHeader; + if (actionHeader != null) + return actionHeader.Action; + using (XmlDictionaryReader reader = GetReaderAtHeader(index)) + { + return ActionHeader.ReadHeaderValue(reader, version.Addressing); + } + } + set + { + if (value != null) + SetActionHeader(ActionHeader.Create(value, version.Addressing)); + else + SetHeaderProperty(HeaderKind.Action, null); + } + } + + internal bool CanRecycle + { + get { return headers.Length <= MaxRecycledArrayLength; } + } + + internal bool ContainsOnlyBufferedMessageHeaders + { + get { return (bufferedMessageData != null && collectionVersion == 0); } + } + + internal int CollectionVersion + { + get { return collectionVersion; } + } + + public int Count + { + get { return headerCount; } + } + + public EndpointAddress FaultTo + { + get + { + int index = FindHeaderProperty(HeaderKind.FaultTo); + if (index < 0) + return null; + FaultToHeader faultToHeader = headers[index].HeaderInfo as FaultToHeader; + if (faultToHeader != null) + return faultToHeader.FaultTo; + using (XmlDictionaryReader reader = GetReaderAtHeader(index)) + { + return FaultToHeader.ReadHeaderValue(reader, version.Addressing); + } + } + set + { + if (value != null) + SetFaultToHeader(FaultToHeader.Create(value, version.Addressing)); + else + SetHeaderProperty(HeaderKind.FaultTo, null); + } + } + + public EndpointAddress From + { + get + { + int index = FindHeaderProperty(HeaderKind.From); + if (index < 0) + return null; + FromHeader fromHeader = headers[index].HeaderInfo as FromHeader; + if (fromHeader != null) + return fromHeader.From; + using (XmlDictionaryReader reader = GetReaderAtHeader(index)) + { + return FromHeader.ReadHeaderValue(reader, version.Addressing); + } + } + set + { + if (value != null) + SetFromHeader(FromHeader.Create(value, version.Addressing)); + else + SetHeaderProperty(HeaderKind.From, null); + } + } + + internal bool HasMustUnderstandBeenModified + { + get + { + if (understoodHeaders != null) + { + return understoodHeaders.Modified; + } + else + { + return understoodHeadersModified; + } + } + } + + public UniqueId MessageId + { + get + { + int index = FindHeaderProperty(HeaderKind.MessageId); + if (index < 0) + return null; + MessageIDHeader messageIDHeader = headers[index].HeaderInfo as MessageIDHeader; + if (messageIDHeader != null) + return messageIDHeader.MessageId; + using (XmlDictionaryReader reader = GetReaderAtHeader(index)) + { + return MessageIDHeader.ReadHeaderValue(reader, version.Addressing); + } + } + set + { + if (value != null) + SetMessageIDHeader(MessageIDHeader.Create(value, version.Addressing)); + else + SetHeaderProperty(HeaderKind.MessageId, null); + } + } + + public MessageVersion MessageVersion + { + get { return version; } + } + + public UniqueId RelatesTo + { + get + { + return GetRelatesTo(RelatesToHeader.ReplyRelationshipType); + } + set + { + SetRelatesTo(RelatesToHeader.ReplyRelationshipType, value); + } + } + + public EndpointAddress ReplyTo + { + get + { + int index = FindHeaderProperty(HeaderKind.ReplyTo); + if (index < 0) + return null; + ReplyToHeader replyToHeader = headers[index].HeaderInfo as ReplyToHeader; + if (replyToHeader != null) + return replyToHeader.ReplyTo; + using (XmlDictionaryReader reader = GetReaderAtHeader(index)) + { + return ReplyToHeader.ReadHeaderValue(reader, version.Addressing); + } + } + set + { + if (value != null) + SetReplyToHeader(ReplyToHeader.Create(value, version.Addressing)); + else + SetHeaderProperty(HeaderKind.ReplyTo, null); + } + } + + public Uri To + { + get + { + int index = FindHeaderProperty(HeaderKind.To); + if (index < 0) + return null; + ToHeader toHeader = headers[index].HeaderInfo as ToHeader; + if (toHeader != null) + return toHeader.To; + using (XmlDictionaryReader reader = GetReaderAtHeader(index)) + { + return ToHeader.ReadHeaderValue(reader, version.Addressing); + } + } + set + { + if (value != null) + SetToHeader(ToHeader.Create(value, version.Addressing)); + else + SetHeaderProperty(HeaderKind.To, null); + } + } + + internal UnderstoodHeaders UnderstoodHeaders + { + get + { + if (understoodHeaders == null) + understoodHeaders = new UnderstoodHeaders(this, understoodHeadersModified); + return understoodHeaders; + } + } + + public MessageHeaderInfo this[int index] + { + get + { + if (index < 0 || index >= headerCount) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new ArgumentOutOfRangeException(nameof(index), index, + SR.Format(SR.ValueMustBeInRange, 0, headerCount))); + } + + return headers[index].HeaderInfo; + } + } + + public void Add(MessageHeader header) + { + Insert(headerCount, header); + } + + internal void AddActionHeader(ActionHeader actionHeader) + { + Insert(headerCount, actionHeader, HeaderKind.Action); + } + + internal void AddMessageIDHeader(MessageIDHeader messageIDHeader) + { + Insert(headerCount, messageIDHeader, HeaderKind.MessageId); + } + + internal void AddRelatesToHeader(RelatesToHeader relatesToHeader) + { + Insert(headerCount, relatesToHeader, HeaderKind.RelatesTo); + } + + internal void AddReplyToHeader(ReplyToHeader replyToHeader) + { + Insert(headerCount, replyToHeader, HeaderKind.ReplyTo); + } + + internal void AddToHeader(ToHeader toHeader) + { + Insert(headerCount, toHeader, HeaderKind.To); + } + + void Add(MessageHeader header, HeaderKind kind) + { + Insert(headerCount, header, kind); + } + + void AddHeader(Header header) + { + InsertHeader(headerCount, header); + } + + internal void AddUnderstood(int i) + { + headers[i].HeaderProcessing |= HeaderProcessing.Understood; + MessageHeaders.TraceUnderstood(headers[i].HeaderInfo); + } + + internal void AddUnderstood(MessageHeaderInfo headerInfo) + { + if (headerInfo == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("headerInfo"); + for (int i = 0; i < headerCount; i++) + { + if ((object)headers[i].HeaderInfo == (object)headerInfo) + { + if ((headers[i].HeaderProcessing & HeaderProcessing.Understood) != 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException( + SR.Format(SR.HeaderAlreadyUnderstood, headerInfo.Name, headerInfo.Namespace), nameof(headerInfo))); + } + + AddUnderstood(i); + } + } + } + + void CaptureBufferedHeaders() + { + CaptureBufferedHeaders(-1); + } + + void CaptureBufferedHeaders(int exceptIndex) + { + using (XmlDictionaryReader reader = GetBufferedMessageHeaderReaderAtHeaderContents(bufferedMessageData)) + { + for (int i = 0; i < headerCount; i++) + { + if (reader.NodeType != XmlNodeType.Element) + { + if (reader.MoveToContent() != XmlNodeType.Element) + break; + } + + Header header = headers[i]; + if (i == exceptIndex || header.HeaderType != HeaderType.BufferedMessageHeader) + { + reader.Skip(); + } + else + { + headers[i] = new Header(header.HeaderKind, CaptureBufferedHeader(reader, + header.HeaderInfo), header.HeaderProcessing); + } + } + } + bufferedMessageData = null; + } + + BufferedHeader CaptureBufferedHeader(XmlDictionaryReader reader, MessageHeaderInfo headerInfo) + { + XmlBuffer buffer = new XmlBuffer(int.MaxValue); + XmlDictionaryWriter writer = buffer.OpenSection(bufferedMessageData.Quotas); + writer.WriteNode(reader, false); + buffer.CloseSection(); + buffer.Close(); + return new BufferedHeader(version, buffer, 0, headerInfo); + } + + BufferedHeader CaptureBufferedHeader(IBufferedMessageData bufferedMessageData, MessageHeaderInfo headerInfo, int bufferedMessageHeaderIndex) + { + XmlBuffer buffer = new XmlBuffer(int.MaxValue); + XmlDictionaryWriter writer = buffer.OpenSection(bufferedMessageData.Quotas); + WriteBufferedMessageHeader(bufferedMessageData, bufferedMessageHeaderIndex, writer); + buffer.CloseSection(); + buffer.Close(); + return new BufferedHeader(version, buffer, 0, headerInfo); + } + + BufferedHeader CaptureWriteableHeader(MessageHeader writeableHeader) + { + XmlBuffer buffer = new XmlBuffer(int.MaxValue); + XmlDictionaryWriter writer = buffer.OpenSection(XmlDictionaryReaderQuotas.Max); + writeableHeader.WriteHeader(writer, version); + buffer.CloseSection(); + buffer.Close(); + return new BufferedHeader(version, buffer, 0, writeableHeader); + } + + public void Clear() + { + for (int i = 0; i < headerCount; i++) + headers[i] = new Header(); + headerCount = 0; + collectionVersion++; + bufferedMessageData = null; + } + + public void CopyHeaderFrom(Message message, int headerIndex) + { + if (message == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("message"); + CopyHeaderFrom(message.Headers, headerIndex); + } + + public void CopyHeaderFrom(MessageHeaders collection, int headerIndex) + { + if (collection == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("collection"); + } + + if (collection.version != version) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.Format(SR.MessageHeaderVersionMismatch, collection.version.ToString(), version.ToString()), nameof(collection))); + } + + if (headerIndex < 0 || headerIndex >= collection.headerCount) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new ArgumentOutOfRangeException(nameof(headerIndex), headerIndex, + SR.Format(SR.ValueMustBeInRange, 0, collection.headerCount))); + } + Header header = collection.headers[headerIndex]; + HeaderProcessing processing = header.HeaderInfo.MustUnderstand ? HeaderProcessing.MustUnderstand : 0; + if ((header.HeaderProcessing & HeaderProcessing.Understood) != 0 || header.HeaderKind != HeaderKind.Unknown) + processing |= HeaderProcessing.Understood; + switch (header.HeaderType) + { + case HeaderType.BufferedMessageHeader: + AddHeader(new Header(header.HeaderKind, collection.CaptureBufferedHeader(collection.bufferedMessageData, + header.HeaderInfo, headerIndex), processing)); + break; + case HeaderType.ReadableHeader: + AddHeader(new Header(header.HeaderKind, header.ReadableHeader, processing)); + break; + case HeaderType.WriteableHeader: + AddHeader(new Header(header.HeaderKind, header.MessageHeader, processing)); + break; + default: + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.InvalidEnumValue, header.HeaderType))); + } + } + + public void CopyHeadersFrom(Message message) + { + if (message == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("message"); + CopyHeadersFrom(message.Headers); + } + + public void CopyHeadersFrom(MessageHeaders collection) + { + if (collection == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("collection"); + for (int i = 0; i < collection.headerCount; i++) + CopyHeaderFrom(collection, i); + } + + public void CopyTo(MessageHeaderInfo[] array, int index) + { + if (array == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("array"); + } + + if (index < 0 || (index + headerCount) > array.Length) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new ArgumentOutOfRangeException(nameof(index), index, + SR.Format(SR.ValueMustBeInRange, 0, array.Length - headerCount))); + } + for (int i = 0; i < headerCount; i++) + array[i + index] = headers[i].HeaderInfo; + } + + Exception CreateDuplicateHeaderException(HeaderKind kind) + { + string name; + switch (kind) + { + case HeaderKind.Action: + name = AddressingStrings.Action; + break; + case HeaderKind.FaultTo: + name = AddressingStrings.FaultTo; + break; + case HeaderKind.From: + name = AddressingStrings.From; + break; + case HeaderKind.MessageId: + name = AddressingStrings.MessageId; + break; + case HeaderKind.ReplyTo: + name = AddressingStrings.ReplyTo; + break; + case HeaderKind.To: + name = AddressingStrings.To; + break; + default: + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.InvalidEnumValue, kind))); + } + + return new MessageHeaderException( + SR.Format(SR.MultipleMessageHeaders, name, version.Addressing.Namespace), + name, + version.Addressing.Namespace, + true); + } + + public int FindHeader(string name, string ns) + { + if (name == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("name"); + if (ns == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("ns"); + + if (ns == version.Addressing.Namespace) + { + return FindAddressingHeader(name, ns); + } + else + { + return FindNonAddressingHeader(name, ns, version.Envelope.UltimateDestinationActorValues); + } + } + + int FindAddressingHeader(string name, string ns) + { + int foundAt = -1; + for (int i = 0; i < headerCount; i++) + { + if (headers[i].HeaderKind != HeaderKind.Unknown) + { + MessageHeaderInfo info = headers[i].HeaderInfo; + if (info.Name == name && info.Namespace == ns) + { + if (foundAt >= 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new MessageHeaderException(SR.Format(SR.MultipleMessageHeaders, name, ns), name, ns, true)); + } + foundAt = i; + } + } + } + return foundAt; + } + + int FindNonAddressingHeader(string name, string ns, string[] actors) + { + int foundAt = -1; + for (int i = 0; i < headerCount; i++) + { + if (headers[i].HeaderKind == HeaderKind.Unknown) + { + MessageHeaderInfo info = headers[i].HeaderInfo; + if (info.Name == name && info.Namespace == ns) + { + for (int j = 0; j < actors.Length; j++) + { + if (actors[j] == info.Actor) + { + if (foundAt >= 0) + { + if (actors.Length == 1) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageHeaderException(SR.Format(SR.MultipleMessageHeadersWithActor, name, ns, actors[0]), name, ns, true)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageHeaderException(SR.Format(SR.MultipleMessageHeaders, name, ns), name, ns, true)); + } + foundAt = i; + } + } + } + } + } + return foundAt; + } + + public int FindHeader(string name, string ns, params string[] actors) + { + if (name == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("name"); + if (ns == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("ns"); + if (actors == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("actors"); + int foundAt = -1; + for (int i = 0; i < headerCount; i++) + { + MessageHeaderInfo info = headers[i].HeaderInfo; + if (info.Name == name && info.Namespace == ns) + { + for (int j = 0; j < actors.Length; j++) + { + if (actors[j] == info.Actor) + { + if (foundAt >= 0) + { + if (actors.Length == 1) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageHeaderException(SR.Format(SR.MultipleMessageHeadersWithActor, name, ns, actors[0]), name, ns, true)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageHeaderException(SR.Format(SR.MultipleMessageHeaders, name, ns), name, ns, true)); + } + foundAt = i; + } + } + } + } + return foundAt; + } + + int FindHeaderProperty(HeaderKind kind) + { + int index = -1; + for (int i = 0; i < headerCount; i++) + { + if (headers[i].HeaderKind == kind) + { + if (index >= 0) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateDuplicateHeaderException(kind)); + index = i; + } + } + return index; + } + + int FindRelatesTo(Uri relationshipType, out UniqueId messageId) + { + UniqueId foundValue = null; + int foundIndex = -1; + for (int i = 0; i < headerCount; i++) + { + if (headers[i].HeaderKind == HeaderKind.RelatesTo) + { + Uri tempRelationship; + UniqueId tempValue; + GetRelatesToValues(i, out tempRelationship, out tempValue); + + if (relationshipType == tempRelationship) + { + if (foundValue != null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new MessageHeaderException( + SR.Format(SR.MultipleRelatesToHeaders, relationshipType.AbsoluteUri), + AddressingStrings.RelatesTo, + version.Addressing.Namespace, + true)); + } + foundValue = tempValue; + foundIndex = i; + } + } + } + + messageId = foundValue; + return foundIndex; + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public IEnumerator GetEnumerator() + { + MessageHeaderInfo[] headers = new MessageHeaderInfo[headerCount]; + CopyTo(headers, 0); + return GetEnumerator(headers); + } + + IEnumerator GetEnumerator(MessageHeaderInfo[] headers) + { + IList list = new ReadOnlyCollection(headers); + return list.GetEnumerator(); + } + + internal IEnumerator GetUnderstoodEnumerator() + { + List understoodHeaders = new List(); + + for (int i = 0; i < headerCount; i++) + { + if ((headers[i].HeaderProcessing & HeaderProcessing.Understood) != 0) + { + understoodHeaders.Add(headers[i].HeaderInfo); + } + } + + return understoodHeaders.GetEnumerator(); + } + + static XmlDictionaryReader GetBufferedMessageHeaderReaderAtHeaderContents(IBufferedMessageData bufferedMessageData) + { + XmlDictionaryReader reader = bufferedMessageData.GetMessageReader(); + if (reader.NodeType == XmlNodeType.Element) + reader.Read(); + else + reader.ReadStartElement(); + if (reader.NodeType == XmlNodeType.Element) + reader.Read(); + else + reader.ReadStartElement(); + return reader; + } + + XmlDictionaryReader GetBufferedMessageHeaderReader(IBufferedMessageData bufferedMessageData, int bufferedMessageHeaderIndex) + { + // Check if we need to change representations + if (nodeCount > MaxBufferedHeaderNodes || attrCount > MaxBufferedHeaderAttributes) + { + CaptureBufferedHeaders(); + return headers[bufferedMessageHeaderIndex].ReadableHeader.GetHeaderReader(); + } + + XmlDictionaryReader reader = GetBufferedMessageHeaderReaderAtHeaderContents(bufferedMessageData); + for (;;) + { + if (reader.NodeType != XmlNodeType.Element) + reader.MoveToContent(); + if (bufferedMessageHeaderIndex == 0) + break; + Skip(reader); + bufferedMessageHeaderIndex--; + } + + return reader; + } + + void Skip(XmlDictionaryReader reader) + { + if (reader.MoveToContent() == XmlNodeType.Element && !reader.IsEmptyElement) + { + int depth = reader.Depth; + do + { + attrCount += reader.AttributeCount; + nodeCount++; + } while (reader.Read() && depth < reader.Depth); + + // consume end tag + if (reader.NodeType == XmlNodeType.EndElement) + { + nodeCount++; + reader.Read(); + } + } + else + { + attrCount += reader.AttributeCount; + nodeCount++; + reader.Read(); + } + } + + public T GetHeader(string name, string ns) + { + return GetHeader(name, ns, DataContractSerializerDefaults.CreateSerializer(typeof(T), name, ns, int.MaxValue/*maxItems*/)); + } + + public T GetHeader(string name, string ns, params string[] actors) + { + int index = FindHeader(name, ns, actors); + if (index < 0) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageHeaderException(SR.Format(SR.HeaderNotFound, name, ns), name, ns)); + return GetHeader(index); + + } + + public T GetHeader(string name, string ns, XmlObjectSerializer serializer) + { + if (serializer == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("serializer"); + int index = FindHeader(name, ns); + if (index < 0) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageHeaderException(SR.Format(SR.HeaderNotFound, name, ns), name, ns)); + return GetHeader(index, serializer); + } + + public T GetHeader(int index) + { + if (index < 0 || index >= headerCount) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new ArgumentOutOfRangeException(nameof(index), index, + SR.Format(SR.ValueMustBeInRange, 0, headerCount))); + } + + MessageHeaderInfo headerInfo = headers[index].HeaderInfo; + return GetHeader(index, DataContractSerializerDefaults.CreateSerializer(typeof(T), headerInfo.Name, headerInfo.Namespace, int.MaxValue/*maxItems*/)); + } + + public T GetHeader(int index, XmlObjectSerializer serializer) + { + if (serializer == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("serializer"); + using (XmlDictionaryReader reader = GetReaderAtHeader(index)) + { + return (T)serializer.ReadObject(reader); + } + } + + HeaderKind GetHeaderKind(MessageHeaderInfo headerInfo) + { + HeaderKind headerKind = HeaderKind.Unknown; + + if (headerInfo.Namespace == version.Addressing.Namespace) + { + if (version.Envelope.IsUltimateDestinationActor(headerInfo.Actor)) + { + string name = headerInfo.Name; + if (name.Length > 0) + { + switch (name[0]) + { + case 'A': + if (name == AddressingStrings.Action) + { + headerKind = HeaderKind.Action; + } + break; + case 'F': + if (name == AddressingStrings.From) + { + headerKind = HeaderKind.From; + } + else if (name == AddressingStrings.FaultTo) + { + headerKind = HeaderKind.FaultTo; + } + break; + case 'M': + if (name == AddressingStrings.MessageId) + { + headerKind = HeaderKind.MessageId; + } + break; + case 'R': + if (name == AddressingStrings.ReplyTo) + { + headerKind = HeaderKind.ReplyTo; + } + else if (name == AddressingStrings.RelatesTo) + { + headerKind = HeaderKind.RelatesTo; + } + break; + case 'T': + if (name == AddressingStrings.To) + { + headerKind = HeaderKind.To; + } + break; + } + } + } + } + + ValidateHeaderKind(headerKind); + return headerKind; + } + + void ValidateHeaderKind(HeaderKind headerKind) + { + if (version.Envelope == EnvelopeVersion.None) + { + if (headerKind != HeaderKind.Action && headerKind != HeaderKind.To) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new InvalidOperationException(SR.Format(SR.HeadersCannotBeAddedToEnvelopeVersion, version.Envelope))); + } + } + + if (version.Addressing == AddressingVersion.None) + { + if (headerKind != HeaderKind.Unknown && headerKind != HeaderKind.Action && headerKind != HeaderKind.To) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new InvalidOperationException(SR.Format(SR.AddressingHeadersCannotBeAddedToAddressingVersion, version.Addressing))); + } + } + } + + public XmlDictionaryReader GetReaderAtHeader(int headerIndex) + { + if (headerIndex < 0 || headerIndex >= headerCount) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new ArgumentOutOfRangeException(nameof(headerIndex), headerIndex, + SR.Format(SR.ValueMustBeInRange, 0, headerCount))); + } + + switch (headers[headerIndex].HeaderType) + { + case HeaderType.ReadableHeader: + return headers[headerIndex].ReadableHeader.GetHeaderReader(); + case HeaderType.WriteableHeader: + MessageHeader writeableHeader = headers[headerIndex].MessageHeader; + BufferedHeader bufferedHeader = CaptureWriteableHeader(writeableHeader); + headers[headerIndex] = new Header(headers[headerIndex].HeaderKind, bufferedHeader, headers[headerIndex].HeaderProcessing); + collectionVersion++; + return bufferedHeader.GetHeaderReader(); + case HeaderType.BufferedMessageHeader: + return GetBufferedMessageHeaderReader(bufferedMessageData, headerIndex); + default: + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.InvalidEnumValue, headers[headerIndex].HeaderType))); + } + } + + internal UniqueId GetRelatesTo(Uri relationshipType) + { + if (relationshipType == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("relationshipType"); + + UniqueId messageId; + FindRelatesTo(relationshipType, out messageId); + return messageId; + } + + void GetRelatesToValues(int index, out Uri relationshipType, out UniqueId messageId) + { + RelatesToHeader relatesToHeader = headers[index].HeaderInfo as RelatesToHeader; + if (relatesToHeader != null) + { + relationshipType = relatesToHeader.RelationshipType; + messageId = relatesToHeader.UniqueId; + } + else + { + using (XmlDictionaryReader reader = GetReaderAtHeader(index)) + { + RelatesToHeader.ReadHeaderValue(reader, version.Addressing, out relationshipType, out messageId); + } + } + } + + internal string[] GetHeaderAttributes(string localName, string ns) + { + string[] attrs = null; + + if (ContainsOnlyBufferedMessageHeaders) + { + XmlDictionaryReader reader = bufferedMessageData.GetMessageReader(); + reader.ReadStartElement(); // Envelope + reader.ReadStartElement(); // Header + for (int index = 0; reader.IsStartElement(); index++) + { + string value = reader.GetAttribute(localName, ns); + if (value != null) + { + if (attrs == null) + attrs = new string[headerCount]; + attrs[index] = value; + } + if (index == headerCount - 1) + break; + reader.Skip(); + } + reader.Dispose(); + } + else + { + for (int index = 0; index < headerCount; index++) + { + if (headers[index].HeaderType != HeaderType.WriteableHeader) + { + using (XmlDictionaryReader reader = GetReaderAtHeader(index)) + { + string value = reader.GetAttribute(localName, ns); + if (value != null) + { + if (attrs == null) + attrs = new string[headerCount]; + attrs[index] = value; + } + } + } + } + } + + return attrs; + } + + internal MessageHeader GetMessageHeader(int index) + { + if (index < 0 || index >= headerCount) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new ArgumentOutOfRangeException("headerIndex", index, + SR.Format(SR.ValueMustBeInRange, 0, headerCount))); + } + MessageHeader messageHeader; + switch (headers[index].HeaderType) + { + case HeaderType.WriteableHeader: + case HeaderType.ReadableHeader: + return headers[index].MessageHeader; + case HeaderType.BufferedMessageHeader: + messageHeader = CaptureBufferedHeader(bufferedMessageData, headers[index].HeaderInfo, index); + headers[index] = new Header(headers[index].HeaderKind, messageHeader, headers[index].HeaderProcessing); + collectionVersion++; + return messageHeader; + default: + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.InvalidEnumValue, headers[index].HeaderType))); + } + } + + internal Collection GetHeadersNotUnderstood() + { + Collection notUnderstoodHeaders = null; + + for (int headerIndex = 0; headerIndex < headerCount; headerIndex++) + { + if (headers[headerIndex].HeaderProcessing == HeaderProcessing.MustUnderstand) + { + if (notUnderstoodHeaders == null) + notUnderstoodHeaders = new Collection(); + + MessageHeaderInfo headerInfo = headers[headerIndex].HeaderInfo; + //if (DiagnosticUtility.ShouldTraceWarning) + //{ + // TraceUtility.TraceEvent(TraceEventType.Warning, TraceCode.DidNotUnderstandMessageHeader, + // SR.TraceCodeDidNotUnderstandMessageHeader, + // new MessageHeaderInfoTraceRecord(headerInfo), null, null); + //} + + notUnderstoodHeaders.Add(headerInfo); + } + } + + return notUnderstoodHeaders; + } + + public bool HaveMandatoryHeadersBeenUnderstood() + { + return HaveMandatoryHeadersBeenUnderstood(version.Envelope.MustUnderstandActorValues); + } + + public bool HaveMandatoryHeadersBeenUnderstood(params string[] actors) + { + if (actors == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("actors"); + + for (int headerIndex = 0; headerIndex < headerCount; headerIndex++) + { + if (headers[headerIndex].HeaderProcessing == HeaderProcessing.MustUnderstand) + { + for (int actorIndex = 0; actorIndex < actors.Length; ++actorIndex) + { + if (headers[headerIndex].HeaderInfo.Actor == actors[actorIndex]) + { + return false; + } + } + } + } + + return true; + } + + internal void Init(MessageVersion version, int initialSize) + { + nodeCount = 0; + attrCount = 0; + if (initialSize < 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new ArgumentOutOfRangeException(nameof(initialSize), initialSize, + SR.ValueMustBeNonNegative)); + } + + if (version == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("version"); + } + + this.version = version; + headers = new Header[initialSize]; + } + + internal void Init(MessageVersion version) + { + nodeCount = 0; + attrCount = 0; + this.version = version; + collectionVersion = 0; + } + + internal void Init(MessageVersion version, XmlDictionaryReader reader, IBufferedMessageData bufferedMessageData, RecycledMessageState recycledMessageState, bool[] understoodHeaders, bool understoodHeadersModified) + { + nodeCount = 0; + attrCount = 0; + this.version = version; + this.bufferedMessageData = bufferedMessageData; + + if (version.Envelope != EnvelopeVersion.None) + { + this.understoodHeadersModified = (understoodHeaders != null) && understoodHeadersModified; + if (reader.IsEmptyElement) + { + reader.Read(); + return; + } + EnvelopeVersion envelopeVersion = version.Envelope; + Fx.Assert(reader.IsStartElement(XD.MessageDictionary.Header, envelopeVersion.DictionaryNamespace), ""); + reader.ReadStartElement(); + + AddressingDictionary dictionary = XD.AddressingDictionary; + + if (localNames == null) + { + XmlDictionaryString[] strings = new XmlDictionaryString[7]; + strings[(int)HeaderKind.To] = dictionary.To; + strings[(int)HeaderKind.Action] = dictionary.Action; + strings[(int)HeaderKind.MessageId] = dictionary.MessageId; + strings[(int)HeaderKind.RelatesTo] = dictionary.RelatesTo; + strings[(int)HeaderKind.ReplyTo] = dictionary.ReplyTo; + strings[(int)HeaderKind.From] = dictionary.From; + strings[(int)HeaderKind.FaultTo] = dictionary.FaultTo; + Interlocked.MemoryBarrier(); + localNames = strings; + } + + + int i = 0; + while (reader.IsStartElement()) + { + ReadBufferedHeader(reader, recycledMessageState, localNames, (understoodHeaders == null) ? false : understoodHeaders[i++]); + } + + reader.ReadEndElement(); + } + collectionVersion = 0; + } + + public void Insert(int headerIndex, MessageHeader header) + { + if (header == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("header"); + if (!header.IsMessageVersionSupported(version)) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.Format(SR.MessageHeaderVersionNotSupported,header.GetType().FullName, version.Envelope.ToString()), nameof(header))); + Insert(headerIndex, header, GetHeaderKind(header)); + } + + void Insert(int headerIndex, MessageHeader header, HeaderKind kind) + { + ReadableMessageHeader readableMessageHeader = header as ReadableMessageHeader; + HeaderProcessing processing = header.MustUnderstand ? HeaderProcessing.MustUnderstand : 0; + if (kind != HeaderKind.Unknown) + processing |= HeaderProcessing.Understood; + if (readableMessageHeader != null) + InsertHeader(headerIndex, new Header(kind, readableMessageHeader, processing)); + else + InsertHeader(headerIndex, new Header(kind, header, processing)); + } + + void InsertHeader(int headerIndex, Header header) + { + ValidateHeaderKind(header.HeaderKind); + + if (headerIndex < 0 || headerIndex > headerCount) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new ArgumentOutOfRangeException(nameof(headerIndex), headerIndex, + SR.Format(SR.ValueMustBeInRange, 0, headerCount))); + } + + if (headerCount == headers.Length) + { + if (headers.Length == 0) + { + headers = new Header[1]; + } + else + { + Header[] newHeaders = new Header[headers.Length * 2]; + headers.CopyTo(newHeaders, 0); + headers = newHeaders; + } + } + if (headerIndex < headerCount) + { + if (bufferedMessageData != null) + { + for (int i = headerIndex; i < headerCount; i++) + { + if (headers[i].HeaderType == HeaderType.BufferedMessageHeader) + { + CaptureBufferedHeaders(); + break; + } + } + } + Array.Copy(headers, headerIndex, headers, headerIndex + 1, headerCount - headerIndex); + } + headers[headerIndex] = header; + headerCount++; + collectionVersion++; + } + + internal bool IsUnderstood(int i) + { + return (headers[i].HeaderProcessing & HeaderProcessing.Understood) != 0; + } + + internal bool IsUnderstood(MessageHeaderInfo headerInfo) + { + if (headerInfo == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("headerInfo"); + for (int i = 0; i < headerCount; i++) + { + if ((object)headers[i].HeaderInfo == (object)headerInfo) + { + if (IsUnderstood(i)) + return true; + } + } + + return false; + } + + void ReadBufferedHeader(XmlDictionaryReader reader, RecycledMessageState recycledMessageState, XmlDictionaryString[] localNames, bool understood) + { + string actor; + bool mustUnderstand; + bool relay; + bool isRefParam; + + if (version.Addressing == AddressingVersion.None && reader.NamespaceURI == AddressingVersion.None.Namespace) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new InvalidOperationException(SR.Format(SR.AddressingHeadersCannotBeAddedToAddressingVersion, version.Addressing))); + } + + MessageHeader.GetHeaderAttributes(reader, version, out actor, out mustUnderstand, out relay, out isRefParam); + + HeaderKind kind = HeaderKind.Unknown; + MessageHeaderInfo info = null; + + if (version.Envelope.IsUltimateDestinationActor(actor)) + { + Fx.Assert(version.Addressing.DictionaryNamespace != null, "non-None Addressing requires a non-null DictionaryNamespace"); + kind = (HeaderKind)reader.IndexOfLocalName(localNames, version.Addressing.DictionaryNamespace); + switch (kind) + { + case HeaderKind.To: + info = ToHeader.ReadHeader(reader, version.Addressing, recycledMessageState.UriCache, actor, mustUnderstand, relay); + break; + case HeaderKind.Action: + info = ActionHeader.ReadHeader(reader, version.Addressing, actor, mustUnderstand, relay); + break; + case HeaderKind.MessageId: + info = MessageIDHeader.ReadHeader(reader, version.Addressing, actor, mustUnderstand, relay); + break; + case HeaderKind.RelatesTo: + info = RelatesToHeader.ReadHeader(reader, version.Addressing, actor, mustUnderstand, relay); + break; + case HeaderKind.ReplyTo: + info = ReplyToHeader.ReadHeader(reader, version.Addressing, actor, mustUnderstand, relay); + break; + case HeaderKind.From: + info = FromHeader.ReadHeader(reader, version.Addressing, actor, mustUnderstand, relay); + break; + case HeaderKind.FaultTo: + info = FaultToHeader.ReadHeader(reader, version.Addressing, actor, mustUnderstand, relay); + break; + default: + kind = HeaderKind.Unknown; + break; + } + } + + if (info == null) + { + info = recycledMessageState.HeaderInfoCache.TakeHeaderInfo(reader, actor, mustUnderstand, relay, isRefParam); + reader.Skip(); + } + + HeaderProcessing processing = mustUnderstand ? HeaderProcessing.MustUnderstand : 0; + if (kind != HeaderKind.Unknown || understood) + { + processing |= HeaderProcessing.Understood; + MessageHeaders.TraceUnderstood(info); + } + AddHeader(new Header(kind, info, processing)); + } + + internal void Recycle(HeaderInfoCache headerInfoCache) + { + for (int i = 0; i < headerCount; i++) + { + if (headers[i].HeaderKind == HeaderKind.Unknown) + { + headerInfoCache.ReturnHeaderInfo(headers[i].HeaderInfo); + } + } + Clear(); + collectionVersion = 0; + if (understoodHeaders != null) + { + understoodHeaders.Modified = false; + } + } + + internal void RemoveUnderstood(MessageHeaderInfo headerInfo) + { + if (headerInfo == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("headerInfo"); + for (int i = 0; i < headerCount; i++) + { + if ((object)headers[i].HeaderInfo == (object)headerInfo) + { + if ((headers[i].HeaderProcessing & HeaderProcessing.Understood) == 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException( + SR.Format(SR.HeaderAlreadyNotUnderstood, headerInfo.Name, headerInfo.Namespace), nameof(headerInfo))); + } + + headers[i].HeaderProcessing &= ~HeaderProcessing.Understood; + } + } + } + + public void RemoveAll(string name, string ns) + { + if (name == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("name"); + if (ns == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("ns"); + for (int i = headerCount - 1; i >= 0; i--) + { + MessageHeaderInfo info = headers[i].HeaderInfo; + if (info.Name == name && info.Namespace == ns) + { + RemoveAt(i); + } + } + } + + public void RemoveAt(int headerIndex) + { + if (headerIndex < 0 || headerIndex >= headerCount) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new ArgumentOutOfRangeException(nameof(headerIndex), headerIndex, + SR.Format(SR.ValueMustBeInRange, 0, headerCount))); + } + if (bufferedMessageData != null && headers[headerIndex].HeaderType == HeaderType.BufferedMessageHeader) + CaptureBufferedHeaders(headerIndex); + Array.Copy(headers, headerIndex + 1, headers, headerIndex, headerCount - headerIndex - 1); + headers[--headerCount] = new Header(); + collectionVersion++; + } + + internal void ReplaceAt(int headerIndex, MessageHeader header) + { + if (headerIndex < 0 || headerIndex >= headerCount) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new ArgumentOutOfRangeException(nameof(headerIndex), headerIndex, + SR.Format(SR.ValueMustBeInRange, 0, headerCount))); + } + + if (header == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("header"); + } + + ReplaceAt(headerIndex, header, GetHeaderKind(header)); + } + + void ReplaceAt(int headerIndex, MessageHeader header, HeaderKind kind) + { + HeaderProcessing processing = header.MustUnderstand ? HeaderProcessing.MustUnderstand : 0; + if (kind != HeaderKind.Unknown) + processing |= HeaderProcessing.Understood; + ReadableMessageHeader readableMessageHeader = header as ReadableMessageHeader; + if (readableMessageHeader != null) + headers[headerIndex] = new Header(kind, readableMessageHeader, processing); + else + headers[headerIndex] = new Header(kind, header, processing); + collectionVersion++; + } + + public void SetAction(XmlDictionaryString action) + { + if (action == null) + SetHeaderProperty(HeaderKind.Action, null); + else + SetActionHeader(ActionHeader.Create(action, version.Addressing)); + } + + internal void SetActionHeader(ActionHeader actionHeader) + { + SetHeaderProperty(HeaderKind.Action, actionHeader); + } + + internal void SetFaultToHeader(FaultToHeader faultToHeader) + { + SetHeaderProperty(HeaderKind.FaultTo, faultToHeader); + } + + internal void SetFromHeader(FromHeader fromHeader) + { + SetHeaderProperty(HeaderKind.From, fromHeader); + } + + internal void SetMessageIDHeader(MessageIDHeader messageIDHeader) + { + SetHeaderProperty(HeaderKind.MessageId, messageIDHeader); + } + + internal void SetRelatesTo(Uri relationshipType, UniqueId messageId) + { + if (relationshipType == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("relationshipType"); + } + + RelatesToHeader relatesToHeader; + if (!object.ReferenceEquals(messageId, null)) + { + relatesToHeader = RelatesToHeader.Create(messageId, version.Addressing, relationshipType); + } + else + { + relatesToHeader = null; + } + + SetRelatesTo(RelatesToHeader.ReplyRelationshipType, relatesToHeader); + } + + void SetRelatesTo(Uri relationshipType, RelatesToHeader relatesToHeader) + { + UniqueId previousUniqueId; + int index = FindRelatesTo(relationshipType, out previousUniqueId); + if (index >= 0) + { + if (relatesToHeader == null) + { + RemoveAt(index); + } + else + { + ReplaceAt(index, relatesToHeader, HeaderKind.RelatesTo); + } + } + else if (relatesToHeader != null) + { + Add(relatesToHeader, HeaderKind.RelatesTo); + } + } + + internal void SetReplyToHeader(ReplyToHeader replyToHeader) + { + SetHeaderProperty(HeaderKind.ReplyTo, replyToHeader); + } + + internal void SetToHeader(ToHeader toHeader) + { + SetHeaderProperty(HeaderKind.To, toHeader); + } + + void SetHeaderProperty(HeaderKind kind, MessageHeader header) + { + int index = FindHeaderProperty(kind); + if (index >= 0) + { + if (header == null) + { + RemoveAt(index); + } + else + { + ReplaceAt(index, header, kind); + } + } + else if (header != null) + { + Add(header, kind); + } + } + + public void WriteHeader(int headerIndex, XmlWriter writer) + { + WriteHeader(headerIndex, XmlDictionaryWriter.CreateDictionaryWriter(writer)); + } + + public void WriteHeader(int headerIndex, XmlDictionaryWriter writer) + { + WriteStartHeader(headerIndex, writer); + WriteHeaderContents(headerIndex, writer); + writer.WriteEndElement(); + } + + public void WriteStartHeader(int headerIndex, XmlWriter writer) + { + WriteStartHeader(headerIndex, XmlDictionaryWriter.CreateDictionaryWriter(writer)); + } + + public void WriteStartHeader(int headerIndex, XmlDictionaryWriter writer) + { + if (writer == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer"); + } + + if (headerIndex < 0 || headerIndex >= headerCount) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new ArgumentOutOfRangeException(nameof(headerIndex), headerIndex, + SR.Format(SR.ValueMustBeInRange, 0, headerCount))); + } + switch (headers[headerIndex].HeaderType) + { + case HeaderType.ReadableHeader: + case HeaderType.WriteableHeader: + headers[headerIndex].MessageHeader.WriteStartHeader(writer, version); + break; + case HeaderType.BufferedMessageHeader: + WriteStartBufferedMessageHeader(bufferedMessageData, headerIndex, writer); + break; + default: + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.InvalidEnumValue, headers[headerIndex].HeaderType))); + } + } + + public void WriteHeaderContents(int headerIndex, XmlWriter writer) + { + WriteHeaderContents(headerIndex, XmlDictionaryWriter.CreateDictionaryWriter(writer)); + } + + public void WriteHeaderContents(int headerIndex, XmlDictionaryWriter writer) + { + if (writer == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer"); + } + + if (headerIndex < 0 || headerIndex >= headerCount) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new ArgumentOutOfRangeException(nameof(headerIndex), headerIndex, + SR.Format(SR.ValueMustBeInRange, 0, headerCount))); + } + switch (headers[headerIndex].HeaderType) + { + case HeaderType.ReadableHeader: + case HeaderType.WriteableHeader: + headers[headerIndex].MessageHeader.WriteHeaderContents(writer, version); + break; + case HeaderType.BufferedMessageHeader: + WriteBufferedMessageHeaderContents(bufferedMessageData, headerIndex, writer); + break; + default: + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.InvalidEnumValue, headers[headerIndex].HeaderType))); + } + } + + static void TraceUnderstood(MessageHeaderInfo info) + { + //if (DiagnosticUtility.ShouldTraceVerbose) + //{ + // TraceUtility.TraceEvent(TraceEventType.Verbose, TraceCode.UnderstoodMessageHeader, + // SR.TraceCodeUnderstoodMessageHeader, + // new MessageHeaderInfoTraceRecord(info), null, null); + //} + } + + void WriteBufferedMessageHeader(IBufferedMessageData bufferedMessageData, int bufferedMessageHeaderIndex, XmlWriter writer) + { + using (XmlReader reader = GetBufferedMessageHeaderReader(bufferedMessageData, bufferedMessageHeaderIndex)) + { + writer.WriteNode(reader, false); + } + } + + void WriteStartBufferedMessageHeader(IBufferedMessageData bufferedMessageData, int bufferedMessageHeaderIndex, XmlWriter writer) + { + using (XmlReader reader = GetBufferedMessageHeaderReader(bufferedMessageData, bufferedMessageHeaderIndex)) + { + writer.WriteStartElement(reader.Prefix, reader.LocalName, reader.NamespaceURI); + writer.WriteAttributes(reader, false); + } + } + + void WriteBufferedMessageHeaderContents(IBufferedMessageData bufferedMessageData, int bufferedMessageHeaderIndex, XmlWriter writer) + { + using (XmlReader reader = GetBufferedMessageHeaderReader(bufferedMessageData, bufferedMessageHeaderIndex)) + { + if (!reader.IsEmptyElement) + { + reader.ReadStartElement(); + while (reader.NodeType != XmlNodeType.EndElement) + { + writer.WriteNode(reader, false); + } + reader.ReadEndElement(); + } + } + } + + enum HeaderType : byte + { + Invalid, + ReadableHeader, + BufferedMessageHeader, + WriteableHeader + } + + enum HeaderKind : byte + { + Action, + FaultTo, + From, + MessageId, + ReplyTo, + RelatesTo, + To, + Unknown, + } + + [Flags] + enum HeaderProcessing : byte + { + MustUnderstand = 0x1, + Understood = 0x2, + } + + struct Header + { + HeaderType type; + HeaderKind kind; + HeaderProcessing processing; + MessageHeaderInfo info; + + public Header(HeaderKind kind, MessageHeaderInfo info, HeaderProcessing processing) + { + this.kind = kind; + type = HeaderType.BufferedMessageHeader; + this.info = info; + this.processing = processing; + } + + public Header(HeaderKind kind, ReadableMessageHeader readableHeader, HeaderProcessing processing) + { + this.kind = kind; + type = HeaderType.ReadableHeader; + info = readableHeader; + this.processing = processing; + } + + public Header(HeaderKind kind, MessageHeader header, HeaderProcessing processing) + { + this.kind = kind; + type = HeaderType.WriteableHeader; + info = header; + this.processing = processing; + } + + public HeaderType HeaderType + { + get { return type; } + } + + public HeaderKind HeaderKind + { + get { return kind; } + } + + public MessageHeaderInfo HeaderInfo + { + get { return info; } + } + + public MessageHeader MessageHeader + { + get + { + Fx.Assert(type == HeaderType.WriteableHeader || type == HeaderType.ReadableHeader, ""); + return (MessageHeader)info; + } + } + + public HeaderProcessing HeaderProcessing + { + get { return processing; } + set { processing = value; } + } + + public ReadableMessageHeader ReadableHeader + { + get + { + Fx.Assert(type == HeaderType.ReadableHeader, ""); + return (ReadableMessageHeader)info; + } + } + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/MessageProperties.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/MessageProperties.cs new file mode 100644 index 000000000..9be4d777b --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/MessageProperties.cs @@ -0,0 +1,889 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using CoreWCF.Runtime; +using CoreWCF.Security; + +namespace CoreWCF.Channels +{ + public sealed class MessageProperties : IDictionary, IDisposable + { + Property[] properties; + int propertyCount; + MessageEncoder encoder; + Uri via; + object allowOutputBatching; + SecurityMessageProperty security; + bool disposed; + const int InitialPropertyCount = 2; + const int MaxRecycledArrayLength = 8; + const string ViaKey = "Via"; + const string AllowOutputBatchingKey = "AllowOutputBatching"; + const string SecurityKey = "Security"; + const string EncoderKey = "Encoder"; + const int NotFoundIndex = -1; + const int ViaIndex = -2; + const int AllowOutputBatchingIndex = -3; + const int SecurityIndex = -4; + const int EncoderIndex = -5; + static object trueBool = true; + static object falseBool = false; + + public MessageProperties() + { + } + + public MessageProperties(MessageProperties properties) + { + CopyProperties(properties); + } + + internal MessageProperties(KeyValuePair[] array) + { + if (array == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("array"); + CopyProperties(array); + } + + void ThrowDisposed() + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ObjectDisposedException(string.Empty, SR.Format(SR.ObjectDisposed, GetType().ToString()))); + } + + public object this[string name] + { + get + { + if (disposed) + ThrowDisposed(); + + object value; + + if (!TryGetValue(name, out value)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.Format(SR.MessagePropertyNotFound, name))); + } + + return value; + } + set + { + if (disposed) + ThrowDisposed(); + UpdateProperty(name, value, false); + } + } + + internal bool CanRecycle + { + get + { + return properties == null || properties.Length <= MaxRecycledArrayLength; + } + } + + public int Count + { + get + { + if (disposed) + ThrowDisposed(); + return propertyCount; + } + } + + public MessageEncoder Encoder + { + get + { + if (disposed) + ThrowDisposed(); + return encoder; + } + set + { + if (disposed) + ThrowDisposed(); + AdjustPropertyCount((object)encoder == null, (object)value == null); + encoder = value; + } + } + + public bool AllowOutputBatching + { + get + { + if (disposed) + ThrowDisposed(); + return (object)allowOutputBatching == trueBool; + } + set + { + if (disposed) + ThrowDisposed(); + AdjustPropertyCount((object)allowOutputBatching == null, false); + + if (value) + { + allowOutputBatching = trueBool; + } + else + { + allowOutputBatching = falseBool; + } + } + } + + public bool IsFixedSize + { + get + { + if (disposed) + ThrowDisposed(); + return false; + } + } + + public bool IsReadOnly + { + get + { + if (disposed) + ThrowDisposed(); + return false; + } + } + + public ICollection Keys + { + get + { + if (disposed) + ThrowDisposed(); + List keys = new List(); + + if ((object)via != null) + { + keys.Add(ViaKey); + } + + if ((object)allowOutputBatching != null) + { + keys.Add(AllowOutputBatchingKey); + } + + //if ((object)security != null) + //{ + // keys.Add(SecurityKey); + //} + + if ((object)encoder != null) + { + keys.Add(EncoderKey); + } + + if (properties != null) + { + for (int i = 0; i < properties.Length; i++) + { + string propertyName = properties[i].Name; + + if (propertyName == null) + { + break; + } + + keys.Add(propertyName); + } + } + + return keys; + } + } + + public SecurityMessageProperty Security + { + get + { + if (disposed) + ThrowDisposed(); + return security; + } + set + { + if (disposed) + ThrowDisposed(); + AdjustPropertyCount((object)security == null, (object)value == null); + security = value; + } + } + + public ICollection Values + { + get + { + if (disposed) + ThrowDisposed(); + List values = new List(); + + if ((object)via != null) + { + values.Add(via); + } + + if ((object)allowOutputBatching != null) + { + values.Add(allowOutputBatching); + } + + //if ((object)security != null) + //{ + // values.Add(security); + //} + + if ((object)encoder != null) + { + values.Add(encoder); + } + if (properties != null) + { + for (int i = 0; i < properties.Length; i++) + { + if (properties[i].Name == null) + { + break; + } + + values.Add(properties[i].Value); + } + } + + return values; + } + } + + public Uri Via + { + get + { + if (disposed) + ThrowDisposed(); + return via; + } + set + { + if (disposed) + ThrowDisposed(); + AdjustPropertyCount((object)via == null, (object)value == null); + via = value; + } + } + + public void Add(string name, object property) + { + if (disposed) + ThrowDisposed(); + + if (property == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("property"); + UpdateProperty(name, property, true); + } + + void AdjustPropertyCount(bool oldValueIsNull, bool newValueIsNull) + { + if (newValueIsNull) + { + if (!oldValueIsNull) + { + propertyCount--; + } + } + else + { + if (oldValueIsNull) + { + propertyCount++; + } + } + } + + public void Clear() + { + if (disposed) + ThrowDisposed(); + + if (properties != null) + { + for (int i = 0; i < properties.Length; i++) + { + if (properties[i].Name == null) + { + break; + } + + properties[i] = new Property(); + } + } + + via = null; + allowOutputBatching = null; + //security = null; + encoder = null; + propertyCount = 0; + } + + public void CopyProperties(MessageProperties properties) + { + // CopyProperties behavior should be equivalent to the behavior + // of MergeProperties except that Merge supports property values that + // implement the IMergeEnabledMessageProperty. Any changes to CopyProperties + // should be reflected in MergeProperties as well. + if (properties == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("properties"); + } + + if (disposed) + { + ThrowDisposed(); + } + + if (properties.properties != null) + { + for (int i = 0; i < properties.properties.Length; i++) + { + if (properties.properties[i].Name == null) + { + break; + } + + Property property = properties.properties[i]; + + // this[string] will call CreateCopyOfPropertyValue, so we don't need to repeat that here + this[property.Name] = property.Value; + } + } + + Via = properties.Via; + AllowOutputBatching = properties.AllowOutputBatching; + //this.Security = (properties.Security != null) ? (SecurityMessageProperty)properties.Security.CreateCopy() : null; + Encoder = properties.Encoder; + } + + internal void MergeProperties(MessageProperties properties) + { + // MergeProperties behavior should be equivalent to the behavior + // of CopyProperties except that Merge supports property values that + // implement the IMergeEnabledMessageProperty. Any changes to CopyProperties + // should be reflected in MergeProperties as well. + if (properties == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("properties"); + } + + if (disposed) + { + ThrowDisposed(); + } + + if (properties.properties != null) + { + for (int i = 0; i < properties.properties.Length; i++) + { + if (properties.properties[i].Name == null) + { + break; + } + + Property property = properties.properties[i]; + + // Used with Http transport + //IMergeEnabledMessageProperty currentValue; + //if (!this.TryGetValue(property.Name, out currentValue) || + // !currentValue.TryMergeWithProperty(property.Value)) + //{ + // Merge wasn't possible so copy + // this[string] will call CreateCopyOfPropertyValue, so we don't need to repeat that here + this[property.Name] = property.Value; + //} + } + } + + Via = properties.Via; + AllowOutputBatching = properties.AllowOutputBatching; + //this.Security = (properties.Security != null) ? (SecurityMessageProperty)properties.Security.CreateCopy() : null; + Encoder = properties.Encoder; + } + + internal void CopyProperties(KeyValuePair[] array) + { + if (disposed) + { + ThrowDisposed(); + } + + for (int i = 0; i < array.Length; i++) + { + KeyValuePair property = array[i]; + + // this[string] will call CreateCopyOfPropertyValue, so we don't need to repeat that here + this[property.Key] = property.Value; + } + } + + public bool ContainsKey(string name) + { + if (disposed) + ThrowDisposed(); + + if (name == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("name"); + int index = FindProperty(name); + switch (index) + { + case ViaIndex: + return (object)via != null; + case AllowOutputBatchingIndex: + return (object)allowOutputBatching != null; + //case SecurityIndex: + // return (object)security != null; + case EncoderIndex: + return (object)encoder != null; + case NotFoundIndex: + return false; + default: + return true; + } + } + + object CreateCopyOfPropertyValue(object propertyValue) + { + IMessageProperty messageProperty = propertyValue as IMessageProperty; + if (messageProperty == null) + return propertyValue; + object copy = messageProperty.CreateCopy(); + if (copy == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.MessagePropertyReturnedNullCopy)); + return copy; + } + + public void Dispose() + { + if (disposed) + return; + + disposed = true; + + if (properties != null) + { + for (int i = 0; i < properties.Length; i++) + { + if (properties[i].Name == null) + { + break; + } + + properties[i].Dispose(); + } + } + + //if (this.security != null) + //{ + // this.security.Dispose(); + //} + } + + int FindProperty(string name) + { + if (name == ViaKey) + return ViaIndex; + else if (name == AllowOutputBatchingKey) + return AllowOutputBatchingIndex; + else if (name == EncoderKey) + return EncoderIndex; + else if (name == SecurityKey) + return SecurityIndex; + + if (properties != null) + { + for (int i = 0; i < properties.Length; i++) + { + string propertyName = properties[i].Name; + + if (propertyName == null) + { + break; + } + + if (propertyName == name) + { + return i; + } + } + } + + return NotFoundIndex; + } + + internal void Recycle() + { + disposed = false; + Clear(); + } + + public bool Remove(string name) + { + if (disposed) + ThrowDisposed(); + + int originalPropertyCount = propertyCount; + UpdateProperty(name, null, false); + return originalPropertyCount != propertyCount; + } + + public bool TryGetValue(string name, out object value) + { + if (disposed) + ThrowDisposed(); + + if (name == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("name"); + + int index = FindProperty(name); + switch (index) + { + case ViaIndex: + value = via; + break; + case AllowOutputBatchingIndex: + value = allowOutputBatching; + break; + //case SecurityIndex: + // value = security; + // break; + case EncoderIndex: + value = encoder; + break; + case NotFoundIndex: + value = null; + break; + default: + value = properties[index].Value; + break; + } + + return value != null; + } + + internal bool TryGetValue(string name, out TProperty property) + { + object o; + if (TryGetValue(name, out o)) + { + property = (TProperty)o; + return true; + } + else + { + property = default(TProperty); + return false; + } + } + + internal TProperty GetValue(string name) where TProperty : class + { + return GetValue(name, false); + } + + internal TProperty GetValue(string name, bool ensureTypeMatch) where TProperty : class + { + object obj; + if (!TryGetValue(name, out obj)) + { + return null; + } + + return ensureTypeMatch ? (TProperty)obj : obj as TProperty; + } + + void UpdateProperty(string name, object value, bool mustNotExist) + { + if (name == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("name"); + int index = FindProperty(name); + if (index != NotFoundIndex) + { + if (mustNotExist) + { + bool exists; + switch (index) + { + case ViaIndex: + exists = (object)via != null; + break; + case AllowOutputBatchingIndex: + exists = (object)allowOutputBatching != null; + break; + //case SecurityIndex: + // exists = (object)security != null; + // break; + case EncoderIndex: + exists = (object)encoder != null; + break; + default: + exists = true; + break; + } + if (exists) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.Format(SR.DuplicateMessageProperty, name))); + } + } + + if (index >= 0) + { + if (value == null) + { + properties[index].Dispose(); + int shiftIndex; + for (shiftIndex = index + 1; shiftIndex < properties.Length; shiftIndex++) + { + if (properties[shiftIndex].Name == null) + { + break; + } + + properties[shiftIndex - 1] = properties[shiftIndex]; + } + properties[shiftIndex - 1] = new Property(); + propertyCount--; + } + else + { + properties[index].Value = CreateCopyOfPropertyValue(value); + } + } + else + { + switch (index) + { + case ViaIndex: + Via = (Uri)value; + break; + case AllowOutputBatchingIndex: + AllowOutputBatching = (bool)value; + break; + //case SecurityIndex: + // if (Security != null) + // Security.Dispose(); + // Security = (SecurityMessageProperty)CreateCopyOfPropertyValue(value); + // break; + case EncoderIndex: + Encoder = (MessageEncoder)value; + break; + default: + Fx.Assert(""); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException()); + } + } + } + else if (value != null) + { + int newIndex; + + if (properties == null) + { + properties = new Property[InitialPropertyCount]; + newIndex = 0; + } + else + { + for (newIndex = 0; newIndex < properties.Length; newIndex++) + { + if (properties[newIndex].Name == null) + { + break; + } + } + + if (newIndex == properties.Length) + { + Property[] newProperties = new Property[properties.Length * 2]; + Array.Copy(properties, newProperties, properties.Length); + properties = newProperties; + } + } + + object newValue = CreateCopyOfPropertyValue(value); + properties[newIndex] = new Property(name, newValue); + propertyCount++; + } + } + + void ICollection>.CopyTo(KeyValuePair[] array, int index) + { + if (disposed) + ThrowDisposed(); + + if (array == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("array"); + if (array.Length < propertyCount) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.MessagePropertiesArraySize0)); + if (index < 0 || index > array.Length - propertyCount) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(index), index, + SR.Format(SR.ValueMustBeInRange, 0, array.Length - propertyCount))); + + if (via != null) + array[index++] = new KeyValuePair(ViaKey, via); + + if (allowOutputBatching != null) + array[index++] = new KeyValuePair(AllowOutputBatchingKey, allowOutputBatching); + + //if (this.security != null) + // array[index++] = new KeyValuePair(SecurityKey, this.security.CreateCopy()); + + if (encoder != null) + array[index++] = new KeyValuePair(EncoderKey, encoder); + + if (properties != null) + { + for (int i = 0; i < properties.Length; i++) + { + string propertyName = properties[i].Name; + + if (propertyName == null) + { + break; + } + + array[index++] = new KeyValuePair(propertyName, CreateCopyOfPropertyValue(properties[i].Value)); + } + } + } + + void ICollection>.Add(KeyValuePair pair) + { + if (disposed) + ThrowDisposed(); + + if (pair.Value == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("pair.Value"); + UpdateProperty(pair.Key, pair.Value, true); + } + + bool ICollection>.Contains(KeyValuePair pair) + { + if (disposed) + ThrowDisposed(); + + if (pair.Value == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("pair.Value"); + if (pair.Key == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("pair.Key"); + object value; + if (!TryGetValue(pair.Key, out value)) + { + return false; + } + return value.Equals(pair.Value); + } + + IEnumerator IEnumerable.GetEnumerator() + { + if (disposed) + ThrowDisposed(); + + return ((IEnumerable>)this).GetEnumerator(); + } + + IEnumerator> IEnumerable>.GetEnumerator() + { + if (disposed) + ThrowDisposed(); + + List> pairs = new List>(propertyCount); + + if (via != null) + pairs.Add(new KeyValuePair(ViaKey, via)); + + if (allowOutputBatching != null) + pairs.Add(new KeyValuePair(AllowOutputBatchingKey, allowOutputBatching)); + + //if (this.security != null) + // pairs.Add(new KeyValuePair(SecurityKey, security)); + + if (encoder != null) + pairs.Add(new KeyValuePair(EncoderKey, encoder)); + + if (properties != null) + { + for (int i = 0; i < properties.Length; i++) + { + string propertyName = properties[i].Name; + + if (propertyName == null) + { + break; + } + + pairs.Add(new KeyValuePair(propertyName, properties[i].Value)); + } + } + + return pairs.GetEnumerator(); + } + + bool ICollection>.Remove(KeyValuePair pair) + { + if (disposed) + ThrowDisposed(); + + if (pair.Value == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("pair.Value"); + if (pair.Key == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("pair.Key"); + + object value; + if (!TryGetValue(pair.Key, out value)) + { + return false; + } + if (!value.Equals(pair.Value)) + { + return false; + } + Remove(pair.Key); + return true; + } + + struct Property : IDisposable + { + string name; + object value; + + public Property(string name, object value) + { + this.name = name; + this.value = value; + } + + public string Name + { + get { return name; } + } + + public object Value + { + get { return value; } + set { this.value = value; } + } + + public void Dispose() + { + IDisposable disposable = value as IDisposable; + if (disposable != null) + disposable.Dispose(); + } + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/MessageState.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/MessageState.cs new file mode 100644 index 000000000..bfbfe8c3b --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/MessageState.cs @@ -0,0 +1,11 @@ +namespace CoreWCF.Channels +{ + public enum MessageState + { + Closed = 4, + Copied = 3, + Created = 0, + Read = 1, + Written = 2, + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/MessageVersion.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/MessageVersion.cs new file mode 100644 index 000000000..14032d446 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/MessageVersion.cs @@ -0,0 +1,156 @@ +using System; +using System.Globalization; +using CoreWCF.Runtime; + +namespace CoreWCF.Channels +{ + public sealed class MessageVersion + { + EnvelopeVersion envelope; + AddressingVersion addressing; + static MessageVersion none; + static MessageVersion soap11; + static MessageVersion soap12Addressing10; + + static MessageVersion() + { + none = new MessageVersion(EnvelopeVersion.None, AddressingVersion.None); + soap11 = new MessageVersion(EnvelopeVersion.Soap11, AddressingVersion.None); + soap12Addressing10 = new MessageVersion(EnvelopeVersion.Soap12, AddressingVersion.WSAddressing10); + } + + private MessageVersion(EnvelopeVersion envelopeVersion, AddressingVersion addressingVersion) + { + envelope = envelopeVersion; + addressing = addressingVersion; + } + + public static MessageVersion CreateVersion(EnvelopeVersion envelopeVersion) + { + return CreateVersion(envelopeVersion, AddressingVersion.WSAddressing10); + } + + public static MessageVersion CreateVersion(EnvelopeVersion envelopeVersion, AddressingVersion addressingVersion) + { + if (envelopeVersion == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("envelopeVersion"); + } + + if (addressingVersion == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("addressingVersion"); + } + + if (envelopeVersion == EnvelopeVersion.Soap12) + { + if (addressingVersion == AddressingVersion.WSAddressing10) + { + return soap12Addressing10; + } + else + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("addressingVersion", + SR.Format(SR.AddressingVersionNotSupported, addressingVersion)); + } + } + else if (envelopeVersion == EnvelopeVersion.Soap11) + { + if (addressingVersion == AddressingVersion.None) + { + return soap11; + } + else + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("addressingVersion", + SR.Format(SR.AddressingVersionNotSupported, addressingVersion)); + } + } + else if (envelopeVersion == EnvelopeVersion.None) + { + if (addressingVersion == AddressingVersion.None) + { + return none; + } + else + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("addressingVersion", + SR.Format(SR.AddressingVersionNotSupported, addressingVersion)); + } + } + else + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("envelopeVersion", + SR.Format(SR.EnvelopeVersionNotSupported, envelopeVersion)); + } + } + + public AddressingVersion Addressing + { + get { return addressing; } + } + + public static MessageVersion Default + { + get { return soap12Addressing10; } + } + + public EnvelopeVersion Envelope + { + get { return envelope; } + } + + public override bool Equals(object obj) + { + return this == obj; + } + + public override int GetHashCode() + { + int code = 0; + if (Envelope == EnvelopeVersion.Soap11) + code += 1; + return code; + } + + public static MessageVersion None + { + get { return none; } + } + + public static MessageVersion Soap12WSAddressing10 + { + get { return soap12Addressing10; } + } + + public static MessageVersion Soap11 + { + get { return soap11; } + } + + public override string ToString() + { + return SR.Format(SR.MessageVersionToStringFormat, envelope.ToString(), addressing.ToString()); + } + + internal bool IsMatch(MessageVersion messageVersion) + { + if (messageVersion == null) + { + Fx.Assert("Invalid (null) messageVersion value"); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("messageVersion"); + } + if (addressing == null) + { + Fx.Assert("Invalid (null) addressing value"); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "MessageVersion.Addressing cannot be null"))); + } + + if (envelope != messageVersion.Envelope) + return false; + if (addressing.Namespace != messageVersion.Addressing.Namespace) + return false; + return true; + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/MethodCall.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/MethodCall.cs new file mode 100644 index 000000000..e302374e7 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/MethodCall.cs @@ -0,0 +1,25 @@ +using System.Diagnostics.Contracts; +using System.Reflection; + +namespace CoreWCF.Channels +{ + // MethodCall associates a MethodBase with the arguments to pass to it. + internal class MethodCall + { + public MethodCall(object[] args) + { + Contract.Assert(args != null); + Args = args; + } + + public MethodCall(MethodBase methodBase, object[] args) : this(args) + { + Contract.Assert(methodBase != null); + MethodBase = methodBase; + } + + public MethodBase MethodBase { get; private set; } + + public object[] Args { get; private set; } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/ReceiveContext.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/ReceiveContext.cs new file mode 100644 index 000000000..505ace0ac --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/ReceiveContext.cs @@ -0,0 +1,487 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using CoreWCF.Runtime; + +namespace CoreWCF.Channels +{ + internal abstract class ReceiveContext + { + public readonly static string Name = "ReceiveContext"; + SemaphoreSlim stateLock; // protects state that may be reverted + bool contextFaulted; + object thisLock; + //EventTraceActivity eventTraceActivity; + + protected ReceiveContext() + { + thisLock = new object(); + State = ReceiveContextState.Received; + stateLock = new SemaphoreSlim(1); + } + + public ReceiveContextState State + { + get; + protected set; + } + + protected object ThisLock + { + get { return thisLock; } + } + + public event EventHandler Faulted; + + public static bool TryGet(Message message, out ReceiveContext property) + { + if (message == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("message"); + } + + bool result = TryGet(message.Properties, out property); + //if (result && FxTrace.Trace.IsEnd2EndActivityTracingEnabled && property.eventTraceActivity == null) + //{ + // property.eventTraceActivity = EventTraceActivityHelper.TryExtractActivity(message); + //} + + return result; + } + + public static bool TryGet(MessageProperties properties, out ReceiveContext property) + { + if (properties == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("properties"); + } + + property = null; + object foundProperty; + if (properties.TryGetValue(Name, out foundProperty)) + { + property = (ReceiveContext)foundProperty; + return true; + } + return false; + } + + public virtual void Abandon(TimeSpan timeout) + { + Abandon(null, timeout); + } + + public virtual void Abandon(Exception exception, TimeSpan timeout) + { + EnsureValidTimeout(timeout); + TimeoutHelper timeoutHelper = new TimeoutHelper(timeout); + WaitForStateLock(timeoutHelper.RemainingTime()); + + try + { + if (PreAbandon()) + { + return; + } + } + finally + { + // Abandon can never be reverted, release the state lock. + ReleaseStateLock(); + } + + bool success = false; + try + { + if (exception == null) + { + OnAbandon(timeoutHelper.RemainingTime()); + } + else + { + //if (TD.ReceiveContextAbandonWithExceptionIsEnabled()) + //{ + // TD.ReceiveContextAbandonWithException(this.eventTraceActivity, this.GetType().ToString(), exception.GetType().ToString()); + //} + OnAbandon(exception, timeoutHelper.RemainingTime()); + } + lock (ThisLock) + { + ThrowIfFaulted(); + ThrowIfNotAbandoning(); + State = ReceiveContextState.Abandoned; + } + success = true; + } + finally + { + if (!success) + { + //if (TD.ReceiveContextAbandonFailedIsEnabled()) + //{ + // TD.ReceiveContextAbandonFailed(this.eventTraceActivity, this.GetType().ToString()); + //} + Fault(); + } + } + } + + public virtual Task AbandonAsync(CancellationToken token) + { + return AbandonAsync(null, token); + } + + public virtual async Task AbandonAsync(Exception exception, CancellationToken token) + { + await WaitForStateLockAsync(token); + + try + { + if (PreAbandon()) + { + return; + } + } + finally + { + // Abandon can never be reverted, release the state lock. + ReleaseStateLock(); + } + + bool success = false; + try + { + if (exception == null) + { + await OnAbandonAsync(token); + } + else + { + //if (TD.ReceiveContextAbandonWithExceptionIsEnabled()) + //{ + // TD.ReceiveContextAbandonWithException(this.eventTraceActivity, this.GetType().ToString(), exception.GetType().ToString()); + //} + await OnAbandonAsync(exception, token); + } + lock (ThisLock) + { + ThrowIfFaulted(); + ThrowIfNotAbandoning(); + State = ReceiveContextState.Abandoned; + } + success = true; + } + finally + { + if (!success) + { + //if (TD.ReceiveContextAbandonFailedIsEnabled()) + //{ + // TD.ReceiveContextAbandonFailed(this.eventTraceActivity, this.GetType().ToString()); + //} + Fault(); + } + } + } + + public virtual async Task CompleteAsync(CancellationToken token) + { + await WaitForStateLockAsync(token); + bool success = false; + + try + { + PreComplete(); + success = true; + } + finally + { + // Case 1: State validation fails, release the lock. + // Case 2: No trasaction, the state can never be reverted, release the lock. + // Case 3: Transaction, keep the lock until we know the transaction outcome (OnTransactionStatusNotification). + if (!success /*|| Transaction.Current == null*/) + { + ReleaseStateLock(); + } + } + + success = false; + try + { + await OnCompleteAsync(token); + lock (ThisLock) + { + ThrowIfFaulted(); + ThrowIfNotCompleting(); + State = ReceiveContextState.Completed; + } + success = true; + } + finally + { + if (!success) + { + //if (TD.ReceiveContextCompleteFailedIsEnabled()) + //{ + // TD.ReceiveContextCompleteFailed(this.eventTraceActivity, this.GetType().ToString()); + //} + Fault(); + } + } + + } + + public virtual void Complete(TimeSpan timeout) + { + EnsureValidTimeout(timeout); + TimeoutHelper timeoutHelper = new TimeoutHelper(timeout); + WaitForStateLock(timeoutHelper.RemainingTime()); + bool success = false; + + try + { + PreComplete(); + success = true; + } + finally + { + // Case 1: State validation fails, release the lock. + // Case 2: No trasaction, the state can never be reverted, release the lock. + // Case 3: Transaction, keep the lock until we know the transaction outcome (OnTransactionStatusNotification). + if (!success /*|| Transaction.Current == null*/) + { + ReleaseStateLock(); + } + } + + success = false; + try + { + OnComplete(timeoutHelper.RemainingTime()); + lock (ThisLock) + { + ThrowIfFaulted(); + ThrowIfNotCompleting(); + State = ReceiveContextState.Completed; + } + success = true; + } + finally + { + if (!success) + { + //if (TD.ReceiveContextCompleteFailedIsEnabled()) + //{ + // TD.ReceiveContextCompleteFailed(this.eventTraceActivity, this.GetType().ToString()); + //} + Fault(); + } + } + } + + void EnsureValidTimeout(TimeSpan timeout) + { + if (timeout < TimeSpan.Zero) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new ArgumentOutOfRangeException(nameof(timeout), SR.SFxTimeoutOutOfRange0)); + } + + if (TimeoutHelper.IsTooLarge(timeout)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new ArgumentOutOfRangeException(nameof(timeout), timeout, SR.SFxTimeoutOutOfRangeTooBig)); + } + } + + protected internal virtual void Fault() + { + lock (ThisLock) + { + if (State == ReceiveContextState.Completed || State == ReceiveContextState.Abandoned || State == ReceiveContextState.Faulted) + { + return; + } + State = ReceiveContextState.Faulted; + } + OnFaulted(); + } + + protected abstract void OnAbandon(TimeSpan timeout); + protected virtual void OnAbandon(Exception exception, TimeSpan timeout) + { + // default implementation: delegate to non-exception overload, ignoring reason + OnAbandon(timeout); + } + + protected abstract Task OnAbandonAsync(CancellationToken token); + + protected virtual Task OnAbandonAsync(Exception exception, CancellationToken token) + { + // default implementation: delegate to non-exception overload, ignoring reason + return OnAbandonAsync(token); + } + + protected abstract Task OnCompleteAsync(CancellationToken token); + protected abstract void OnComplete(TimeSpan timeout); + + protected virtual void OnFaulted() + { + lock (ThisLock) + { + if (contextFaulted) + { + return; + } + contextFaulted = true; + } + + //if (TD.ReceiveContextFaultedIsEnabled()) + //{ + // TD.ReceiveContextFaulted(this.eventTraceActivity, this); + //} + + EventHandler handler = Faulted; + + if (handler != null) + { + try + { + handler(this, EventArgs.Empty); + } + catch (Exception exception) + { + if (Fx.IsFatal(exception)) + { + throw; + } + + throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(exception); + } + } + } + + //void OnTransactionStatusNotification(TransactionStatus status) + //{ + // lock (ThisLock) + // { + // if (status == TransactionStatus.Aborted) + // { + // if (this.State == ReceiveContextState.Completing || this.State == ReceiveContextState.Completed) + // { + // this.State = ReceiveContextState.Received; + // } + // } + // } + + // if (status != TransactionStatus.Active) + // { + // this.ReleaseStateLock(); + // } + //} + + bool PreAbandon() + { + bool alreadyAbandoned = false; + lock (ThisLock) + { + if (State == ReceiveContextState.Abandoning || State == ReceiveContextState.Abandoned) + { + alreadyAbandoned = true; + } + else + { + ThrowIfFaulted(); + ThrowIfNotReceived(); + State = ReceiveContextState.Abandoning; + } + } + return alreadyAbandoned; + } + + void PreComplete() + { + lock (ThisLock) + { + ThrowIfFaulted(); + ThrowIfNotReceived(); + //if (Transaction.Current != null) + //{ + // Transaction.Current.EnlistVolatile(new EnlistmentNotifications(this), EnlistmentOptions.None); + //} + State = ReceiveContextState.Completing; + } + } + + void ReleaseStateLock() + { + stateLock.Release(); + } + + void ThrowIfFaulted() + { + + if (State == ReceiveContextState.Faulted) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new CommunicationException(SR.Format(SR.ReceiveContextFaulted, GetType().ToString()))); + } + } + + void ThrowIfNotAbandoning() + { + if (State != ReceiveContextState.Abandoning) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new InvalidOperationException(SR.Format(SR.ReceiveContextInInvalidState, GetType().ToString(), State.ToString()))); + } + } + + void ThrowIfNotCompleting() + { + if (State != ReceiveContextState.Completing) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new InvalidOperationException(SR.Format(SR.ReceiveContextInInvalidState, GetType().ToString(), State.ToString()))); + } + } + + void ThrowIfNotReceived() + { + if (State != ReceiveContextState.Received) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new InvalidOperationException(SR.Format(SR.ReceiveContextCannotBeUsed, GetType().ToString(), State.ToString()))); + } + } + + async Task WaitForStateLockAsync(CancellationToken token) + { + try + { + await stateLock.WaitAsync(token); + } + catch (TaskCanceledException exception) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(WrapStateException(exception)); + } + } + + void WaitForStateLock(TimeSpan timeout) + { + try + { + stateLock.Wait(timeout); + } + catch (TimeoutException exception) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(WrapStateException(exception)); + } + } + + Exception WrapStateException(Exception exception) + { + return new InvalidOperationException(SR.Format(SR.ReceiveContextInInvalidState, GetType().ToString(), State.ToString()), exception); + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/ReceiveContextState.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/ReceiveContextState.cs new file mode 100644 index 000000000..916a15676 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/ReceiveContextState.cs @@ -0,0 +1,12 @@ +namespace CoreWCF.Channels +{ + public enum ReceiveContextState + { + Received, + Completing, + Completed, + Abandoning, + Abandoned, + Faulted + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/RemoteEndpointMessageProperty.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/RemoteEndpointMessageProperty.cs new file mode 100644 index 000000000..82895002c --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/RemoteEndpointMessageProperty.cs @@ -0,0 +1,130 @@ +using System; +using System.Net; + +namespace CoreWCF.Channels +{ + public sealed class RemoteEndpointMessageProperty + { + string address; + int port; + IPEndPoint remoteEndPoint; + IRemoteEndpointProvider remoteEndpointProvider; + InitializationState state; + object thisLock = new object(); + + public RemoteEndpointMessageProperty(string address, int port) + { + if (string.IsNullOrEmpty(address)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("address"); + } + + if (port < IPEndPoint.MinPort || port > IPEndPoint.MaxPort) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("port", + SR.Format(SR.ValueMustBeInRange, IPEndPoint.MinPort, IPEndPoint.MaxPort)); + } + + this.port = port; + this.address = address; + state = InitializationState.All; + } + + internal RemoteEndpointMessageProperty(IRemoteEndpointProvider remoteEndpointProvider) + { + this.remoteEndpointProvider = remoteEndpointProvider; + } + + public RemoteEndpointMessageProperty(IPEndPoint remoteEndPoint) + { + this.remoteEndPoint = remoteEndPoint; + } + + public static string Name + { + get { return typeof(RemoteEndpointMessageProperty).FullName; } + } + + public string Address + { + get + { + if ((state & InitializationState.Address) != InitializationState.Address) + { + lock (ThisLock) + { + if ((state & InitializationState.Address) != InitializationState.Address) + { + Initialize(false); + } + } + } + return address; + } + } + + public int Port + { + get + { + if ((state & InitializationState.Port) != InitializationState.Port) + { + lock (ThisLock) + { + if ((state & InitializationState.Port) != InitializationState.Port) + { + Initialize(true); + } + } + } + return port; + } + } + + object ThisLock + { + get { return thisLock; } + } + + void Initialize(bool getHostedPort) + { + if (remoteEndPoint != null) + { + address = remoteEndPoint.Address.ToString(); + port = remoteEndPoint.Port; + state = InitializationState.All; + remoteEndPoint = null; + } + else + { + if ((state & InitializationState.Address) != InitializationState.Address) + { + address = remoteEndpointProvider.GetAddress(); + state |= InitializationState.Address; + } + + if (getHostedPort) + { + port = remoteEndpointProvider.GetPort(); + state |= InitializationState.Port; + remoteEndpointProvider = null; + } + } + } + + internal interface IRemoteEndpointProvider + { + string GetAddress(); + int GetPort(); + } + + [Flags] + enum InitializationState + { + None = 0, + Address = 1, + Port = 2, + All = 3 + } + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/RequestContext.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/RequestContext.cs new file mode 100644 index 000000000..2ae1ee3fa --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/RequestContext.cs @@ -0,0 +1,22 @@ +using CoreWCF.Dispatcher; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace CoreWCF.Channels +{ + public abstract class RequestContext : IDisposable + { + public abstract Message RequestMessage { get; } + public abstract void Abort(); + public abstract Task ReplyAsync(Message message); + public abstract Task ReplyAsync(Message message, CancellationToken token); + public abstract Task CloseAsync(); + public abstract Task CloseAsync(CancellationToken token); + protected virtual void Dispose(bool disposing) { } + void IDisposable.Dispose() + { + Dispose(true); + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/RequestReplyCorrelator.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/RequestReplyCorrelator.cs new file mode 100644 index 000000000..ed9b59fa1 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/RequestReplyCorrelator.cs @@ -0,0 +1,242 @@ +using System; +using System.Collections.Generic; +using System.Xml; +using CoreWCF.Runtime; +using CoreWCF.Diagnostics; + +namespace CoreWCF.Channels +{ + internal class RequestReplyCorrelator : IRequestReplyCorrelator + { + IDictionary states; + + internal RequestReplyCorrelator() + { + states = new Dictionary(); + } + + void IRequestReplyCorrelator.Add(Message request, T state) + { + UniqueId messageId = request.Headers.MessageId; + Type stateType = typeof(T); + Key key = new Key(messageId, stateType); + + // add the correlator key to the request, this will be needed for cleaning up the correlator table in case of + // channel aborting or faulting while there are pending requests + ICorrelatorKey value = state as ICorrelatorKey; + if (value != null) + { + value.RequestCorrelatorKey = key; + } + + lock (states) + { + states.Add(key, state); + } + } + + T IRequestReplyCorrelator.Find(Message reply, bool remove) + { + UniqueId relatesTo = GetRelatesTo(reply); + Type stateType = typeof(T); + Key key = new Key(relatesTo, stateType); + T value; + + lock (states) + { + value = (T)states[key]; + + if (remove) + states.Remove(key); + } + + return value; + } + + // This method is used to remove the request from the correlator table when the + // reply is lost. This will avoid leaking the correlator table in cases where the + // channel faults or aborts while there are pending requests. + internal void RemoveRequest(ICorrelatorKey request) + { + Fx.Assert(request != null, "request cannot be null"); + if (request.RequestCorrelatorKey != null) + { + lock (states) + { + states.Remove(request.RequestCorrelatorKey); + } + } + } + + UniqueId GetRelatesTo(Message reply) + { + UniqueId relatesTo = reply.Headers.RelatesTo; + if (relatesTo == null) + throw TraceUtility.ThrowHelperError(new ArgumentException(SR.SuppliedMessageIsNotAReplyItHasNoRelatesTo0), reply); + return relatesTo; + } + + internal static bool AddressReply(Message reply, Message request) + { + ReplyToInfo info = RequestReplyCorrelator.ExtractReplyToInfo(request); + return RequestReplyCorrelator.AddressReply(reply, info); + } + + internal static bool AddressReply(Message reply, ReplyToInfo info) + { + EndpointAddress destination = null; + + if (info.HasFaultTo && (reply.IsFault)) + { + destination = info.FaultTo; + } + else if (info.HasReplyTo) + { + destination = info.ReplyTo; + } + + if (destination != null) + { + destination.ApplyTo(reply); + return !destination.IsNone; + } + else + { + return true; + } + } + + internal static ReplyToInfo ExtractReplyToInfo(Message message) + { + return new ReplyToInfo(message); + } + + internal static void PrepareRequest(Message request) + { + MessageHeaders requestHeaders = request.Headers; + + if (requestHeaders.MessageId == null) + { + requestHeaders.MessageId = new UniqueId(); + } + + request.Properties.AllowOutputBatching = false; + //if (TraceUtility.PropagateUserActivity || TraceUtility.ShouldPropagateActivity) + //{ + // TraceUtility.AddAmbientActivityToMessage(request); + //} + } + + internal static void PrepareReply(Message reply, UniqueId messageId) + { + if (object.ReferenceEquals(messageId, null)) + throw TraceUtility.ThrowHelperError(new InvalidOperationException(SR.MissingMessageID), reply); + + MessageHeaders replyHeaders = reply.Headers; + + if (object.ReferenceEquals(replyHeaders.RelatesTo, null)) + { + replyHeaders.RelatesTo = messageId; + } + + //if (TraceUtility.PropagateUserActivity || TraceUtility.ShouldPropagateActivity) + //{ + // TraceUtility.AddAmbientActivityToMessage(reply); + //} + } + + internal static void PrepareReply(Message reply, Message request) + { + UniqueId messageId = request.Headers.MessageId; + + if (messageId != null) + { + MessageHeaders replyHeaders = reply.Headers; + + if (object.ReferenceEquals(replyHeaders.RelatesTo, null)) + { + replyHeaders.RelatesTo = messageId; + } + } + + //if (TraceUtility.PropagateUserActivity || TraceUtility.ShouldPropagateActivity) + //{ + // TraceUtility.AddAmbientActivityToMessage(reply); + //} + } + + internal struct ReplyToInfo + { + internal ReplyToInfo(Message message) + { + FaultTo = message.Headers.FaultTo; + ReplyTo = message.Headers.ReplyTo; + if (message.Version.Addressing == AddressingVersion.WSAddressingAugust2004) + { + this.From = message.Headers.From; + } + else + { + From = null; + } + } + + internal EndpointAddress FaultTo { get; } + + internal EndpointAddress From { get; } + + internal bool HasFaultTo + { + get { return !IsTrivial(FaultTo); } + } + + internal bool HasFrom + { + get { return !IsTrivial(From); } + } + + internal bool HasReplyTo + { + get { return !IsTrivial(ReplyTo); } + } + + internal EndpointAddress ReplyTo { get; } + + bool IsTrivial(EndpointAddress address) + { + // Note: even if address.IsAnonymous, it may have identity, reference parameters, etc. + return (address == null) || (address == EndpointAddress.AnonymousAddress); + } + } + + internal class Key + { + internal UniqueId MessageId; + internal Type StateType; + + internal Key(UniqueId messageId, Type stateType) + { + MessageId = messageId; + StateType = stateType; + } + + public override bool Equals(object obj) + { + Key other = obj as Key; + if (other == null) + return false; + return other.MessageId == MessageId && other.StateType == StateType; + } + + public override int GetHashCode() + { + return MessageId.GetHashCode() ^ StateType.GetHashCode(); + } + + public override string ToString() + { + return typeof(Key).ToString() + ": {" + MessageId + ", " + StateType.ToString() + "}"; + } + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/SecurityCapabilities.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/SecurityCapabilities.cs new file mode 100644 index 000000000..1a8083c90 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/SecurityCapabilities.cs @@ -0,0 +1,74 @@ +using System.Net.Security; + +namespace CoreWCF.Channels +{ + class SecurityCapabilities : ISecurityCapabilities + { + internal bool supportsServerAuth; + internal bool supportsClientAuth; + internal bool supportsClientWindowsIdentity; + internal ProtectionLevel requestProtectionLevel; + internal ProtectionLevel responseProtectionLevel; + + public SecurityCapabilities(bool supportsClientAuth, bool supportsServerAuth, bool supportsClientWindowsIdentity, + ProtectionLevel requestProtectionLevel, ProtectionLevel responseProtectionLevel) + { + this.supportsClientAuth = supportsClientAuth; + this.supportsServerAuth = supportsServerAuth; + this.supportsClientWindowsIdentity = supportsClientWindowsIdentity; + this.requestProtectionLevel = requestProtectionLevel; + this.responseProtectionLevel = responseProtectionLevel; + } + + public ProtectionLevel SupportedRequestProtectionLevel { get { return requestProtectionLevel; } } + public ProtectionLevel SupportedResponseProtectionLevel { get { return responseProtectionLevel; } } + public bool SupportsClientAuthentication { get { return supportsClientAuth; } } + public bool SupportsClientWindowsIdentity { get { return supportsClientWindowsIdentity; } } + public bool SupportsServerAuthentication { get { return supportsServerAuth; } } + + static SecurityCapabilities None + { + get { return new SecurityCapabilities(false, false, false, ProtectionLevel.None, ProtectionLevel.None); } + } + + internal static bool IsEqual(ISecurityCapabilities capabilities1, ISecurityCapabilities capabilities2) + { + if (capabilities1 == null) + { + capabilities1 = SecurityCapabilities.None; + } + + if (capabilities2 == null) + { + capabilities2 = SecurityCapabilities.None; + } + + if (capabilities1.SupportedRequestProtectionLevel != capabilities2.SupportedRequestProtectionLevel) + { + return false; + } + + if (capabilities1.SupportedResponseProtectionLevel != capabilities2.SupportedResponseProtectionLevel) + { + return false; + } + + if (capabilities1.SupportsClientAuthentication != capabilities2.SupportsClientAuthentication) + { + return false; + } + + if (capabilities1.SupportsClientWindowsIdentity != capabilities2.SupportsClientWindowsIdentity) + { + return false; + } + + if (capabilities1.SupportsServerAuthentication != capabilities2.SupportsServerAuthentication) + { + return false; + } + + return true; + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/ServiceChannel.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/ServiceChannel.cs new file mode 100644 index 000000000..02652eef1 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/ServiceChannel.cs @@ -0,0 +1,1780 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; +using CoreWCF.Runtime; +using CoreWCF.Description; +using CoreWCF.Diagnostics; +using CoreWCF.Dispatcher; + +namespace CoreWCF.Channels +{ + // This class is sealed because the constructor could call Abort, which is virtual + sealed class ServiceChannel : CommunicationObject, IChannel, IClientChannel, IDuplexContextChannel, IOutputChannel, IRequestChannel, IServiceChannel + { + + int activityCount = 0; + readonly bool allowOutputBatching = false; + bool autoClose = true; + //CallOnceManager autoDisplayUIManager; + CallOnceManager autoOpenManager; + readonly IChannelBinder binder; + readonly ChannelDispatcher channelDispatcher; + ClientRuntime clientRuntime; + readonly bool closeBinder = true; + bool closeFactory; + bool doneReceiving; + EndpointDispatcher endpointDispatcher; + bool explicitlyOpened; + ExtensionCollection extensions; + readonly bool hasSession; + readonly SessionIdleManager idleManager; + InstanceContext instanceContext; + //ServiceThrottle instanceContextServiceThrottle; + bool isPending; + readonly bool isReplyChannel; + EndpointAddress localAddress; + readonly MessageVersion messageVersion; + readonly bool openBinder = false; + TimeSpan operationTimeout; + object proxy; + //ServiceThrottle serviceThrottle; + string terminatingOperationName; + bool hasCleanedUpChannelCollections; + //EventTraceActivity eventActivity; + IDefaultCommunicationTimeouts timeouts; + + EventHandler unknownMessageReceived; + + ServiceChannel(IChannelBinder binder, Binding binding) + { + if (binder == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("binder"); + } + + this.messageVersion = binding.MessageVersion; + this.binder = binder; + isReplyChannel = this.binder.Channel is IReplyChannel; + + IChannel innerChannel = binder.Channel; + hasSession = (innerChannel is ISessionChannel) || + (innerChannel is ISessionChannel) || + (innerChannel is ISessionChannel); + + IncrementActivity(); + openBinder = (binder.Channel.State == CommunicationState.Created); + + operationTimeout = binding.SendTimeout; + this.timeouts = binding; + } + + // Only used by ServiceChannelFactory + //internal ServiceChannel(ServiceChannelFactory factory, IChannelBinder binder) + // : this(binder, factory.MessageVersion, factory) + //{ + // this.factory = factory; + // this.clientRuntime = factory.ClientRuntime; + + // this.SetupInnerChannelFaultHandler(); + + // DispatchRuntime dispatch = factory.ClientRuntime.DispatchRuntime; + // if (dispatch != null) + // { + // this.autoClose = dispatch.AutomaticInputSessionShutdown; + // } + + // factory.ChannelCreated(this); + //} + + internal ServiceChannel(IChannelBinder binder, + EndpointDispatcher endpointDispatcher, + Binding binding, + SessionIdleManager idleManager) + : this(binder, binding) + { + if (endpointDispatcher == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(endpointDispatcher)); + } + + //this.channelDispatcher = channelDispatcher; + this.endpointDispatcher = endpointDispatcher; + clientRuntime = endpointDispatcher.DispatchRuntime.CallbackClientRuntime; + + SetupInnerChannelFaultHandler(); + + autoClose = endpointDispatcher.DispatchRuntime.AutomaticInputSessionShutdown; + isPending = true; + + this.idleManager = idleManager; + + if (!binder.HasSession) + closeBinder = false; + + if (this.idleManager != null) + { + bool didIdleAbort; + this.idleManager.RegisterChannel(this, out didIdleAbort); + if (didIdleAbort) + { + Abort(); + } + } + } + + CallOnceManager AutoOpenManager + { + get + { + if (!explicitlyOpened && (autoOpenManager == null)) + { + EnsureAutoOpenManagers(); + } + return autoOpenManager; + } + } + + //CallOnceManager AutoDisplayUIManager + //{ + // get + // { + // if (!this.explicitlyOpened && (this.autoDisplayUIManager == null)) + // { + // this.EnsureAutoOpenManagers(); + // } + // return this.autoDisplayUIManager; + // } + //} + + + //internal EventTraceActivity EventActivity + //{ + // get + // { + // if (this.eventActivity == null) + // { + // //Take the id on the thread so that we know the initiating operation. + // this.eventActivity = EventTraceActivity.GetFromThreadOrCreate(); + // } + // return this.eventActivity; + // } + //} + + internal bool CloseFactory + { + get { return closeFactory; } + set { closeFactory = value; } + } + + protected override TimeSpan DefaultCloseTimeout + { + get { return CloseTimeout; } + } + + protected override TimeSpan DefaultOpenTimeout + { + get { return OpenTimeout; } + } + + internal DispatchRuntime DispatchRuntime + { + get + { + if (endpointDispatcher != null) + { + return endpointDispatcher.DispatchRuntime; + } + if (clientRuntime != null) + { + return clientRuntime.DispatchRuntime; + } + return null; + } + } + + internal MessageVersion MessageVersion + { + get { return messageVersion; } + } + + internal IChannelBinder Binder + { + get { return binder; } + } + + internal TimeSpan CloseTimeout + { + get + { + //if (this.IsClient) + //{ + // return factory.InternalCloseTimeout; + //} + //else + //{ + return timeouts.CloseTimeout; + //} + } + } + + internal ChannelDispatcher ChannelDispatcher + { + get { return channelDispatcher; } + } + + internal EndpointDispatcher EndpointDispatcher + { + get { return endpointDispatcher; } + set + { + lock (ThisLock) + { + endpointDispatcher = value; + clientRuntime = value.DispatchRuntime.CallbackClientRuntime; + } + } + } + + //internal ServiceChannelFactory Factory + //{ + // get { return this.factory; } + //} + + internal IChannel InnerChannel + { + get { return binder.Channel; } + } + + internal bool IsPending + { + get { return isPending; } + set { isPending = value; } + } + + internal bool HasSession + { + get { return hasSession; } + } + + internal bool IsReplyChannel + { + get { return isReplyChannel; } + } + + public Uri ListenUri + { + get + { + return binder.ListenUri; + } + } + + public EndpointAddress LocalAddress + { + get + { + if (localAddress == null) + { + if (endpointDispatcher != null) + { + localAddress = endpointDispatcher.EndpointAddress; + } + else + { + localAddress = binder.LocalAddress; + } + } + return localAddress; + } + } + + internal TimeSpan OpenTimeout + { + get + { + //if (this.IsClient) + //{ + // return factory.InternalOpenTimeout; + //} + //else + //{ + return ChannelDispatcher.InternalOpenTimeout; + //} + } + } + + public TimeSpan OperationTimeout + { + get { return operationTimeout; } + set + { + if (value < TimeSpan.Zero) + { + string message = SR.SFxTimeoutOutOfRange0; + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(value), value, message)); + } + if (TimeoutHelper.IsTooLarge(value)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(value), value, SR.SFxTimeoutOutOfRangeTooBig)); + } + + + operationTimeout = value; + } + } + + internal object Proxy + { + get + { + object proxy = this.proxy; + if (proxy != null) + return proxy; + else + return this; + } + set + { + proxy = value; + base.EventSender = value; // need to use "proxy" as open/close event source + } + } + + internal ClientRuntime ClientRuntime + { + get { return clientRuntime; } + } + + public EndpointAddress RemoteAddress + { + get + { + IOutputChannel outputChannel = InnerChannel as IOutputChannel; + if (outputChannel != null) + return outputChannel.RemoteAddress; + + IRequestChannel requestChannel = InnerChannel as IRequestChannel; + if (requestChannel != null) + return requestChannel.RemoteAddress; + + return null; + } + } + + ProxyOperationRuntime UnhandledProxyOperation + { + get { return ClientRuntime.GetRuntime().UnhandledProxyOperation; } + } + + public Uri Via + { + get + { + IOutputChannel outputChannel = InnerChannel as IOutputChannel; + if (outputChannel != null) + return outputChannel.Via; + + IRequestChannel requestChannel = InnerChannel as IRequestChannel; + if (requestChannel != null) + return requestChannel.Via; + + return null; + } + } + + internal InstanceContext InstanceContext + { + get { return instanceContext; } + set { instanceContext = value; } + } + + //internal ServiceThrottle InstanceContextServiceThrottle + //{ + // get { return this.instanceContextServiceThrottle; } + // set { this.instanceContextServiceThrottle = value; } + //} + + //internal ServiceThrottle ServiceThrottle + //{ + // get { return this.serviceThrottle; } + // set + // { + // this.ThrowIfDisposed(); + // this.serviceThrottle = value; + // } + //} + + void SetupInnerChannelFaultHandler() + { + // need to call this method after this.binder and this.clientRuntime are set to prevent a potential + // NullReferenceException in this method or in the OnInnerChannelFaulted method; + // because this method accesses this.binder and OnInnerChannelFaulted acesses this.clientRuntime. + binder.Channel.Faulted += OnInnerChannelFaulted; + } + + //void BindDuplexCallbacks() + //{ + // IDuplexChannel duplexChannel = this.InnerChannel as IDuplexChannel; + // if ((duplexChannel != null) && (this.factory != null) && (this.instanceContext != null)) + // { + // if (this.binder is DuplexChannelBinder) + // ((DuplexChannelBinder)this.binder).EnsurePumping(); + // } + //} + + internal bool CanCastTo(Type t) + { + if (t.IsAssignableFrom(typeof(IClientChannel))) + return true; + + if (t.IsAssignableFrom(typeof(IDuplexContextChannel))) + return InnerChannel is IDuplexChannel; + + if (t.IsAssignableFrom(typeof(IServiceChannel))) + return true; + + return false; + } + + internal void CompletedIOOperation() + { + if (idleManager != null) + { + idleManager.CompletedActivity(); + } + } + + void EnsureAutoOpenManagers() + { + lock (ThisLock) + { + if (!explicitlyOpened) + { + if (autoOpenManager == null) + { + autoOpenManager = new CallOnceManager(this, CallOpenOnce.Instance); + } + } + } + } + + async Task EnsureOpenedAsync(CancellationToken token) + { + ///// TASKS ****** + CallOnceManager manager = AutoOpenManager; + if (manager != null) + { + await manager.CallOnceAsync(token); + } + + ThrowIfOpening(); + ThrowIfDisposedOrNotOpen(); + } + + public T GetProperty() where T : class + { + IChannel innerChannel = InnerChannel; + if (innerChannel != null) + return innerChannel.GetProperty(); + return null; + } + + void PrepareCall(ProxyOperationRuntime operation, bool oneway, ref ProxyRpc rpc) + { + OperationContext context = OperationContext.Current; + // Doing a request reply callback when dispatching in-order deadlocks. + // We never receive the reply until we finish processing the current message. + if (!oneway) + { + DispatchRuntime dispatchBehavior = ClientRuntime.DispatchRuntime; + if ((dispatchBehavior != null) && (dispatchBehavior.ConcurrencyMode == ConcurrencyMode.Single)) + { + if ((context != null) && (!context.IsUserContext) && (context.InternalServiceChannel == this)) + { + if (dispatchBehavior.IsOnServer) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxCallbackRequestReplyInOrder1, typeof(ServiceBehaviorAttribute).Name))); + } + else + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxCallbackRequestReplyInOrder1, typeof(CallbackBehaviorAttribute).Name))); + } + } + } + } + + if ((State == CommunicationState.Created) && !operation.IsInitiating) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxNonInitiatingOperation1, operation.Name))); + } + + if (terminatingOperationName != null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxTerminatingOperationAlreadyCalled1, terminatingOperationName))); + } + + operation.BeforeRequest(ref rpc); + AddMessageProperties(rpc.Request, context); + if (!oneway && !ClientRuntime.ManualAddressing && rpc.Request.Version.Addressing != AddressingVersion.None) + { + RequestReplyCorrelator.PrepareRequest(rpc.Request); + + MessageHeaders headers = rpc.Request.Headers; + EndpointAddress localAddress = LocalAddress; + EndpointAddress replyTo = headers.ReplyTo; + + if (replyTo == null) + { + headers.ReplyTo = localAddress ?? EndpointAddress.AnonymousAddress; + } + } + + //if (TraceUtility.MessageFlowTracingOnly) + //{ + // //always set a new ID if none provided + // if (Trace.CorrelationManager.ActivityId == Guid.Empty) + // { + // rpc.ActivityId = Guid.NewGuid(); + // FxTrace.Trace.SetAndTraceTransfer(rpc.ActivityId, true); + // } + //} + + //if (rpc.Activity != null) + //{ + // TraceUtility.SetActivity(rpc.Request, rpc.Activity); + // if (TraceUtility.ShouldPropagateActivity) + // { + // TraceUtility.AddActivityHeader(rpc.Request); + // } + //} + //else if (TraceUtility.PropagateUserActivity || TraceUtility.ShouldPropagateActivity) + //{ + // TraceUtility.AddAmbientActivityToMessage(rpc.Request); + //} + operation.Parent.BeforeSendRequest(ref rpc); + + + //Attach and transfer Activity + //if (FxTrace.Trace.IsEnd2EndActivityTracingEnabled) + //{ + // TraceClientOperationPrepared(ref rpc); + //} + + //TraceUtility.MessageFlowAtMessageSent(rpc.Request, rpc.EventTraceActivity); + + //if (MessageLogger.LogMessagesAtServiceLevel) + //{ + // MessageLogger.LogMessage(ref rpc.Request, (oneway ? MessageLoggingSource.ServiceLevelSendDatagram : MessageLoggingSource.ServiceLevelSendRequest) | MessageLoggingSource.LastChance); + //} + } + + //private void TraceClientOperationPrepared(ref ProxyRpc rpc) + //{ + // //Retrieve the old id on the RPC and attach the id on the message since we have a message id now. + // Guid previousId = rpc.EventTraceActivity != null ? rpc.EventTraceActivity.ActivityId : Guid.Empty; + // EventTraceActivity requestActivity = EventTraceActivityHelper.TryExtractActivity(rpc.Request); + // if (requestActivity == null) + // { + // requestActivity = EventTraceActivity.GetFromThreadOrCreate(); + // EventTraceActivityHelper.TryAttachActivity(rpc.Request, requestActivity); + // } + // rpc.EventTraceActivity = requestActivity; + + // if (TD.ClientOperationPreparedIsEnabled()) + // { + // string remoteAddress = string.Empty; + // if (this.RemoteAddress != null && this.RemoteAddress.Uri != null) + // { + // remoteAddress = this.RemoteAddress.Uri.AbsoluteUri; + // } + // TD.ClientOperationPrepared(rpc.EventTraceActivity, + // rpc.Action, + // this.clientRuntime.ContractName, + // remoteAddress, + // previousId); + // } + + //} + + internal IAsyncResult BeginCall(string action, bool oneway, ProxyOperationRuntime operation, object[] ins, AsyncCallback callback, object asyncState) + { + return BeginCall(action, oneway, operation, ins, operationTimeout, callback, asyncState); + } + + internal IAsyncResult BeginCall(string action, bool oneway, ProxyOperationRuntime operation, object[] ins, TimeSpan timeout, AsyncCallback callback, object asyncState) + { + var helper = new TimeoutHelper(operationTimeout); + return BeginCallAsync(action, oneway, operation, ins, helper.GetCancellationToken()).ToApm(callback, asyncState); + } + + internal async Task BeginCallAsync(string action, bool oneway, ProxyOperationRuntime operation, object[] ins, CancellationToken token) + { + ThrowIfIdleAborted(operation); + ThrowIfIsConnectionOpened(operation); + + ProxyRpc rpc = new ProxyRpc(this, operation, action, ins, token); + + PrepareCall(operation, oneway, ref rpc); + + if (!explicitlyOpened) + { + await EnsureOpenedAsync(token); + } + else + { + ThrowIfOpening(); + ThrowIfDisposedOrNotOpen(); + } + + try + { + ConcurrencyBehavior.UnlockInstanceBeforeCallout(OperationContext.Current); + + if (oneway) + { + await binder.SendAsync(rpc.Request, rpc.CancellationToken); + } + else + { + rpc.Reply = await binder.RequestAsync(rpc.Request, rpc.CancellationToken); + + if (rpc.Reply == null) + { + ThrowIfFaulted(); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(SR.SFxServerDidNotReply)); + } + } + } + finally + { + CompletedIOOperation(); + CallOnceManager.SignalNextIfNonNull(autoOpenManager); + await ConcurrencyBehavior.LockInstanceAfterCalloutAsync(OperationContext.Current); + } + + return rpc; + } + + + internal Task CallAsync(string action, bool oneway, ProxyOperationRuntime operation, object[] ins, object[] outs) + { + var helper = new TimeoutHelper(operationTimeout); + return CallAsync(action, oneway, operation, ins, outs, helper.GetCancellationToken()); + } + + internal async Task CallAsync(string action, bool oneway, ProxyOperationRuntime operation, object[] ins, object[] outs, CancellationToken token) + { + ThrowIfIdleAborted(operation); + ThrowIfIsConnectionOpened(operation); + + ProxyRpc rpc = new ProxyRpc(this, operation, action, ins, token); + + //TraceServiceChannelCallStart(rpc.EventTraceActivity, true); + + //using (rpc.Activity = DiagnosticUtility.ShouldUseActivity ? ServiceModelActivity.CreateBoundedActivity() : null) + //{ + // if (DiagnosticUtility.ShouldUseActivity) + // { + // ServiceModelActivity.Start(rpc.Activity, SR.Format(SR.ActivityProcessAction, action), ActivityType.ProcessAction); + // } + + PrepareCall(operation, oneway, ref rpc); + + if (!explicitlyOpened) + { + await EnsureOpenedAsync(token); + } + else + { + ThrowIfOpening(); + ThrowIfDisposedOrNotOpen(); + } + + try + { + ConcurrencyBehavior.UnlockInstanceBeforeCallout(OperationContext.Current); + + if (oneway) + { + await binder.SendAsync(rpc.Request, rpc.CancellationToken); + } + else + { + rpc.Reply = await binder.RequestAsync(rpc.Request, rpc.CancellationToken); + + if (rpc.Reply == null) + { + ThrowIfFaulted(); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(SR.SFxServerDidNotReply)); + } + } + } + finally + { + CompletedIOOperation(); + CallOnceManager.SignalNextIfNonNull(autoOpenManager); + await ConcurrencyBehavior.LockInstanceAfterCalloutAsync(OperationContext.Current); + } + + rpc.OutputParameters = outs; + HandleReply(operation, ref rpc); + //} + return rpc.ReturnValue; + } + + internal object EndCall(string action, object[] outs, IAsyncResult result) + { + var rpc = result.ToApmEnd(); + rpc.OutputParameters = outs; + HandleReply(rpc.Operation, ref rpc); + return rpc.ReturnValue; + } + + internal void DecrementActivity() + { + int updatedActivityCount = Interlocked.Decrement(ref activityCount); + + if (!((updatedActivityCount >= 0))) + { + throw Fx.AssertAndThrowFatal("ServiceChannel.DecrementActivity: (updatedActivityCount >= 0)"); + } + + if (updatedActivityCount == 0 && autoClose) + { + try + { + if (State == CommunicationState.Opened) + { + // TODO: Async + var helper = new TimeoutHelper(CloseTimeout); + CloseAsync(helper.GetCancellationToken()).GetAwaiter().GetResult(); + } + } + catch (CommunicationException e) + { + DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); + } + catch (TimeoutException e) + { + //if (TD.CloseTimeoutIsEnabled()) + //{ + // TD.CloseTimeout(e.Message); + //} + DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); + } + catch (ObjectDisposedException e) + { + DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); + } + catch (InvalidOperationException e) + { + DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); + } + } + } + + internal void FireUnknownMessageReceived(Message message) + { + EventHandler handler = unknownMessageReceived; + if (handler != null) + handler(proxy, new UnknownMessageReceivedEventArgs(message)); + } + + TimeoutException GetOpenTimeoutException(TimeSpan timeout) + { + EndpointAddress address = RemoteAddress ?? LocalAddress; + if (address != null) + { + return new TimeoutException(SR.Format(SR.TimeoutServiceChannelConcurrentOpen2, address, timeout)); + } + else + { + return new TimeoutException(SR.Format(SR.TimeoutServiceChannelConcurrentOpen1, timeout)); + } + } + + internal void HandleReceiveComplete(RequestContext context) + { + if (context == null && HasSession) + { + bool first; + lock (ThisLock) + { + first = !doneReceiving; + doneReceiving = true; + } + + if (first) + { + DispatchRuntime dispatchBehavior = ClientRuntime.DispatchRuntime; + if (dispatchBehavior != null) + dispatchBehavior.GetRuntime().InputSessionDoneReceiving(this); + + DecrementActivity(); + } + } + } + + void HandleReply(ProxyOperationRuntime operation, ref ProxyRpc rpc) + { + try + { + //set the ID after response + //if (TraceUtility.MessageFlowTracingOnly && rpc.ActivityId != Guid.Empty) + //{ + // System.Runtime.Diagnostics.DiagnosticTraceBase.ActivityId = rpc.ActivityId; + //} + + if (rpc.Reply != null) + { + //TraceUtility.MessageFlowAtMessageReceived(rpc.Reply, null, rpc.EventTraceActivity, false); + + //if (MessageLogger.LogMessagesAtServiceLevel) + //{ + // MessageLogger.LogMessage(ref rpc.Reply, MessageLoggingSource.ServiceLevelReceiveReply | MessageLoggingSource.LastChance); + //} + operation.Parent.AfterReceiveReply(ref rpc); + + if ((operation.ReplyAction != MessageHeaders.WildcardAction) && !rpc.Reply.IsFault && rpc.Reply.Headers.Action != null) + { + if (string.CompareOrdinal(operation.ReplyAction, rpc.Reply.Headers.Action) != 0) + { + Exception error = new ProtocolException(SR.Format(SR.SFxReplyActionMismatch3,operation.Name, + rpc.Reply.Headers.Action, + operation.ReplyAction)); + TerminateIfNecessary(ref rpc); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(error); + } + } + if (operation.DeserializeReply && clientRuntime.IsFault(ref rpc.Reply)) + { + MessageFault fault = MessageFault.CreateFault(rpc.Reply, clientRuntime.MaxFaultSize); + string action = rpc.Reply.Headers.Action; + if (action == rpc.Reply.Version.Addressing.DefaultFaultAction) + { + action = null; + } + ThrowIfFaultUnderstood(rpc.Reply, fault, action, rpc.Reply.Version, rpc.Channel.GetProperty()); + FaultException fe = rpc.Operation.FaultFormatter.Deserialize(fault, action); + TerminateIfNecessary(ref rpc); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(fe); + } + + operation.AfterReply(ref rpc); + } + } + finally + { + if (operation.SerializeRequest) + { + rpc.Request.Close(); + } + + OperationContext operationContext = OperationContext.Current; + bool consumed = ((rpc.Reply != null) && (rpc.Reply.State != MessageState.Created)); + + if ((operationContext != null) && operationContext.IsUserContext) + { + operationContext.SetClientReply(rpc.Reply, consumed); + } + else if (consumed) + { + rpc.Reply.Close(); + } + + //if (TraceUtility.MessageFlowTracingOnly) + //{ + // if (rpc.ActivityId != Guid.Empty) + // { + // //reset the ID as it was created internally - ensures each call is uniquely correlatable + // System.Runtime.Diagnostics.DiagnosticTraceBase.ActivityId = Guid.Empty; + // rpc.ActivityId = Guid.Empty; + // } + //} + } + TerminateIfNecessary(ref rpc); + + //if (TD.ServiceChannelCallStopIsEnabled()) + //{ + // string remoteAddress = string.Empty; + // if (this.RemoteAddress != null && this.RemoteAddress.Uri != null) + // { + // remoteAddress = this.RemoteAddress.Uri.AbsoluteUri; + // } + // TD.ServiceChannelCallStop(rpc.EventTraceActivity, rpc.Action, + // this.clientRuntime.ContractName, + // remoteAddress); + //} + + } + + void TerminateIfNecessary(ref ProxyRpc rpc) + { + if (rpc.Operation.IsTerminating) + { + terminatingOperationName = rpc.Operation.Name; + TerminatingOperationBehavior.AfterReply(ref rpc); + } + } + + void ThrowIfFaultUnderstood(Message reply, MessageFault fault, string action, MessageVersion version, FaultConverter faultConverter) + { + Exception exception; + if (faultConverter != null && faultConverter.TryCreateException(reply, fault, out exception)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(exception); + } + + bool checkSender; + bool checkReceiver; + FaultCode code; + + if (version.Envelope == EnvelopeVersion.Soap11) + { + checkSender = true; + checkReceiver = true; + code = fault.Code; + } + else + { + checkSender = fault.Code.IsSenderFault; + checkReceiver = fault.Code.IsReceiverFault; + code = fault.Code.SubCode; + } + + if (code == null) + { + return; + } + + if (code.Namespace == null) + { + return; + } + + if (checkSender) + { + if (string.Compare(code.Namespace, FaultCodeConstants.Namespaces.NetDispatch, StringComparison.Ordinal) == 0) + { + if (string.Compare(code.Name, FaultCodeConstants.Codes.SessionTerminated, StringComparison.Ordinal) == 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new ChannelTerminatedException(fault.Reason.GetMatchingTranslation(CultureInfo.CurrentCulture).Text)); + } + + //if (string.Compare(code.Name, FaultCodeConstants.Codes.TransactionAborted, StringComparison.Ordinal) == 0) + //{ + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new ProtocolException(fault.Reason.GetMatchingTranslation(CultureInfo.CurrentCulture).Text)); + //} + } + + // throw SecurityAccessDeniedException explicitly + // MessageSecurity + //if (string.Compare(code.Namespace, SecurityVersion.Default.HeaderNamespace.Value, StringComparison.Ordinal) == 0) + //{ + // if (string.Compare(code.Name, SecurityVersion.Default.FailedAuthenticationFaultCode.Value, StringComparison.Ordinal) == 0) + // { + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new SecurityAccessDeniedException(fault.Reason.GetMatchingTranslation(CultureInfo.CurrentCulture).Text)); + // } + //} + } + + if (checkReceiver) + { + if (string.Compare(code.Namespace, FaultCodeConstants.Namespaces.NetDispatch, StringComparison.Ordinal) == 0) + { + if (string.Compare(code.Name, FaultCodeConstants.Codes.InternalServiceFault, StringComparison.Ordinal) == 0) + { + if (HasSession) + { + Fault(); + } + if (fault.HasDetail) + { + ExceptionDetail detail = fault.GetDetail(); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new FaultException(detail, fault.Reason, fault.Code, action)); + } + throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new FaultException(fault, action)); + } + if (string.Compare(code.Name, FaultCodeConstants.Codes.DeserializationFailed, StringComparison.Ordinal) == 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new ProtocolException( + fault.Reason.GetMatchingTranslation(CultureInfo.CurrentCulture).Text)); + } + } + } + } + + void ThrowIfIdleAborted(ProxyOperationRuntime operation) + { + if (idleManager != null && idleManager.DidIdleAbort) + { + string text = SR.Format(SR.SFxServiceChannelIdleAborted, operation.Name); + Exception error = new CommunicationObjectAbortedException(text); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(error); + } + } + + void ThrowIfIsConnectionOpened(ProxyOperationRuntime operation) + { + if (operation.IsSessionOpenNotificationEnabled) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException( + SR.Format(SR.SFxServiceChannelCannotBeCalledBecauseIsSessionOpenNotificationEnabled, operation.Name, "Action", OperationDescription.SessionOpenedAction, "Open"))); + } + } + + void ThrowIfOpening() + { + if (State == CommunicationState.Opening) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.SFxCannotCallAutoOpenWhenExplicitOpenCalled)); + } + } + + internal void IncrementActivity() + { + Interlocked.Increment(ref activityCount); + } + + void OnInnerChannelFaulted(object sender, EventArgs e) + { + Fault(); + + if (HasSession) + { + DispatchRuntime dispatchRuntime = ClientRuntime.DispatchRuntime; + if (dispatchRuntime != null) + { + dispatchRuntime.GetRuntime().InputSessionFaulted(this); + } + } + + if (autoClose) + { + Abort(); + } + } + + void AddMessageProperties(Message message, OperationContext context) + { + if (allowOutputBatching) + { + message.Properties.AllowOutputBatching = true; + } + + if (context != null && context.InternalServiceChannel == this) + { + if (!context.OutgoingMessageVersion.IsMatch(message.Headers.MessageVersion)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException( + SR.Format(SR.SFxVersionMismatchInOperationContextAndMessage2, context.OutgoingMessageVersion, message.Headers.MessageVersion) + )); + } + + if (context.HasOutgoingMessageHeaders) + { + message.Headers.CopyHeadersFrom(context.OutgoingMessageHeaders); + } + + if (context.HasOutgoingMessageProperties) + { + message.Properties.CopyProperties(context.OutgoingMessageProperties); + } + } + } + + #region IChannel Members + public Task SendAsync(Message message) + { + var helper = new TimeoutHelper(OperationTimeout); + return SendAsync(message, helper.GetCancellationToken()); + } + + public Task SendAsync(Message message, CancellationToken token) + { + ProxyOperationRuntime operation = UnhandledProxyOperation; + return CallAsync(message.Headers.Action, true, operation, new object[] { message }, Array.Empty(), token); + } + + public Task RequestAsync(Message message) + { + var helper = new TimeoutHelper(OperationTimeout); + return RequestAsync(message, helper.GetCancellationToken()); + } + + public async Task RequestAsync(Message message, CancellationToken token) + { + ProxyOperationRuntime operation = UnhandledProxyOperation; + return (Message)await CallAsync(message.Headers.Action, false, operation, new object[] { message }, Array.Empty(), token); + } + + protected override void OnAbort() + { + if (idleManager != null) + { + idleManager.CancelTimer(); + } + + binder.Abort(); + + CleanupChannelCollections(); + + //ServiceThrottle serviceThrottle = this.serviceThrottle; + //if (serviceThrottle != null) + // serviceThrottle.DeactivateChannel(); + + //rollback the attached transaction if one is present + //if ((this.instanceContext != null) && this.HasSession) + //{ + // if (instanceContext.HasTransaction) + // { + // instanceContext.Transaction.CompletePendingTransaction(instanceContext.Transaction.Attached, new Exception()); // error!=null forces Tx rollback + // } + //} + } + + protected override async Task OnCloseAsync(CancellationToken token) + { + if (idleManager != null) + { + idleManager.CancelTimer(); + } + + //if (this.InstanceContext != null && this.InstanceContext.HasTransaction) + //{ + // this.InstanceContext.CompleteAttachedTransaction(); + //} + + if (closeBinder) + await InnerChannel.CloseAsync(token); + + CleanupChannelCollections(); + + //ServiceThrottle serviceThrottle = this.serviceThrottle; + //if (serviceThrottle != null) + //{ + // serviceThrottle.DeactivateChannel(); + //} + } + + protected override async Task OnOpenAsync(CancellationToken token) + { + if (autoOpenManager == null) + { + explicitlyOpened = true; + } + + //this.TraceChannelOpenStarted(); + + if (openBinder) + { + await InnerChannel.OpenAsync(token); + } + + CompletedIOOperation(); + + //this.TraceChannelOpenCompleted(); + } + + void CleanupChannelCollections() + { + if (!hasCleanedUpChannelCollections) + { + lock (ThisLock) + { + if (!hasCleanedUpChannelCollections) + { + if (InstanceContext != null) + { + InstanceContext.OutgoingChannels.Remove((IChannel)proxy); + } + + hasCleanedUpChannelCollections = true; + } + } + } + } + #endregion + + #region IClientChannel Members + + bool IDuplexContextChannel.AutomaticInputSessionShutdown + { + get { return autoClose; } + set { autoClose = value; } + } + + //bool IContextChannel.AllowOutputBatching + //{ + // get { return this.allowOutputBatching; } + // set { this.allowOutputBatching = value; } + //} + + Task IDuplexContextChannel.CloseOutputSessionAsync(CancellationToken token) + { + return GetDuplexSessionOrThrow().CloseOutputSessionAsync(token); + } + + IDuplexSession GetDuplexSessionOrThrow() + { + if (InnerChannel == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.channelIsNotAvailable0)); + } + + ISessionChannel duplexSessionChannel = InnerChannel as ISessionChannel; + if (duplexSessionChannel == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.channelDoesNotHaveADuplexSession0)); + } + + return duplexSessionChannel.Session; + } + + IExtensionCollection IExtensibleObject.Extensions + { + get + { + lock (ThisLock) + { + if (extensions == null) + extensions = new ExtensionCollection((IContextChannel)Proxy, ThisLock); + return extensions; + } + } + } + + InstanceContext IDuplexContextChannel.CallbackInstance + { + get { return instanceContext; } + set + { + lock (ThisLock) + { + if (instanceContext != null) + { + instanceContext.OutgoingChannels.Remove((IChannel)proxy); + } + + instanceContext = value; + + if (instanceContext != null) + { + instanceContext.OutgoingChannels.Add((IChannel)proxy); + } + } + } + } + + IInputSession IContextChannel.InputSession + { + get + { + if (InnerChannel != null) + { + ISessionChannel inputSession = InnerChannel as ISessionChannel; + if (inputSession != null) + return inputSession.Session; + + ISessionChannel duplexSession = InnerChannel as ISessionChannel; + if (duplexSession != null) + return duplexSession.Session; + } + + return null; + } + } + + IOutputSession IContextChannel.OutputSession + { + get + { + if (InnerChannel != null) + { + ISessionChannel outputSession = InnerChannel as ISessionChannel; + if (outputSession != null) + return outputSession.Session; + + ISessionChannel duplexSession = InnerChannel as ISessionChannel; + if (duplexSession != null) + return duplexSession.Session; + } + + return null; + } + } + + string IContextChannel.SessionId + { + get + { + if (InnerChannel != null) + { + ISessionChannel inputSession = InnerChannel as ISessionChannel; + if (inputSession != null) + return inputSession.Session.Id; + + ISessionChannel outputSession = InnerChannel as ISessionChannel; + if (outputSession != null) + return outputSession.Session.Id; + + ISessionChannel duplexSession = InnerChannel as ISessionChannel; + if (duplexSession != null) + return duplexSession.Session.Id; + } + + return null; + } + } + + event EventHandler IClientChannel.UnknownMessageReceived + { + add + { + lock (ThisLock) + { + unknownMessageReceived += value; + } + } + remove + { + lock (ThisLock) + { + unknownMessageReceived -= value; + } + } + } + + void IDisposable.Dispose() + { + CloseAsync().GetAwaiter().GetResult(); + } + + #endregion + + //void TraceChannelOpenStarted() + //{ + // if (TD.ClientChannelOpenStartIsEnabled() && this.endpointDispatcher == null) + // { + // TD.ClientChannelOpenStart(this.EventActivity); + // } + // else if (TD.ServiceChannelOpenStartIsEnabled()) + // { + // TD.ServiceChannelOpenStart(this.EventActivity); + // } + + // //if (DiagnosticUtility.ShouldTraceInformation) + // //{ + // // Dictionary values = new Dictionary(4); + // // bool traceNeeded = false; + // // DispatchRuntime behavior = this.DispatchRuntime; + // // if (behavior != null) + // // { + // // if (behavior.Type != null) + // // { + // // values["ServiceType"] = behavior.Type.AssemblyQualifiedName; + // // } + // // values["ContractNamespace"] = this.clientRuntime.ContractNamespace; + // // values["ContractName"] = this.clientRuntime.ContractName; + // // traceNeeded = true; + // // } + // // if ((this.endpointDispatcher != null) && (this.endpointDispatcher.ListenUri != null)) + // // { + // // values["Uri"] = this.endpointDispatcher.ListenUri.ToString(); + // // traceNeeded = true; + // // } + // // if (traceNeeded) + // // { + // // TraceUtility.TraceEvent(TraceEventType.Information, TraceCode.ServiceChannelLifetime, + // // SR.Format(SR.TraceCodeServiceChannelLifetime), + // // new DictionaryTraceRecord(values), this, null); + // // } + // //} + //} + + //void TraceChannelOpenCompleted() + //{ + // if (this.endpointDispatcher == null && TD.ClientChannelOpenStopIsEnabled()) + // { + // TD.ClientChannelOpenStop(this.EventActivity); + // } + // else if (TD.ServiceChannelOpenStopIsEnabled()) + // { + // TD.ServiceChannelOpenStop(this.EventActivity); + // } + //} + + //static void TraceServiceChannelCallStart(EventTraceActivity eventTraceActivity, bool isSynchronous) + //{ + // if (TD.ServiceChannelCallStartIsEnabled()) + // { + // if (isSynchronous) + // { + // TD.ServiceChannelCallStart(eventTraceActivity); + // } + // else + // { + // TD.ServiceChannelBeginCallStart(eventTraceActivity); + // } + // } + //} + + // Invariants for signalling the CallOnce manager. + // + // 1) If a Call, BeginCall, or EndCall on the channel throws, + // the manager will SignalNext itself. + // 2) If a Waiter times out, it will SignalNext its manager + // once it is both timed out and signalled. + // 3) Once Call or EndCall returns successfully, it guarantees + // that SignalNext will be called once the // next stage + // has sufficiently completed. + interface ICallOnce + { + Task CallAsync(ServiceChannel channel, CancellationToken token); + } + + class CallOpenOnce : ICallOnce + { + static CallOpenOnce instance; + + internal static CallOpenOnce Instance + { + get + { + if (CallOpenOnce.instance == null) + { + CallOpenOnce.instance = new CallOpenOnce(); + } + return CallOpenOnce.instance; + } + } + + Task ICallOnce.CallAsync(ServiceChannel channel, CancellationToken token) + { + return channel.OpenAsync(token); + } + } + + class CallOnceManager + { + readonly ICallOnce callOnce; + readonly ServiceChannel channel; + bool isFirst = true; + Queue queue; + + static readonly Action signalWaiter = CallOnceManager.SignalWaiter; + + internal CallOnceManager(ServiceChannel channel, ICallOnce callOnce) + { + this.callOnce = callOnce; + this.channel = channel; + queue = new Queue(); + } + + object ThisLock + { + get { return this; } + } + + internal async Task CallOnceAsync(CancellationToken token) + { + AsyncWaiter waiter = null; + bool first = false; + + if (queue != null) + { + lock (ThisLock) + { + if (queue != null) + { + if (isFirst) + { + first = true; + isFirst = false; + } + else + { + waiter = new AsyncWaiter(this); + queue.Enqueue(waiter); + } + } + } + } + + if (first) + { + bool throwing = true; + try + { + await callOnce.CallAsync(channel, token); + throwing = false; + } + finally + { + if (throwing) + { + SignalNext(); + } + } + } + else if (waiter != null) + { + await waiter.WaitAsync(token); + } + } + + static internal void SignalNextIfNonNull(CallOnceManager manager) + { + if (manager != null) + { + manager.SignalNext(); + } + } + + internal void SignalNext() + { + if (queue == null) + { + return; + } + + IWaiter waiter = null; + + lock (ThisLock) + { + if (queue != null) + { + if (queue.Count > 0) + { + waiter = queue.Dequeue(); + } + else + { + queue = null; + } + } + } + + if (waiter != null) + { + ActionItem.Schedule(CallOnceManager.signalWaiter, waiter); + } + } + + static void SignalWaiter(object state) + { + ((IWaiter)state).Signal(); + } + + interface IWaiter + { + void Signal(); + } + + class AsyncWaiter : IWaiter + { + readonly AsyncManualResetEvent wait = new AsyncManualResetEvent(); + readonly CallOnceManager manager; + bool isTimedOut = false; + bool isSignaled = false; + int waitCount = 0; + + internal AsyncWaiter(CallOnceManager manager) + { + this.manager = manager; + } + + bool ShouldSignalNext + { + get { return isTimedOut && isSignaled; } + } + + void IWaiter.Signal() + { + wait.Set(); + CloseWaitHandle(); + + bool signalNext; + lock (manager.ThisLock) + { + isSignaled = true; + signalNext = ShouldSignalNext; + } + if (signalNext) + { + manager.SignalNext(); + } + } + + internal async Task WaitAsync(CancellationToken token) + { + try + { + if (!await wait.WaitAsync(token)) + { + bool signalNext; + lock (manager.ThisLock) + { + isTimedOut = true; + signalNext = ShouldSignalNext; + } + if (signalNext) + { + manager.SignalNext(); + } + } + } + finally + { + CloseWaitHandle(); + } + + return !isTimedOut; + } + + void CloseWaitHandle() + { + if (Interlocked.Increment(ref waitCount) == 2) + { + wait.Dispose(); + } + } + } + } + + internal class SessionIdleManager + { + IChannelBinder binder; + ServiceChannel channel; + long idleTicks; + long lastActivity; + Timer timer; + static Action timerCallback; + bool didIdleAbort; + bool isTimerCancelled; + object thisLock; + bool? isNeeded = null; + + public SessionIdleManager() { } + + internal SessionIdleManager UseIfNeeded(IChannelBinder binder, TimeSpan idle) + { + if (isNeeded.HasValue) + { + return isNeeded.Value ? this : null; + } + + if (binder.HasSession && (idle != TimeSpan.MaxValue)) + { + this.binder = binder; + timer = new Timer(new TimerCallback(GetTimerCallback()), this, idle, TimeSpan.FromMilliseconds(-1)); + idleTicks = Ticks.FromTimeSpan(idle); + thisLock = new object(); + isNeeded = true; + return this; + } + else + { + isNeeded = false; + return null; + } + } + + internal bool DidIdleAbort + { + get + { + lock (thisLock) + { + return didIdleAbort; + } + } + } + + internal void CancelTimer() + { + lock (thisLock) + { + isTimerCancelled = true; + timer.Change(TimeSpan.FromMilliseconds(-1), TimeSpan.FromMilliseconds(-1)); + } + } + + internal void CompletedActivity() + { + Interlocked.Exchange(ref lastActivity, Ticks.Now); + } + + internal void RegisterChannel(ServiceChannel channel, out bool didIdleAbort) + { + lock (thisLock) + { + this.channel = channel; + didIdleAbort = this.didIdleAbort; + } + } + + static Action GetTimerCallback() + { + if (SessionIdleManager.timerCallback == null) + { + SessionIdleManager.timerCallback = SessionIdleManager.TimerCallback; + } + return SessionIdleManager.timerCallback; + } + + static void TimerCallback(object state) + { + ((SessionIdleManager)state).TimerCallback(); + } + + void TimerCallback() + { + // This reads lastActivity atomically without changing its value. + // (it only sets if it is zero, and then it sets it to zero). + long last = Interlocked.CompareExchange(ref lastActivity, 0, 0); + long abortTime = last + idleTicks; + + lock (thisLock) + { + long ticksNow = Ticks.Now; + if (ticksNow > abortTime) + { + //if (TD.SessionIdleTimeoutIsEnabled()) + //{ + // string listenUri = string.Empty; + // if (this.binder.ListenUri != null) + // { + // listenUri = this.binder.ListenUri.AbsoluteUri; + // } + + // TD.SessionIdleTimeout(listenUri); + //} + + didIdleAbort = true; + if (channel != null) + { + channel.Abort(); + } + else + { + binder.Abort(); + } + } + else + { + if (!isTimerCancelled && binder.Channel.State != CommunicationState.Faulted && binder.Channel.State != CommunicationState.Closed) + { + timer.Change(Ticks.ToTimeSpan(abortTime - ticksNow), TimeSpan.FromMilliseconds(-1)); + } + } + } + } + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/ServiceChannelFactory.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/ServiceChannelFactory.cs new file mode 100644 index 000000000..5c5f69e8c --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/ServiceChannelFactory.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Reflection; +using CoreWCF.Description; + +namespace CoreWCF.Channels +{ + public class ServiceChannelFactory + { + private delegate object CreateProxyDelegate(MessageDirection direction, ServiceChannel serviceChannel); + private static IDictionary s_createProxyDelegateCache = new ConcurrentDictionary(); + + internal static object CreateProxy(Type interfaceType, Type proxiedType, MessageDirection direction, ServiceChannel serviceChannel) + { + if (!proxiedType.GetTypeInfo().IsInterface) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.SFxChannelFactoryTypeMustBeInterface)); + } + + CreateProxyDelegate createProxyDelegate; + if (!s_createProxyDelegateCache.TryGetValue(proxiedType, out createProxyDelegate)) + { + MethodInfo method = typeof(ServiceChannelFactory).GetMethod(nameof(CreateProxyWithType), + BindingFlags.NonPublic | BindingFlags.Static); + MethodInfo generic = method.MakeGenericMethod(proxiedType); + createProxyDelegate = (CreateProxyDelegate)generic.CreateDelegate(typeof(CreateProxyDelegate)); + s_createProxyDelegateCache[proxiedType] = createProxyDelegate; + } + + return createProxyDelegate(direction, serviceChannel); + } + + internal static object CreateProxyWithType(MessageDirection direction, ServiceChannel serviceChannel) + { + if (!typeof(TChannel).GetTypeInfo().IsInterface) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.SFxChannelFactoryTypeMustBeInterface)); + } + + return ServiceChannelProxy.CreateProxy(direction, serviceChannel); + } + + internal static ServiceChannel GetServiceChannel(object transparentProxy) + { + ServiceChannelProxy proxy = transparentProxy as ServiceChannelProxy; + + if (proxy != null) + return proxy.GetServiceChannel(); + else + return null; + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/ServiceChannelProxy.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/ServiceChannelProxy.cs new file mode 100644 index 000000000..42bece6a1 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/ServiceChannelProxy.cs @@ -0,0 +1,706 @@ +using System; +using System.Collections; +using System.Globalization; +using System.Reflection; +using System.Threading.Tasks; +using CoreWCF.Runtime; +using CoreWCF.Description; +using CoreWCF.Diagnostics; +using CoreWCF.Dispatcher; +using System.Collections.Concurrent; +using System.Diagnostics.Contracts; +using System.Runtime; +using System.Runtime.CompilerServices; +using System.Threading; + +namespace CoreWCF.Channels +{ + public class ServiceChannelProxy : DispatchProxy, ICommunicationObject, IChannel, IClientChannel, IOutputChannel, IRequestChannel, IServiceChannel, IDuplexContextChannel + { + private const string activityIdSlotName = "E2ETrace.ActivityID"; + private Type _proxiedType; + private ServiceChannel _serviceChannel; + private ImmutableClientRuntime _proxyRuntime; + private MethodDataCache _methodDataCache; + + // ServiceChannelProxy serves 2 roles. It is the TChannel proxy called by the client, + // and it is also the handler of those calls that dispatches them to the appropriate service channel. + // In .Net Remoting terms, it is conceptually the same as a RealProxy and a TransparentProxy combined. + internal static TChannel CreateProxy(MessageDirection direction, ServiceChannel serviceChannel) + { + TChannel proxy = DispatchProxy.Create(); + if (proxy == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.FailedToCreateTypedProxy, typeof(TChannel)))); + } + + ServiceChannelProxy channelProxy = (ServiceChannelProxy)(object)proxy; + channelProxy._proxiedType = typeof(TChannel); + channelProxy._serviceChannel = serviceChannel; + channelProxy._proxyRuntime = serviceChannel.ClientRuntime.GetRuntime(); + channelProxy._methodDataCache = new MethodDataCache(); + return proxy; + } + + //Workaround is to set the activityid in remoting call's LogicalCallContext + + // Override ToString() to reveal only the expected proxy type, not the generated one + public override string ToString() + { + return _proxiedType.ToString(); + } + + private MethodData GetMethodData(MethodCall methodCall) + { + MethodData methodData; + MethodBase method = methodCall.MethodBase; + if (_methodDataCache.TryGetMethodData(method, out methodData)) + { + return methodData; + } + + bool canCacheMessageData; + + Type declaringType = method.DeclaringType; + if (declaringType == typeof(object) && method == typeof(object).GetMethod("GetType")) + { + canCacheMessageData = true; + methodData = new MethodData(method, MethodType.GetType); + } + else if (declaringType.IsAssignableFrom(_serviceChannel.GetType())) + { + canCacheMessageData = true; + methodData = new MethodData(method, MethodType.Channel); + } + else + { + ProxyOperationRuntime operation = _proxyRuntime.GetOperation(method, methodCall.Args, out canCacheMessageData); + + if (operation == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.Format(SR.SFxMethodNotSupportedOnCallback1, method.Name))); + } + + MethodType methodType; + + if (operation.IsTaskCall(methodCall)) + { + methodType = MethodType.TaskService; + } + else if (operation.IsSyncCall(methodCall)) + { + methodType = MethodType.Service; + } + else if (operation.IsBeginCall(methodCall)) + { + methodType = MethodType.BeginService; + } + else + { + methodType = MethodType.EndService; + } + + methodData = new MethodData(method, methodType, operation); + } + + if (canCacheMessageData) + { + _methodDataCache.SetMethodData(methodData); + } + + return methodData; + } + + internal ServiceChannel GetServiceChannel() + { + return _serviceChannel; + } + + protected override object Invoke(MethodInfo targetMethod, object[] args) + { + if (args == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("args"); + } + + if (targetMethod == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.InvalidTypedProxyMethodHandle, _proxiedType.Name))); + } + + MethodCall methodCall = new MethodCall(targetMethod, args); + MethodData methodData = GetMethodData(methodCall); + + switch (methodData.MethodType) + { + case MethodType.Service: + return InvokeService(methodCall, methodData.Operation); + case MethodType.BeginService: + return InvokeBeginService(methodCall, methodData.Operation); + case MethodType.EndService: + return InvokeEndService(methodCall, methodData.Operation); + case MethodType.TaskService: + return InvokeTaskService(methodCall, methodData.Operation); + case MethodType.Channel: + return InvokeChannel(methodCall); + case MethodType.GetType: + return InvokeGetType(methodCall); + default: + Fx.Assert("Invalid proxy method type"); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Invalid proxy method type"))); + } + } + + internal static class TaskCreator + { + public static Task CreateTask(ServiceChannel channel, MethodCall methodCall, ProxyOperationRuntime operation) + { + if (operation.TaskTResult == ServiceReflector.VoidType) + { + return TaskCreator.CreateTask(channel, operation, methodCall.Args); + } + return TaskCreator.CreateGenericTask(channel, operation, methodCall.Args); + } + + private static Task CreateGenericTask(ServiceChannel channel, ProxyOperationRuntime operation, object[] inputParameters) + { + TaskCompletionSourceProxy tcsp = new TaskCompletionSourceProxy(operation.TaskTResult); + Action, object> completeCallDelegate = (antecedent, obj) => + { + var tcsProxy = obj as TaskCompletionSourceProxy; + Contract.Assert(tcsProxy != null); + if (antecedent.IsFaulted) tcsProxy.TrySetException(antecedent.Exception.InnerException); + else if (antecedent.IsCanceled) tcsProxy.TrySetCanceled(); + else tcsProxy.TrySetResult(antecedent.Result); + }; + + try + { + channel.CallAsync(operation.Action, operation.IsOneWay, operation, inputParameters, + Array.Empty()).ContinueWith(completeCallDelegate, tcsp); + } + catch (Exception e) + { + tcsp.TrySetException(e); + } + + return tcsp.Task; + } + + private static Task CreateTask(ServiceChannel channel, ProxyOperationRuntime operation, object[] inputParameters) + { + TaskCompletionSource tcs = new TaskCompletionSource(); + + Action, object> completeCallDelegate = (antecedent, obj) => + { + var tcsObj = obj as TaskCompletionSource; + Contract.Assert(tcsObj != null); + if (antecedent.IsFaulted) tcsObj.TrySetException(antecedent.Exception.InnerException); + else if (antecedent.IsCanceled) tcsObj.TrySetCanceled(); + else tcsObj.TrySetResult(antecedent.Result); + }; + + + try + { + channel.CallAsync(operation.Action, operation.IsOneWay, operation, inputParameters, + Array.Empty()).ContinueWith(completeCallDelegate, tcs); + } + catch (Exception e) + { + tcs.TrySetException(e); + } + + return tcs.Task; + } + } + + private class TaskCompletionSourceProxy + { + private TaskCompletionSourceInfo _tcsInfo; + private object _tcsInstance; + + public TaskCompletionSourceProxy(Type resultType) + { + _tcsInfo = TaskCompletionSourceInfo.GetTaskCompletionSourceInfo(resultType); + _tcsInstance = Activator.CreateInstance(_tcsInfo.GenericType); + } + + public Task Task { get { return (Task)_tcsInfo.TaskProperty.GetValue(_tcsInstance); } } + + public bool TrySetResult(object result) + { + return (bool)_tcsInfo.TrySetResultMethod.Invoke(_tcsInstance, new object[] { result }); + } + + public bool TrySetException(Exception exception) + { + return (bool)_tcsInfo.TrySetExceptionMethod.Invoke(_tcsInstance, new object[] { exception }); + } + + public bool TrySetCanceled() + { + return (bool)_tcsInfo.TrySetCanceledMethod.Invoke(_tcsInstance, Array.Empty()); + } + } + + private class TaskCompletionSourceInfo + { + private static ConcurrentDictionary s_cache = new ConcurrentDictionary(); + + public TaskCompletionSourceInfo(Type resultType) + { + ResultType = resultType; + Type tcsType = typeof(TaskCompletionSource<>); + GenericType = tcsType.MakeGenericType(new Type[] { resultType }); + TaskProperty = GenericType.GetTypeInfo().GetDeclaredProperty("Task"); + TrySetResultMethod = GenericType.GetTypeInfo().GetDeclaredMethod("TrySetResult"); + TrySetExceptionMethod = GenericType.GetRuntimeMethod("TrySetException", new Type[] { typeof(Exception) }); + TrySetCanceledMethod = GenericType.GetRuntimeMethod("TrySetCanceled", Array.Empty()); + } + + public Type ResultType { get; private set; } + public Type GenericType { get; private set; } + public PropertyInfo TaskProperty { get; private set; } + public MethodInfo TrySetResultMethod { get; private set; } + public MethodInfo TrySetExceptionMethod { get; set; } + public MethodInfo TrySetCanceledMethod { get; set; } + + public static TaskCompletionSourceInfo GetTaskCompletionSourceInfo(Type resultType) + { + return s_cache.GetOrAdd(resultType, t => new TaskCompletionSourceInfo(t)); + } + } + + private object InvokeTaskService(MethodCall methodCall, ProxyOperationRuntime operation) + { + Task task = TaskCreator.CreateTask(_serviceChannel, methodCall, operation); + return task; + } + + private object InvokeChannel(MethodCall methodCall) + { + //string activityName = null; + //ActivityType activityType = ActivityType.Unknown; + //if (DiagnosticUtility.ShouldUseActivity) + //{ + // if (ServiceModelActivity.Current == null || + // ServiceModelActivity.Current.ActivityType != ActivityType.Close) + // { + // MethodData methodData = this.GetMethodData(methodCall); + // if (methodData.MethodBase.DeclaringType == typeof(System.ServiceModel.ICommunicationObject) + // && methodData.MethodBase.Name.Equals("Close", StringComparison.Ordinal)) + // { + // activityName = SR.Format(SR.ActivityClose, _serviceChannel.GetType().FullName); + // activityType = ActivityType.Close; + // } + // } + //} + + //using (ServiceModelActivity activity = string.IsNullOrEmpty(activityName) ? null : ServiceModelActivity.CreateBoundedActivity()) + //{ + // if (DiagnosticUtility.ShouldUseActivity) + // { + // ServiceModelActivity.Start(activity, activityName, activityType); + // } + return ExecuteMessage(_serviceChannel, methodCall); + //} + } + + private object InvokeGetType(MethodCall methodCall) + { + return _proxiedType; + } + + private object InvokeBeginService(MethodCall methodCall, ProxyOperationRuntime operation) + { + AsyncCallback callback; + object asyncState; + object[] ins = operation.MapAsyncBeginInputs(methodCall, out callback, out asyncState); + object ret = _serviceChannel.BeginCall(operation.Action, operation.IsOneWay, operation, ins, callback, asyncState); + return ret; + } + + private object InvokeEndService(MethodCall methodCall, ProxyOperationRuntime operation) + { + IAsyncResult result; + object[] outs; + operation.MapAsyncEndInputs(methodCall, out result, out outs); + object ret = _serviceChannel.EndCall(operation.Action, outs, result); + operation.MapAsyncOutputs(methodCall, outs, ref ret); + return ret; + } + + private object InvokeService(MethodCall methodCall, ProxyOperationRuntime operation) + { + object[] outs; + object[] ins = operation.MapSyncInputs(methodCall, out outs); + object ret; + using (TaskHelpers.RunTaskContinuationsOnOurThreads()) + { + ret = _serviceChannel.CallAsync(operation.Action, operation.IsOneWay, operation, ins, outs).GetAwaiter().GetResult(); + } + operation.MapSyncOutputs(methodCall, outs, ref ret); + return ret; + } + + private object ExecuteMessage(object target, MethodCall methodCall) + { + MethodBase targetMethod = methodCall.MethodBase; + + object[] args = methodCall.Args; + object returnValue = null; + try + { + returnValue = targetMethod.Invoke(target, args); + } + catch (TargetInvocationException e) + { + throw e.InnerException; + } + + return returnValue; + } + + internal class MethodDataCache + { + private MethodData[] _methodDatas; + + public MethodDataCache() + { + _methodDatas = new MethodData[4]; + } + + private object ThisLock + { + get { return this; } + } + + public bool TryGetMethodData(MethodBase method, out MethodData methodData) + { + lock (ThisLock) + { + MethodData[] methodDatas = _methodDatas; + int index = FindMethod(methodDatas, method); + if (index >= 0) + { + methodData = methodDatas[index]; + return true; + } + else + { + methodData = new MethodData(); + return false; + } + } + } + + private static int FindMethod(MethodData[] methodDatas, MethodBase methodToFind) + { + for (int i = 0; i < methodDatas.Length; i++) + { + MethodBase method = methodDatas[i].MethodBase; + if (method == null) + { + break; + } + if (method == methodToFind) + { + return i; + } + } + return -1; + } + + public void SetMethodData(MethodData methodData) + { + lock (ThisLock) + { + int index = FindMethod(_methodDatas, methodData.MethodBase); + if (index < 0) + { + for (int i = 0; i < _methodDatas.Length; i++) + { + if (_methodDatas[i].MethodBase == null) + { + _methodDatas[i] = methodData; + return; + } + } + MethodData[] newMethodDatas = new MethodData[_methodDatas.Length * 2]; + Array.Copy(_methodDatas, newMethodDatas, _methodDatas.Length); + newMethodDatas[_methodDatas.Length] = methodData; + _methodDatas = newMethodDatas; + } + } + } + } + + internal enum MethodType + { + Service, + BeginService, + EndService, + Channel, + Object, + GetType, + TaskService + } + + internal struct MethodData + { + private MethodBase _methodBase; + private MethodType _methodType; + private ProxyOperationRuntime _operation; + + public MethodData(MethodBase methodBase, MethodType methodType) + : this(methodBase, methodType, null) + { + } + + public MethodData(MethodBase methodBase, MethodType methodType, ProxyOperationRuntime operation) + { + _methodBase = methodBase; + _methodType = methodType; + _operation = operation; + } + + public MethodBase MethodBase + { + get { return _methodBase; } + } + + public MethodType MethodType + { + get { return _methodType; } + } + + public ProxyOperationRuntime Operation + { + get { return _operation; } + } + } + + #region Channel interfaces + // These channel methods exist only to implement additional channel interfaces for ServiceChannelProxy. + // This is required because clients can down-cast typed proxies to the these channel interfaces. + // On the desktop, the .Net Remoting layer allowed that type cast, and subsequent calls against the + // interface went back through the RealProxy and invoked the underlying ServiceChannel. + // Net Native and CoreClr do not have .Net Remoting and therefore cannot use that mechanism. + // But because typed proxies derive from ServiceChannelProxy, implementing these interfaces + // on ServiceChannelProxy permits casting the typed proxy to these interfaces. + // All interface implementations delegate directly to the underlying ServiceChannel. + T IChannel.GetProperty() + { + return _serviceChannel.GetProperty(); + } + + CommunicationState ICommunicationObject.State + { + get { return _serviceChannel.State; } + } + + event EventHandler ICommunicationObject.Closed + { + add { _serviceChannel.Closed += value; } + remove { _serviceChannel.Closed -= value; } + } + + event EventHandler ICommunicationObject.Closing + { + add { _serviceChannel.Closing += value; } + remove { _serviceChannel.Closing -= value; } + } + + event EventHandler ICommunicationObject.Faulted + { + add { _serviceChannel.Faulted += value; } + remove { _serviceChannel.Faulted -= value; } + } + + event EventHandler ICommunicationObject.Opened + { + add { _serviceChannel.Opened += value; } + remove { _serviceChannel.Opened -= value; } + } + + event EventHandler ICommunicationObject.Opening + { + add { _serviceChannel.Opening += value; } + remove { _serviceChannel.Opening -= value; } + } + + void ICommunicationObject.Abort() + { + _serviceChannel.Abort(); + } + + Task ICommunicationObject.CloseAsync() + { + return _serviceChannel.CloseAsync(); + } + + Task ICommunicationObject.CloseAsync(CancellationToken token) + { + return _serviceChannel.CloseAsync(token); + } + + Task ICommunicationObject.OpenAsync() + { + return _serviceChannel.OpenAsync(); + } + + Task ICommunicationObject.OpenAsync(CancellationToken token) + { + return _serviceChannel.OpenAsync(token); + } + + //Uri IClientChannel.Via + //{ + // get { return _serviceChannel.Via; } + //} + + event EventHandler IClientChannel.UnknownMessageReceived + { + add { ((IClientChannel)_serviceChannel).UnknownMessageReceived += value; } + remove { ((IClientChannel)_serviceChannel).UnknownMessageReceived -= value; } + } + + void IDisposable.Dispose() + { + ((IClientChannel)_serviceChannel).Dispose(); + } + + //bool IContextChannel.AllowOutputBatching + //{ + // get + // { + // return ((IContextChannel)_serviceChannel).AllowOutputBatching; + // } + // set + // { + // ((IContextChannel)_serviceChannel).AllowOutputBatching = value; + // } + //} + + IInputSession IContextChannel.InputSession + { + get { return ((IContextChannel)_serviceChannel).InputSession; } + } + + EndpointAddress IContextChannel.LocalAddress + { + get { return ((IContextChannel)_serviceChannel).LocalAddress; } + } + + TimeSpan IContextChannel.OperationTimeout + { + get + { + return ((IContextChannel)_serviceChannel).OperationTimeout; + } + set + { + ((IContextChannel)_serviceChannel).OperationTimeout = value; + } + } + + IOutputSession IContextChannel.OutputSession + { + get { return ((IContextChannel)_serviceChannel).OutputSession; } + } + + EndpointAddress IOutputChannel.RemoteAddress + { + get { return ((IContextChannel)_serviceChannel).RemoteAddress; } + } + + Uri IOutputChannel.Via + { + get { return _serviceChannel.Via; } + } + + EndpointAddress IContextChannel.RemoteAddress + { + get { return ((IContextChannel)_serviceChannel).RemoteAddress; } + } + + string IContextChannel.SessionId + { + get { return ((IContextChannel)_serviceChannel).SessionId; } + } + + IExtensionCollection IExtensibleObject.Extensions + { + get { return ((IContextChannel)_serviceChannel).Extensions; } + } + + Task IOutputChannel.SendAsync(Message message) + { + return _serviceChannel.SendAsync(message); + } + + Task IOutputChannel.SendAsync(Message message, CancellationToken token) + { + return _serviceChannel.SendAsync(message, token); + } + + Task IRequestChannel.RequestAsync(Message message) + { + return _serviceChannel.RequestAsync(message); + } + + Task IRequestChannel.RequestAsync(Message message, CancellationToken token) + { + return _serviceChannel.RequestAsync(message, token); + } + + public Task CloseOutputSessionAsync(CancellationToken token) + { + return ((IDuplexContextChannel)_serviceChannel).CloseOutputSessionAsync(token); + } + + EndpointAddress IRequestChannel.RemoteAddress + { + get { return ((IContextChannel)_serviceChannel).RemoteAddress; } + } + + Uri IRequestChannel.Via + { + get { return _serviceChannel.Via; } + } + + Uri IServiceChannel.ListenUri + { + get { return _serviceChannel.ListenUri; } + } + + public bool AutomaticInputSessionShutdown + { + get + { + return ((IDuplexContextChannel)_serviceChannel).AutomaticInputSessionShutdown; + } + + set + { + ((IDuplexContextChannel)_serviceChannel).AutomaticInputSessionShutdown = value; + } + } + + public InstanceContext CallbackInstance + { + get + { + return ((IDuplexContextChannel)_serviceChannel).CallbackInstance; + } + + set + { + ((IDuplexContextChannel)_serviceChannel).CallbackInstance = value; + } + } + #endregion // Channel interfaces + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/SessionOpenNotification.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/SessionOpenNotification.cs new file mode 100644 index 000000000..f6de63ee3 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/SessionOpenNotification.cs @@ -0,0 +1,12 @@ +namespace CoreWCF.Channels +{ + internal abstract class SessionOpenNotification + { + public abstract bool IsEnabled + { + get; + } + + public abstract void UpdateMessageProperties(MessageProperties inboundMessageProperties); + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/SingletonChannelAcceptor.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/SingletonChannelAcceptor.cs new file mode 100644 index 000000000..9dbf0dc29 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/SingletonChannelAcceptor.cs @@ -0,0 +1,222 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using CoreWCF.Runtime; + +namespace CoreWCF.Channels +{ + abstract class SingletonChannelAcceptor + : InputQueueChannelAcceptor + where ChannelInterfaceType : class, IChannel + where TChannel : /*ChannelInterfaceType,*/ InputQueueChannel + where QueueItemType : class, IDisposable + { + TChannel currentChannel; + object currentChannelLock = new object(); + static Action onInvokeDequeuedCallback; + + public SingletonChannelAcceptor(ChannelManagerBase channelManager) + : base(channelManager) + { + } + + public override Task AcceptChannelAsync(CancellationToken token) + { + EnsureChannelAvailable(); + return base.AcceptChannelAsync(token); + } + + protected TChannel GetCurrentChannel() + { + return currentChannel; + } + + TChannel EnsureChannelAvailable() + { + bool channelCreated = false; + TChannel newChannel; + + if ((newChannel = currentChannel) == null) + { + lock (currentChannelLock) + { + if (IsDisposed) + { + return null; + } + + if ((newChannel = currentChannel) == null) + { + newChannel = OnCreateChannel(); + newChannel.Closed += OnChannelClosed; + currentChannel = newChannel; + channelCreated = true; + } + } + } + + if (channelCreated) + { + EnqueueAndDispatch((ChannelInterfaceType)(object)newChannel); + } + + return newChannel; + } + + protected abstract TChannel OnCreateChannel(); + protected abstract void OnTraceMessageReceived(QueueItemType item); + + public void DispatchItems() + { + TChannel channel = EnsureChannelAvailable(); + if (channel != null) + { + channel.Dispatch(); + } + } + + public void Enqueue(QueueItemType item) + { + Enqueue(item, null); + } + + public void Enqueue(QueueItemType item, Action dequeuedCallback) + { + Enqueue(item, dequeuedCallback, true); + } + + public void Enqueue(QueueItemType item, Action dequeuedCallback, bool canDispatchOnThisThread) + { + TChannel channel = EnsureChannelAvailable(); + + if (channel != null) + { + channel.EnqueueAndDispatch(item, dequeuedCallback, canDispatchOnThisThread); + } + else + { + InvokeDequeuedCallback(dequeuedCallback, canDispatchOnThisThread); + item.Dispose(); + } + } + + public void Enqueue(Exception exception, Action dequeuedCallback) + { + Enqueue(exception, dequeuedCallback, true); + } + + public void Enqueue(Exception exception, Action dequeuedCallback, bool canDispatchOnThisThread) + { + TChannel channel = EnsureChannelAvailable(); + + if (channel != null) + { + channel.EnqueueAndDispatch(exception, dequeuedCallback, canDispatchOnThisThread); + } + else + { + InvokeDequeuedCallback(dequeuedCallback, canDispatchOnThisThread); + } + } + + public bool EnqueueWithoutDispatch(QueueItemType item, Action dequeuedCallback) + { + TChannel channel = EnsureChannelAvailable(); + + if (channel != null) + { + return channel.EnqueueWithoutDispatch(item, dequeuedCallback); + } + else + { + InvokeDequeuedCallback(dequeuedCallback, false); + item.Dispose(); + return false; + } + } + + public override bool EnqueueWithoutDispatch(Exception exception, Action dequeuedCallback) + { + TChannel channel = EnsureChannelAvailable(); + + if (channel != null) + { + return channel.EnqueueWithoutDispatch(exception, dequeuedCallback); + } + else + { + InvokeDequeuedCallback(dequeuedCallback, false); + return false; + } + } + + public void EnqueueAndDispatch(QueueItemType item, Action dequeuedCallback, bool canDispatchOnThisThread) + { + TChannel channel = EnsureChannelAvailable(); + + if (channel != null) + { + channel.EnqueueAndDispatch(item, dequeuedCallback, canDispatchOnThisThread); + } + else + { + InvokeDequeuedCallback(dequeuedCallback, canDispatchOnThisThread); + item.Dispose(); + } + } + + public override void EnqueueAndDispatch(Exception exception, Action dequeuedCallback, bool canDispatchOnThisThread) + { + TChannel channel = EnsureChannelAvailable(); + + if (channel != null) + { + channel.EnqueueAndDispatch(exception, dequeuedCallback, canDispatchOnThisThread); + } + else + { + InvokeDequeuedCallback(dequeuedCallback, canDispatchOnThisThread); + } + } + + protected void OnChannelClosed(object sender, EventArgs args) + { + IChannel channel = (IChannel)sender; + lock (currentChannelLock) + { + if (channel == currentChannel) + { + currentChannel = null; + } + } + } + + static void InvokeDequeuedCallback(Action dequeuedCallback, bool canDispatchOnThisThread) + { + if (dequeuedCallback != null) + { + if (canDispatchOnThisThread) + { + dequeuedCallback(); + return; + } + + if (onInvokeDequeuedCallback == null) + { + onInvokeDequeuedCallback = OnInvokeDequeuedCallback; + } + + ActionItem.Schedule(onInvokeDequeuedCallback, dequeuedCallback); + } + } + + static void OnInvokeDequeuedCallback(object state) + { + Fx.Assert(state != null, "SingletonChannelAcceptor.OnInvokeDequeuedCallback: (state != null)"); + + Action dequeuedCallback = (Action)state; + dequeuedCallback(); + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/SslStreamSecurityBindingElement.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/SslStreamSecurityBindingElement.cs new file mode 100644 index 000000000..e7cab449d --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/SslStreamSecurityBindingElement.cs @@ -0,0 +1,148 @@ +using CoreWCF.Security; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Net.Security; +using System.Security.Authentication; +using System.Text; + +namespace CoreWCF.Channels +{ + public class SslStreamSecurityBindingElement : StreamUpgradeBindingElement + { + IdentityVerifier identityVerifier; + bool requireClientCertificate; + SslProtocols sslProtocols; + + public SslStreamSecurityBindingElement() + { + requireClientCertificate = TransportDefaults.RequireClientCertificate; + sslProtocols = TransportDefaults.SslProtocols; + } + + protected SslStreamSecurityBindingElement(SslStreamSecurityBindingElement elementToBeCloned) + : base(elementToBeCloned) + { + identityVerifier = elementToBeCloned.identityVerifier; + requireClientCertificate = elementToBeCloned.requireClientCertificate; + sslProtocols = elementToBeCloned.sslProtocols; + } + + internal IdentityVerifier IdentityVerifier + { + get + { + if (identityVerifier == null) + { + identityVerifier = IdentityVerifier.CreateDefault(); + } + + return identityVerifier; + } + set + { + if (value == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value"); + } + + identityVerifier = value; + } + } + + [DefaultValue(TransportDefaults.RequireClientCertificate)] + public bool RequireClientCertificate + { + get + { + return requireClientCertificate; + } + set + { + requireClientCertificate = value; + } + } + + [DefaultValue(TransportDefaults.SslProtocols)] + public SslProtocols SslProtocols + { + get + { + return sslProtocols; + } + set + { + SslProtocolsHelper.Validate(value); + sslProtocols = value; + } + } + + public override IChannelListener BuildChannelListener(BindingContext context) + { + if (context == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("context"); + } + + context.BindingParameters.Add(this); + return context.BuildInnerChannelListener(); + } + + public override bool CanBuildChannelListener(BindingContext context) + { + if (context == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("context"); + } + + context.BindingParameters.Add(this); + return context.CanBuildInnerChannelListener(); + } + + public override BindingElement Clone() + { + return new SslStreamSecurityBindingElement(this); + } + + public override T GetProperty(BindingContext context) + { + if (context == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("context"); + } + if (typeof(T) == typeof(ISecurityCapabilities)) + { + return (T)(object)new SecurityCapabilities(RequireClientCertificate, true, RequireClientCertificate, + ProtectionLevel.EncryptAndSign, ProtectionLevel.EncryptAndSign); + } + else if (typeof(T) == typeof(IdentityVerifier)) + { + return (T)(object)IdentityVerifier; + } + else + { + return context.GetInnerProperty(); + } + } + + public override StreamUpgradeProvider BuildServerStreamUpgradeProvider(BindingContext context) + { + return SslStreamSecurityUpgradeProvider.CreateServerProvider(this, context); + } + + protected override bool IsMatch(BindingElement b) + { + if (b == null) + { + return false; + } + SslStreamSecurityBindingElement ssl = b as SslStreamSecurityBindingElement; + if (ssl == null) + { + return false; + } + + return requireClientCertificate == ssl.requireClientCertificate && sslProtocols == ssl.sslProtocols; + } + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/SslStreamSecurityUpgradeProvider.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/SslStreamSecurityUpgradeProvider.cs new file mode 100644 index 000000000..4651fe226 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/SslStreamSecurityUpgradeProvider.cs @@ -0,0 +1,367 @@ +using CoreWCF.IdentityModel.Policy; +using CoreWCF.IdentityModel.Selectors; +using CoreWCF.IdentityModel.Tokens; +using CoreWCF.Runtime; +using CoreWCF.Description; +using CoreWCF.Security; +using CoreWCF.Security.Tokens; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.IO; +using System.Net.Security; +using System.Security.Authentication; +using System.Security.Authentication.ExtendedProtection; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace CoreWCF.Channels +{ + class SslStreamSecurityUpgradeProvider : StreamSecurityUpgradeProvider, IStreamUpgradeChannelBindingProvider + { + SecurityTokenAuthenticator clientCertificateAuthenticator; + SecurityTokenProvider serverTokenProvider; + EndpointIdentity identity; + IdentityVerifier identityVerifier; + X509Certificate2 serverCertificate; + bool requireClientCertificate; + string scheme; + bool enableChannelBinding; + SslProtocols sslProtocols; + SecurityTokenManager clientSecurityTokenManager; + + SslStreamSecurityUpgradeProvider(IDefaultCommunicationTimeouts timeouts, SecurityTokenProvider serverTokenProvider, bool requireClientCertificate, SecurityTokenAuthenticator clientCertificateAuthenticator, string scheme, IdentityVerifier identityVerifier, SslProtocols sslProtocols) + : base(timeouts) + { + this.serverTokenProvider = serverTokenProvider; + this.requireClientCertificate = requireClientCertificate; + this.clientCertificateAuthenticator = clientCertificateAuthenticator; + this.identityVerifier = identityVerifier; + this.scheme = scheme; + this.sslProtocols = sslProtocols; + clientSecurityTokenManager = null; // Used for client but there's public api which need this and the compiler complains it's never assigned + } + + public static SslStreamSecurityUpgradeProvider CreateServerProvider( + SslStreamSecurityBindingElement bindingElement, BindingContext context) + { + SecurityCredentialsManager credentialProvider = + context.BindingParameters.Find(); + + if (credentialProvider == null) + { + credentialProvider = ServiceCredentials.CreateDefaultCredentials(); + } + + Uri listenUri = TransportSecurityHelpers.GetListenUri(context.ListenUriBaseAddress, context.ListenUriRelativeAddress); + SecurityTokenManager tokenManager = credentialProvider.CreateSecurityTokenManager(); + + RecipientServiceModelSecurityTokenRequirement serverCertRequirement = new RecipientServiceModelSecurityTokenRequirement(); + serverCertRequirement.TokenType = SecurityTokenTypes.X509Certificate; + serverCertRequirement.RequireCryptographicToken = true; + serverCertRequirement.KeyUsage = SecurityKeyUsage.Exchange; + serverCertRequirement.TransportScheme = context.Binding.Scheme; + serverCertRequirement.ListenUri = listenUri; + + SecurityTokenProvider tokenProvider = tokenManager.CreateSecurityTokenProvider(serverCertRequirement); + if (tokenProvider == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.ClientCredentialsUnableToCreateLocalTokenProvider, serverCertRequirement))); + } + + SecurityTokenAuthenticator certificateAuthenticator = + TransportSecurityHelpers.GetCertificateTokenAuthenticator(tokenManager, context.Binding.Scheme, listenUri); + + return new SslStreamSecurityUpgradeProvider(context.Binding, tokenProvider, bindingElement.RequireClientCertificate, + certificateAuthenticator, context.Binding.Scheme, bindingElement.IdentityVerifier, bindingElement.SslProtocols); + } + + public override EndpointIdentity Identity + { + get + { + if ((identity == null) && (serverCertificate != null)) + { + identity = SecurityUtils.GetServiceCertificateIdentity(serverCertificate); + } + return identity; + } + } + + public IdentityVerifier IdentityVerifier + { + get + { + return identityVerifier; + } + } + + public bool RequireClientCertificate + { + get + { + return requireClientCertificate; + } + } + + public X509Certificate2 ServerCertificate + { + get + { + return serverCertificate; + } + } + + public SecurityTokenAuthenticator ClientCertificateAuthenticator + { + get + { + if (clientCertificateAuthenticator == null) + { + clientCertificateAuthenticator = new X509SecurityTokenAuthenticator(X509ClientCertificateAuthentication.DefaultCertificateValidator); + } + + return clientCertificateAuthenticator; + } + } + + public SecurityTokenManager ClientSecurityTokenManager + { + get + { + return clientSecurityTokenManager; + } + } + + public string Scheme + { + get { return scheme; } + } + + public SslProtocols SslProtocols + { + get { return sslProtocols; } + } + + public override T GetProperty() + { + if (typeof(T) == typeof(IChannelBindingProvider) || typeof(T) == typeof(IStreamUpgradeChannelBindingProvider)) + { + return (T)(object)this; + } + return base.GetProperty(); + } + + ChannelBinding IStreamUpgradeChannelBindingProvider.GetChannelBinding(StreamUpgradeAcceptor upgradeAcceptor, ChannelBindingKind kind) + { + if (upgradeAcceptor == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("upgradeAcceptor"); + } + + SslStreamSecurityUpgradeAcceptor sslupgradeAcceptor = upgradeAcceptor as SslStreamSecurityUpgradeAcceptor; + + if (sslupgradeAcceptor == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("upgradeAcceptor", SR.Format(SR.UnsupportedUpgradeAcceptor, upgradeAcceptor.GetType())); + } + + if (kind != ChannelBindingKind.Endpoint) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("kind", SR.Format(SR.StreamUpgradeUnsupportedChannelBindingKind, GetType(), kind)); + } + + return sslupgradeAcceptor.ChannelBinding; + } + + void IChannelBindingProvider.EnableChannelBindingSupport() + { + enableChannelBinding = true; + } + + + bool IChannelBindingProvider.IsChannelBindingSupportEnabled + { + get + { + return enableChannelBinding; + } + } + + public override StreamUpgradeAcceptor CreateUpgradeAcceptor() + { + ThrowIfDisposedOrNotOpen(); + return new SslStreamSecurityUpgradeAcceptor(this); + } + + + + protected override void OnAbort() + { + if (clientCertificateAuthenticator != null) + { + SecurityUtils.AbortTokenAuthenticatorIfRequired(clientCertificateAuthenticator); + } + CleanupServerCertificate(); + } + + protected override async Task OnCloseAsync(CancellationToken token) + { + if (clientCertificateAuthenticator != null) + { + await SecurityUtils.CloseTokenAuthenticatorIfRequiredAsync(clientCertificateAuthenticator, token); + } + CleanupServerCertificate(); + } + + void SetupServerCertificate(SecurityToken token) + { + X509SecurityToken x509Token = token as X509SecurityToken; + if (x509Token == null) + { + SecurityUtils.AbortTokenProviderIfRequired(serverTokenProvider); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format( + SR.InvalidTokenProvided, serverTokenProvider.GetType(), typeof(X509SecurityToken)))); + } + serverCertificate = new X509Certificate2(x509Token.Certificate); + } + + void CleanupServerCertificate() + { + if (serverCertificate != null) + { + SecurityUtils.ResetCertificate(serverCertificate); + serverCertificate = null; + } + } + + protected override async Task OnOpenAsync(CancellationToken token) + { + await SecurityUtils.OpenTokenAuthenticatorIfRequiredAsync(ClientCertificateAuthenticator, token); + + if (serverTokenProvider != null) + { + await SecurityUtils.OpenTokenProviderIfRequiredAsync(serverTokenProvider, token); + // TODO: Solve issue with GetToken/GetTokenAsync needing timeouts and there is only a token available + SecurityToken securityToken = await serverTokenProvider.GetTokenAsync(token); + SetupServerCertificate(securityToken); + await SecurityUtils.CloseTokenProviderIfRequiredAsync(serverTokenProvider, token); + serverTokenProvider = null; + } + } + } + + class SslStreamSecurityUpgradeAcceptor : StreamSecurityUpgradeAcceptorBase + { + SslStreamSecurityUpgradeProvider parent; + SecurityMessageProperty clientSecurity; + // for audit + X509Certificate2 clientCertificate = null; + ChannelBinding channelBindingToken; + + public SslStreamSecurityUpgradeAcceptor(SslStreamSecurityUpgradeProvider parent) + : base(FramingUpgradeString.SslOrTls) + { + this.parent = parent; + clientSecurity = new SecurityMessageProperty(); + } + + internal ChannelBinding ChannelBinding + { + get + { + Fx.Assert(IsChannelBindingSupportEnabled, "A request for the ChannelBinding is not permitted without enabling ChannelBinding first (through the IChannelBindingProvider interface)"); + return channelBindingToken; + } + } + + internal bool IsChannelBindingSupportEnabled + { + get + { + return ((IChannelBindingProvider)parent).IsChannelBindingSupportEnabled; + } + } + + protected override async Task<(Stream,SecurityMessageProperty)> OnAcceptUpgradeAsync(Stream stream) + { + var sslStream = new SslStream(stream, false, ValidateRemoteCertificate); + + try + { + await sslStream.AuthenticateAsServerAsync(parent.ServerCertificate, parent.RequireClientCertificate, + parent.SslProtocols, false); + } + catch (AuthenticationException exception) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityNegotiationException(exception.Message, + exception)); + } + catch (IOException ioException) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityNegotiationException( + SR.Format(SR.NegotiationFailedIO, ioException.Message), ioException)); + } + + SecurityMessageProperty remoteSecurity = clientSecurity; + + if (IsChannelBindingSupportEnabled) + { + channelBindingToken = ChannelBindingUtility.GetToken(sslStream); + } + + return (sslStream, remoteSecurity); + } + + // callback from schannel + bool ValidateRemoteCertificate(object sender, X509Certificate certificate, X509Chain chain, + SslPolicyErrors sslPolicyErrors) + { + if (parent.RequireClientCertificate) + { + if (certificate == null) + { + return false; + } + // Note: add ref to handle since the caller will reset the cert after the callback return. + X509Certificate2 certificate2 = new X509Certificate2(certificate); + clientCertificate = certificate2; + try + { + SecurityToken token = new X509SecurityToken(certificate2, false); + ReadOnlyCollection authorizationPolicies = parent.ClientCertificateAuthenticator.ValidateToken(token); + clientSecurity = new SecurityMessageProperty(); + clientSecurity.TransportToken = new SecurityTokenSpecification(token, authorizationPolicies); + clientSecurity.ServiceSecurityContext = new ServiceSecurityContext(authorizationPolicies); + } + catch (SecurityTokenException e) + { + DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); + return false; + } + } + return true; + } + + public override SecurityMessageProperty GetRemoteSecurity() + { + if (clientSecurity.TransportToken != null) + { + return clientSecurity; + } + if (clientCertificate != null) + { + SecurityToken token = new X509SecurityToken(clientCertificate); + ReadOnlyCollection authorizationPolicies = SecurityUtils.NonValidatingX509Authenticator.ValidateToken(token); + clientSecurity = new SecurityMessageProperty(); + clientSecurity.TransportToken = new SecurityTokenSpecification(token, authorizationPolicies); + clientSecurity.ServiceSecurityContext = new ServiceSecurityContext(authorizationPolicies); + return clientSecurity; + } + return base.GetRemoteSecurity(); + } + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/StreamSecurityUpgradeAcceptor.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/StreamSecurityUpgradeAcceptor.cs new file mode 100644 index 000000000..bd422f43d --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/StreamSecurityUpgradeAcceptor.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Text; +using CoreWCF.Security; + +namespace CoreWCF.Channels +{ + public abstract class StreamSecurityUpgradeAcceptor : StreamUpgradeAcceptor + { + protected StreamSecurityUpgradeAcceptor() + { + } + + public abstract SecurityMessageProperty GetRemoteSecurity(); // works after call to AcceptUpgrade + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/StreamSecurityUpgradeAcceptorBase.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/StreamSecurityUpgradeAcceptorBase.cs new file mode 100644 index 000000000..3d54afe6a --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/StreamSecurityUpgradeAcceptorBase.cs @@ -0,0 +1,51 @@ +using CoreWCF.Security; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Threading.Tasks; + +namespace CoreWCF.Channels +{ + abstract class StreamSecurityUpgradeAcceptorBase : StreamSecurityUpgradeAcceptor + { + SecurityMessageProperty remoteSecurity; + bool securityUpgraded; + string upgradeString; + + protected StreamSecurityUpgradeAcceptorBase(string upgradeString) + { + this.upgradeString = upgradeString; + } + + public override async Task AcceptUpgradeAsync(Stream stream) + { + if (stream == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("stream"); + } + + (stream, remoteSecurity) = await OnAcceptUpgradeAsync(stream); + return stream; + } + + public override bool CanUpgrade(string contentType) + { + if (securityUpgraded) + { + return false; + } + + return (contentType == upgradeString); + } + + public override SecurityMessageProperty GetRemoteSecurity() + { + // this could be null if upgrade not completed. + return remoteSecurity; + } + + protected abstract Task<(Stream,SecurityMessageProperty)> OnAcceptUpgradeAsync(Stream stream); + } + +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/StreamSecurityUpgradeProvider.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/StreamSecurityUpgradeProvider.cs new file mode 100644 index 000000000..6b4d4a7ae --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/StreamSecurityUpgradeProvider.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoreWCF.Channels +{ + public abstract class StreamSecurityUpgradeProvider : StreamUpgradeProvider + { + protected StreamSecurityUpgradeProvider() + : base() + { + } + + protected StreamSecurityUpgradeProvider(IDefaultCommunicationTimeouts timeouts) + : base(timeouts) + { + } + + public abstract EndpointIdentity Identity + { + get; + } + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/StreamUpgradeAcceptor.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/StreamUpgradeAcceptor.cs new file mode 100644 index 000000000..a961f2547 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/StreamUpgradeAcceptor.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Threading.Tasks; + +namespace CoreWCF.Channels +{ + public abstract class StreamUpgradeAcceptor + { + protected StreamUpgradeAcceptor() + { + } + + public abstract bool CanUpgrade(string contentType); + + public abstract Task AcceptUpgradeAsync(Stream stream); + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/StreamUpgradeBindingElement.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/StreamUpgradeBindingElement.cs new file mode 100644 index 000000000..d73955be8 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/StreamUpgradeBindingElement.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoreWCF.Channels +{ + public abstract class StreamUpgradeBindingElement : BindingElement + { + protected StreamUpgradeBindingElement() + { + } + + protected StreamUpgradeBindingElement(StreamUpgradeBindingElement elementToBeCloned) + : base(elementToBeCloned) + { + } + + public abstract StreamUpgradeProvider BuildServerStreamUpgradeProvider(BindingContext context); + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/StreamUpgradeProvider.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/StreamUpgradeProvider.cs new file mode 100644 index 000000000..10fe8c66e --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/StreamUpgradeProvider.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoreWCF.Channels +{ + public abstract class StreamUpgradeProvider : CommunicationObject + { + TimeSpan closeTimeout; + TimeSpan openTimeout; + + protected StreamUpgradeProvider() + : this(null) + { + } + + protected StreamUpgradeProvider(IDefaultCommunicationTimeouts timeouts) + { + if (timeouts != null) + { + closeTimeout = timeouts.CloseTimeout; + openTimeout = timeouts.OpenTimeout; + } + else + { + closeTimeout = ServiceDefaults.CloseTimeout; + openTimeout = ServiceDefaults.OpenTimeout; + } + } + + protected override TimeSpan DefaultCloseTimeout + { + get { return closeTimeout; } + } + + protected override TimeSpan DefaultOpenTimeout + { + get { return closeTimeout; } + } + + public virtual T GetProperty() where T : class + { + return null; + } + + public abstract StreamUpgradeAcceptor CreateUpgradeAcceptor(); + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/TextMessageEncoderFactory.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/TextMessageEncoderFactory.cs new file mode 100644 index 000000000..426949bc9 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/TextMessageEncoderFactory.cs @@ -0,0 +1,778 @@ +using System; +using System.Diagnostics.Contracts; +using System.Globalization; +using System.IO; +using System.Net.Http.Headers; +using System.Text; +using System.Threading.Tasks; +using System.Xml; +using CoreWCF.Runtime; +using CoreWCF.Diagnostics; + +namespace CoreWCF.Channels +{ + internal class TextMessageEncoderFactory : MessageEncoderFactory + { + TextMessageEncoder messageEncoder; + //internal static ContentEncoding[] Soap11Content = GetContentEncodingMap(MessageVersion.Soap11WSAddressing10); + // I believe replacing Soap11WSAddressing10 with Soap11 should work + internal static ContentEncoding[] Soap11Content = GetContentEncodingMap(MessageVersion.Soap11); + internal static ContentEncoding[] Soap12Content = GetContentEncodingMap(MessageVersion.Soap12WSAddressing10); + internal static ContentEncoding[] SoapNoneContent = GetContentEncodingMap(MessageVersion.None); + internal const string Soap11MediaType = "text/xml"; + internal const string Soap12MediaType = "application/soap+xml"; + const string XmlMediaType = "application/xml"; + + public TextMessageEncoderFactory(MessageVersion version, Encoding writeEncoding, int maxReadPoolSize, int maxWritePoolSize, XmlDictionaryReaderQuotas quotas) + { + messageEncoder = new TextMessageEncoder(version, writeEncoding, maxReadPoolSize, maxWritePoolSize, quotas); + } + + public override MessageEncoder Encoder + { + get { return messageEncoder; } + } + + public override MessageVersion MessageVersion + { + get { return messageEncoder.MessageVersion; } + } + + public int MaxWritePoolSize + { + get { return messageEncoder.MaxWritePoolSize; } + } + + public int MaxReadPoolSize + { + get { return messageEncoder.MaxReadPoolSize; } + } + + public static Encoding[] GetSupportedEncodings() + { + Encoding[] supported = TextEncoderDefaults.SupportedEncodings; + Encoding[] enc = new Encoding[supported.Length]; + Array.Copy(supported, enc, supported.Length); + return enc; + } + + public XmlDictionaryReaderQuotas ReaderQuotas + { + get + { + return messageEncoder.ReaderQuotas; + } + } + + internal static string GetMediaType(MessageVersion version) + { + string mediaType = null; + if (version.Envelope == EnvelopeVersion.Soap12) + { + mediaType = TextMessageEncoderFactory.Soap12MediaType; + } + else if (version.Envelope == EnvelopeVersion.Soap11) + { + mediaType = TextMessageEncoderFactory.Soap11MediaType; + } + else if (version.Envelope == EnvelopeVersion.None) + { + mediaType = TextMessageEncoderFactory.XmlMediaType; + } + else + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException( + SR.Format(SR.EnvelopeVersionNotSupported, version.Envelope))); + } + return mediaType; + } + + internal static string GetContentType(string mediaType, Encoding encoding) + { + return string.Format(CultureInfo.InvariantCulture, "{0}; charset={1}", mediaType, TextEncoderDefaults.EncodingToCharSet(encoding)); + } + + static ContentEncoding[] GetContentEncodingMap(MessageVersion version) + { + Encoding[] readEncodings = TextMessageEncoderFactory.GetSupportedEncodings(); + string media = GetMediaType(version); + ContentEncoding[] map = new ContentEncoding[readEncodings.Length]; + for (int i = 0; i < readEncodings.Length; i++) + { + ContentEncoding contentEncoding = new ContentEncoding(); + contentEncoding.contentType = GetContentType(media, readEncodings[i]); + contentEncoding.encoding = readEncodings[i]; + map[i] = contentEncoding; + } + return map; + } + + internal static Encoding GetEncodingFromContentType(string contentType, ContentEncoding[] contentMap) + { + if (contentType == null) + { + return null; + } + + // Check for known/expected content types + for (int i = 0; i < contentMap.Length; i++) + { + if (contentMap[i].contentType == contentType) + { + return contentMap[i].encoding; + } + } + + // then some heuristic matches (since System.Mime.ContentType is a performance hit) + // start by looking for a parameter. + + // If none exists, we don't have an encoding + int semiColonIndex = contentType.IndexOf(';'); + if (semiColonIndex == -1) + { + return null; + } + + // optimize for charset being the first parameter + int charsetValueIndex = -1; + + // for Indigo scenarios, we'll have "; charset=", so check for the c + if ((contentType.Length > semiColonIndex + 11) // need room for parameter + charset + '=' + && contentType[semiColonIndex + 2] == 'c' + && string.Compare("charset=", 0, contentType, semiColonIndex + 2, 8, StringComparison.OrdinalIgnoreCase) == 0) + { + charsetValueIndex = semiColonIndex + 10; + } + else + { + // look for charset= somewhere else in the message + int paramIndex = contentType.IndexOf("charset=", semiColonIndex + 1, StringComparison.OrdinalIgnoreCase); + if (paramIndex != -1) + { + // validate there's only whitespace or semi-colons beforehand + for (int i = paramIndex - 1; i >= semiColonIndex; i--) + { + if (contentType[i] == ';') + { + charsetValueIndex = paramIndex + 8; + break; + } + + if (contentType[i] == '\n') + { + if (i == semiColonIndex || contentType[i - 1] != '\r') + { + break; + } + + i--; + continue; + } + + if (contentType[i] != ' ' + && contentType[i] != '\t') + { + break; + } + } + } + } + + string charSet; + Encoding enc; + + // we have a possible charset value. If it's easy to parse, do so + if (charsetValueIndex != -1) + { + // get the next semicolon + semiColonIndex = contentType.IndexOf(';', charsetValueIndex); + if (semiColonIndex == -1) + { + charSet = contentType.Substring(charsetValueIndex); + } + else + { + charSet = contentType.Substring(charsetValueIndex, semiColonIndex - charsetValueIndex); + } + + // and some minimal quote stripping + if (charSet.Length > 2 && charSet[0] == '"' && charSet[charSet.Length - 1] == '"') + { + charSet = charSet.Substring(1, charSet.Length - 2); + } + + if (TryGetEncodingFromCharSet(charSet, out enc)) + { + return enc; + } + } + + // our quick heuristics failed. fall back to System.Net + try + { + MediaTypeHeaderValue parsedContentType = MediaTypeHeaderValue.Parse(contentType); + charSet = parsedContentType.CharSet; + } + catch (FormatException e) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ProtocolException(SR.EncoderBadContentType, e)); + } + + if (TryGetEncodingFromCharSet(charSet, out enc)) + return enc; + + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ProtocolException(SR.Format(SR.EncoderUnrecognizedCharSet, charSet))); + } + + internal static bool TryGetEncodingFromCharSet(string charSet, out Encoding encoding) + { + encoding = null; + if (charSet == null || charSet.Length == 0) + return true; + + return TextEncoderDefaults.TryGetEncoding(charSet, out encoding); + } + + internal class ContentEncoding + { + internal string contentType; + internal Encoding encoding; + } + + class TextMessageEncoder : MessageEncoder + { + int maxReadPoolSize; + int maxWritePoolSize; + + // Double-checked locking pattern requires volatile for read/write synchronization + volatile SynchronizedPool streamedWriterPool; + volatile SynchronizedPool streamedReaderPool; + volatile SynchronizedPool bufferedReaderPool; + volatile SynchronizedPool bufferedWriterPool; + volatile SynchronizedPool recycledStatePool; + + object thisLock; + string contentType; + string mediaType; + Encoding writeEncoding; + MessageVersion version; + bool optimizeWriteForUTF8; + const int maxPooledXmlReadersPerMessage = 2; + XmlDictionaryReaderQuotas readerQuotas; + XmlDictionaryReaderQuotas bufferedReadReaderQuotas; + OnXmlDictionaryReaderClose onStreamedReaderClose; + ContentEncoding[] contentEncodingMap; + + public TextMessageEncoder(MessageVersion version, Encoding writeEncoding, int maxReadPoolSize, int maxWritePoolSize, XmlDictionaryReaderQuotas quotas) + { + if (version == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("version"); + if (writeEncoding == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writeEncoding"); + + TextEncoderDefaults.ValidateEncoding(writeEncoding); + this.writeEncoding = writeEncoding; + optimizeWriteForUTF8 = IsUTF8Encoding(writeEncoding); + + thisLock = new object(); + + this.version = version; + this.maxReadPoolSize = maxReadPoolSize; + this.maxWritePoolSize = maxWritePoolSize; + + readerQuotas = new XmlDictionaryReaderQuotas(); + quotas.CopyTo(readerQuotas); + + bufferedReadReaderQuotas = EncoderHelpers.GetBufferedReadQuotas(readerQuotas); + + onStreamedReaderClose = new OnXmlDictionaryReaderClose(ReturnStreamedReader); + + mediaType = TextMessageEncoderFactory.GetMediaType(version); + contentType = TextMessageEncoderFactory.GetContentType(mediaType, writeEncoding); + if (version.Envelope == EnvelopeVersion.Soap12) + { + contentEncodingMap = TextMessageEncoderFactory.Soap12Content; + } + else if (version.Envelope == EnvelopeVersion.Soap11) + { + contentEncodingMap = TextMessageEncoderFactory.Soap11Content; + } + else if (version.Envelope == EnvelopeVersion.None) + { + contentEncodingMap = TextMessageEncoderFactory.SoapNoneContent; + } + else + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException( + SR.Format(SR.EnvelopeVersionNotSupported, version.Envelope))); + } + } + + static bool IsUTF8Encoding(Encoding encoding) + { + return encoding.WebName == "utf-8"; + } + + public override string ContentType + { + get { return contentType; } + } + + public int MaxWritePoolSize + { + get { return maxWritePoolSize; } + } + + public int MaxReadPoolSize + { + get { return maxReadPoolSize; } + } + + public XmlDictionaryReaderQuotas ReaderQuotas + { + get + { + return readerQuotas; + } + } + + public override string MediaType + { + get { return mediaType; } + } + + public override MessageVersion MessageVersion + { + get { return version; } + } + + object ThisLock + { + get { return thisLock; } + } + + + internal override bool IsCharSetSupported(string charSet) + { + Encoding tmp; + return TextEncoderDefaults.TryGetEncoding(charSet, out tmp); + } + + public override bool IsContentTypeSupported(string contentType) + { + if (contentType == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("contentType"); + } + + if (base.IsContentTypeSupported(contentType)) + { + return true; + } + + // we support a few extra content types for "none" + if (MessageVersion == MessageVersion.None) + { + const string rss1MediaType = "text/xml"; + const string rss2MediaType = "application/rss+xml"; + const string atomMediaType = "application/atom+xml"; + const string htmlMediaType = "text/html"; + + if (IsContentTypeSupported(contentType, rss1MediaType, rss1MediaType)) + { + return true; + } + if (IsContentTypeSupported(contentType, rss2MediaType, rss2MediaType)) + { + return true; + } + if (IsContentTypeSupported(contentType, htmlMediaType, atomMediaType)) + { + return true; + } + if (IsContentTypeSupported(contentType, atomMediaType, atomMediaType)) + { + return true; + } + // application/xml checked by base method + } + + return false; + } + + public override Message ReadMessage(ArraySegment buffer, BufferManager bufferManager, string contentType) + { + if (bufferManager == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(bufferManager)); + + Message message; + + UTF8BufferedMessageData messageData = TakeBufferedReader(); + messageData.Encoding = GetEncodingFromContentType(contentType, contentEncodingMap); + messageData.Open(buffer, bufferManager); + RecycledMessageState messageState = messageData.TakeMessageState(); + if (messageState == null) + messageState = new RecycledMessageState(); + message = new BufferedMessage(messageData, messageState); + + message.Properties.Encoder = this; + + return message; + } + + public override Message ReadMessage(Stream stream, int maxSizeOfHeaders, string contentType) + { + if (stream == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException(nameof(stream))); + + XmlReader reader = TakeStreamedReader(stream, GetEncodingFromContentType(contentType, contentEncodingMap)); + Message message = Message.CreateMessage(reader, maxSizeOfHeaders, version); + message.Properties.Encoder = this; + + return message; + } + + public override ArraySegment WriteMessage(Message message, int maxMessageSize, BufferManager bufferManager, int messageOffset) + { + if (message == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException(nameof(message))); + if (bufferManager == null) + throw TraceUtility.ThrowHelperError(new ArgumentNullException(nameof(bufferManager)), message); + if (maxMessageSize < 0) + throw TraceUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(maxMessageSize), maxMessageSize, + SR.ValueMustBeNonNegative), message); + if (messageOffset < 0 || messageOffset > maxMessageSize) + throw TraceUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(messageOffset), messageOffset, + SR.Format(SR.ValueMustBeInRange, 0, maxMessageSize)), message); + + ThrowIfMismatchedMessageVersion(message); + + message.Properties.Encoder = this; + TextBufferedMessageWriter messageWriter = TakeBufferedWriter(); + ArraySegment messageData = messageWriter.WriteMessage(message, bufferManager, messageOffset, maxMessageSize); + ReturnMessageWriter(messageWriter); + + return messageData; + } + + public override void WriteMessage(Message message, Stream stream) + { + if (message == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException(nameof(message))); + if (stream == null) + throw TraceUtility.ThrowHelperError(new ArgumentNullException(nameof(stream)), message); + ThrowIfMismatchedMessageVersion(message); + + message.Properties.Encoder = this; + XmlDictionaryWriter xmlWriter = TakeStreamedWriter(stream); + if (optimizeWriteForUTF8) + { + message.WriteMessage(xmlWriter); + } + else + { + xmlWriter.WriteStartDocument(); + message.WriteMessage(xmlWriter); + xmlWriter.WriteEndDocument(); + } + xmlWriter.Flush(); + ReturnStreamedWriter(xmlWriter); + } + + public override async Task WriteMessageAsync(Message message, Stream stream) + { + if (message == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(message)); + if (stream == null) + throw TraceUtility.ThrowHelperError(new ArgumentNullException(nameof(stream)), message); + ThrowIfMismatchedMessageVersion(message); + + message.Properties.Encoder = this; + XmlDictionaryWriter xmlWriter = TakeStreamedWriter(stream); + if (optimizeWriteForUTF8) + { + await message.WriteMessageAsync(xmlWriter); + } + else + { + xmlWriter.WriteStartDocument(); + await message.WriteMessageAsync(xmlWriter); + xmlWriter.WriteEndDocument(); + } + + await xmlWriter.FlushAsync(); + ReturnStreamedWriter(xmlWriter); + } + + XmlDictionaryWriter TakeStreamedWriter(Stream stream) + { + if (streamedWriterPool == null) + { + lock (ThisLock) + { + if (streamedWriterPool == null) + { + streamedWriterPool = new SynchronizedPool(maxWritePoolSize); + } + } + } + XmlDictionaryWriter xmlWriter = streamedWriterPool.Take(); + if (xmlWriter == null) + { + xmlWriter = XmlDictionaryWriter.CreateTextWriter(stream, writeEncoding, false); + } + // TODO: Use the reinitialization API's once moved to .Net Standard 2.0 + //else + //{ + // ((IXmlTextWriterInitializer)xmlWriter).SetOutput(stream, this.writeEncoding, false); + //} + return xmlWriter; + } + + void ReturnStreamedWriter(XmlWriter xmlWriter) + { + xmlWriter.Dispose(); + // TODO: Use the reinitialization API's once moved to .Net Standard 2.0 + //streamedWriterPool.Return((XmlDictionaryWriter)xmlWriter); + } + + TextBufferedMessageWriter TakeBufferedWriter() + { + if (bufferedWriterPool == null) + { + lock (ThisLock) + { + if (bufferedWriterPool == null) + { + bufferedWriterPool = new SynchronizedPool(maxWritePoolSize); + } + } + } + + TextBufferedMessageWriter messageWriter = bufferedWriterPool.Take(); + if (messageWriter == null) + { + messageWriter = new TextBufferedMessageWriter(this); + } + return messageWriter; + } + + void ReturnMessageWriter(TextBufferedMessageWriter messageWriter) + { + bufferedWriterPool.Return(messageWriter); + } + + XmlReader TakeStreamedReader(Stream stream, Encoding enc) + { + if (streamedReaderPool == null) + { + lock (ThisLock) + { + if (streamedReaderPool == null) + { + streamedReaderPool = new SynchronizedPool(maxReadPoolSize); + } + } + } + XmlDictionaryReader xmlReader = streamedReaderPool.Take(); + if (xmlReader == null) + { + xmlReader = XmlDictionaryReader.CreateTextReader(stream, enc, readerQuotas, null); + } + // TODO: Use the reinitialization API's once moved to .Net Standard 2.0 + //else + //{ + // ((IXmlTextReaderInitializer)xmlReader).SetInput(stream, enc, this.readerQuotas, onStreamedReaderClose); + //} + return xmlReader; + } + + void ReturnStreamedReader(XmlDictionaryReader xmlReader) + { + streamedReaderPool.Return(xmlReader); + } + + XmlDictionaryWriter CreateWriter(Stream stream) + { + return XmlDictionaryWriter.CreateTextWriter(stream, writeEncoding, false); + } + + UTF8BufferedMessageData TakeBufferedReader() + { + if (bufferedReaderPool == null) + { + lock (ThisLock) + { + if (bufferedReaderPool == null) + { + bufferedReaderPool = new SynchronizedPool(maxReadPoolSize); + } + } + } + UTF8BufferedMessageData messageData = bufferedReaderPool.Take(); + if (messageData == null) + { + messageData = new UTF8BufferedMessageData(this, maxPooledXmlReadersPerMessage); + } + return messageData; + } + + void ReturnBufferedData(UTF8BufferedMessageData messageData) + { + bufferedReaderPool.Return(messageData); + } + + SynchronizedPool RecycledStatePool + { + get + { + if (recycledStatePool == null) + { + lock (ThisLock) + { + if (recycledStatePool == null) + { + recycledStatePool = new SynchronizedPool(maxReadPoolSize); + } + } + } + return recycledStatePool; + } + } + + static readonly byte[] xmlDeclarationStartText = { (byte)'<', (byte)'?', (byte)'x', (byte)'m', (byte)'l' }; + static readonly byte[] version10Text = { (byte)'v', (byte)'e', (byte)'r', (byte)'s', (byte)'i', (byte)'o', (byte)'n', (byte)'=', (byte)'"', (byte)'1', (byte)'.', (byte)'0', (byte)'"' }; + static readonly byte[] encodingText = { (byte)'e', (byte)'n', (byte)'c', (byte)'o', (byte)'d', (byte)'i', (byte)'n', (byte)'g', (byte)'=' }; + + class UTF8BufferedMessageData : BufferedMessageData + { + TextMessageEncoder messageEncoder; + Pool readerPool; + OnXmlDictionaryReaderClose onClose; + Encoding encoding; + + const int additionalNodeSpace = 1024; + + public UTF8BufferedMessageData(TextMessageEncoder messageEncoder, int maxReaderPoolSize) + : base(messageEncoder.RecycledStatePool) + { + this.messageEncoder = messageEncoder; + readerPool = new Pool(maxReaderPoolSize); + onClose = new OnXmlDictionaryReaderClose(OnXmlReaderClosed); + } + + internal Encoding Encoding + { + set + { + encoding = value; + } + } + + public override MessageEncoder MessageEncoder + { + get { return messageEncoder; } + } + + public override XmlDictionaryReaderQuotas Quotas + { + get { return messageEncoder.bufferedReadReaderQuotas; } + } + + protected override void OnClosed() + { + messageEncoder.ReturnBufferedData(this); + } + + protected override XmlDictionaryReader TakeXmlReader() + { + ArraySegment buffer = Buffer; + + XmlDictionaryReader xmlReader = readerPool.Take(); + if (xmlReader == null) + { + // TODO: Use the reinitialization API's once moved to .Net Standard 2.0 + xmlReader = XmlDictionaryReader.CreateTextReader(buffer.Array, buffer.Offset, buffer.Count, /*this.encoding,*/ Quotas/*, onClose*/); + } + // TODO: Use the reinitialization API's once moved to .Net Standard 2.0 + //else + //{ + // ((IXmlTextReaderInitializer)xmlReader).SetInput(buffer.Array, buffer.Offset, buffer.Count, this.encoding, this.Quotas, onClose); + //} + + return xmlReader; + } + + protected override void ReturnXmlReader(XmlDictionaryReader xmlReader) + { + if (xmlReader != null) + { + readerPool.Return(xmlReader); + } + } + } + + class TextBufferedMessageWriter : BufferedMessageWriter + { + TextMessageEncoder messageEncoder; + //XmlDictionaryWriter writer; + + public TextBufferedMessageWriter(TextMessageEncoder messageEncoder) + { + this.messageEncoder = messageEncoder; + } + + protected override void OnWriteStartMessage(XmlDictionaryWriter writer) + { + if (!messageEncoder.optimizeWriteForUTF8) + writer.WriteStartDocument(); + } + + protected override void OnWriteEndMessage(XmlDictionaryWriter writer) + { + if (!messageEncoder.optimizeWriteForUTF8) + writer.WriteEndDocument(); + } + + protected override XmlDictionaryWriter TakeXmlWriter(Stream stream) + { + if (messageEncoder.optimizeWriteForUTF8) + { + //XmlDictionaryWriter returnedWriter = writer; + //if (returnedWriter == null) + //{ + // returnedWriter = XmlDictionaryWriter.CreateTextWriter(stream, messageEncoder.writeEncoding, false); + //} + //else + //{ + // writer = null; + // ((IXmlTextWriterInitializer)returnedWriter).SetOutput(stream, messageEncoder.writeEncoding, false); + //} + //return returnedWriter; + // TODO: Use IXmlTextWriterInitializer when moved to .Net Standard 2.0 + return XmlDictionaryWriter.CreateTextWriter(stream, messageEncoder.writeEncoding, false); + } + else + { + return messageEncoder.CreateWriter(stream); + } + } + + protected override void ReturnXmlWriter(XmlDictionaryWriter writer) + { + Contract.Assert(writer != null, "writer MUST NOT be null"); + writer.Flush(); + writer.Dispose(); + + // TODO: Use IXmlTextWriterInitializer reuse once moved to .Net Standard 2.0 + //if (messageEncoder.optimizeWriteForUTF8) + //{ + // if (this.writer == null) + // this.writer = writer; + //} + } + } + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/TextMessageEncodingBindingElement.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/TextMessageEncodingBindingElement.cs new file mode 100644 index 000000000..d04de3d33 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/TextMessageEncodingBindingElement.cs @@ -0,0 +1,206 @@ +using System; +using System.Text; +using System.Xml; + +namespace CoreWCF.Channels +{ + public sealed class TextMessageEncodingBindingElement : MessageEncodingBindingElement + { + int _maxReadPoolSize; + int _maxWritePoolSize; + XmlDictionaryReaderQuotas _readerQuotas; + MessageVersion _messageVersion; + Encoding _writeEncoding; + + public TextMessageEncodingBindingElement() + : this(MessageVersion.Default, TextEncoderDefaults.Encoding) + { + } + + public TextMessageEncodingBindingElement(MessageVersion messageVersion, Encoding writeEncoding) + { + if (messageVersion == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(messageVersion)); + + if (writeEncoding == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(writeEncoding)); + + TextEncoderDefaults.ValidateEncoding(writeEncoding); + + _maxReadPoolSize = EncoderDefaults.MaxReadPoolSize; + _maxWritePoolSize = EncoderDefaults.MaxWritePoolSize; + _readerQuotas = new XmlDictionaryReaderQuotas(); + EncoderDefaults.ReaderQuotas.CopyTo(_readerQuotas); + _messageVersion = messageVersion; + _writeEncoding = writeEncoding; + } + + TextMessageEncodingBindingElement(TextMessageEncodingBindingElement elementToBeCloned) + : base(elementToBeCloned) + { + _maxReadPoolSize = elementToBeCloned._maxReadPoolSize; + _maxWritePoolSize = elementToBeCloned._maxWritePoolSize; + _readerQuotas = new XmlDictionaryReaderQuotas(); + elementToBeCloned._readerQuotas.CopyTo(_readerQuotas); + _writeEncoding = elementToBeCloned._writeEncoding; + _messageVersion = elementToBeCloned._messageVersion; + } + + public int MaxReadPoolSize + { + get + { + return _maxReadPoolSize; + } + set + { + if (value <= 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(value), value, + SR.ValueMustBePositive)); + } + _maxReadPoolSize = value; + } + } + + public int MaxWritePoolSize + { + get + { + return _maxWritePoolSize; + } + set + { + if (value <= 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(value), value, + SR.ValueMustBePositive)); + } + _maxWritePoolSize = value; + } + } + + public XmlDictionaryReaderQuotas ReaderQuotas + { + get + { + return _readerQuotas; + } + set + { + if (value == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(value)); + value.CopyTo(_readerQuotas); + } + } + + public override MessageVersion MessageVersion + { + get + { + return _messageVersion; + } + set + { + if (value == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value"); + } + + _messageVersion = value; + } + } + + public Encoding WriteEncoding + { + get + { + return _writeEncoding; + } + set + { + if (value == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value"); + } + + TextEncoderDefaults.ValidateEncoding(value); + _writeEncoding = value; + } + } + + public override IChannelListener BuildChannelListener(BindingContext context) + { + return InternalBuildChannelListener(context); + } + + public override bool CanBuildChannelListener(BindingContext context) + { + return InternalCanBuildChannelListener(context); + } + + public override BindingElement Clone() + { + return new TextMessageEncodingBindingElement(this); + } + + public override MessageEncoderFactory CreateMessageEncoderFactory() + { + return new TextMessageEncoderFactory(MessageVersion, WriteEncoding, MaxReadPoolSize, MaxWritePoolSize, ReaderQuotas); + } + + public override T GetProperty(BindingContext context) + { + if (context == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("context"); + } + if (typeof(T) == typeof(XmlDictionaryReaderQuotas)) + { + return (T)(object)_readerQuotas; + } + else + { + return base.GetProperty(context); + } + } + + internal override bool CheckEncodingVersion(EnvelopeVersion version) + { + return _messageVersion.Envelope == version; + } + + protected override bool IsMatch(BindingElement b) + { + if (!base.IsMatch(b)) + return false; + + TextMessageEncodingBindingElement text = b as TextMessageEncodingBindingElement; + if (text == null) + return false; + if (_maxReadPoolSize != text.MaxReadPoolSize) + return false; + if (_maxWritePoolSize != text.MaxWritePoolSize) + return false; + + // compare XmlDictionaryReaderQuotas + if (_readerQuotas.MaxStringContentLength != text.ReaderQuotas.MaxStringContentLength) + return false; + if (_readerQuotas.MaxArrayLength != text.ReaderQuotas.MaxArrayLength) + return false; + if (_readerQuotas.MaxBytesPerRead != text.ReaderQuotas.MaxBytesPerRead) + return false; + if (_readerQuotas.MaxDepth != text.ReaderQuotas.MaxDepth) + return false; + if (_readerQuotas.MaxNameTableCharCount != text.ReaderQuotas.MaxNameTableCharCount) + return false; + + if (WriteEncoding.EncodingName != text.WriteEncoding.EncodingName) + return false; + if (!MessageVersion.IsMatch(text.MessageVersion)) + return false; + + return true; + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/TransportBindingElement.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/TransportBindingElement.cs new file mode 100644 index 000000000..bbe9cbd85 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/TransportBindingElement.cs @@ -0,0 +1,143 @@ +using System; +using System.Collections.ObjectModel; +using System.Xml; + +namespace CoreWCF.Channels +{ + public abstract class TransportBindingElement : BindingElement + { + bool manualAddressing; + long maxBufferPoolSize; + long maxReceivedMessageSize; + + protected TransportBindingElement() + { + manualAddressing = TransportDefaults.ManualAddressing; + maxBufferPoolSize = TransportDefaults.MaxBufferPoolSize; + maxReceivedMessageSize = TransportDefaults.MaxReceivedMessageSize; + } + + protected TransportBindingElement(TransportBindingElement elementToBeCloned) + { + manualAddressing = elementToBeCloned.manualAddressing; + maxBufferPoolSize = elementToBeCloned.maxBufferPoolSize; + maxReceivedMessageSize = elementToBeCloned.maxReceivedMessageSize; + } + + [System.ComponentModel.DefaultValueAttribute(false)] + public virtual bool ManualAddressing + { + get + { + return manualAddressing; + } + + set + { + manualAddressing = value; + } + } + + public virtual long MaxBufferPoolSize + { + get + { + return maxBufferPoolSize; + } + set + { + if (value < 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(value), value, + SR.ValueMustBeNonNegative)); + } + maxBufferPoolSize = value; + } + } + + [System.ComponentModel.DefaultValueAttribute((long)65536)] + public virtual long MaxReceivedMessageSize + { + get + { + return maxReceivedMessageSize; + } + set + { + if (value <= 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(value), value, + SR.ValueMustBePositive)); + } + maxReceivedMessageSize = value; + } + } + + public abstract string Scheme { get; } + public override T GetProperty(BindingContext context) + { + if (context == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("context"); + } + + + // to cover all our bases, let's iterate through the BindingParameters to make sure + // we haven't missed a query (since we're the Transport and we're at the bottom) + Collection bindingElements = new Collection(); + foreach (var param in context.BindingParameters) + { + if (param is BindingElement) + { + bindingElements.Add((BindingElement)param); + } + } + + T result = default(T); + for (int i = 0; i < bindingElements.Count; i++) + { + result = bindingElements[i].GetIndividualProperty(); + if (result != default(T)) + { + return result; + } + } + + if (typeof(T) == typeof(MessageVersion)) + { + return (T)(object)TransportDefaults.GetDefaultMessageEncoderFactory().MessageVersion; + } + + if (typeof(T) == typeof(XmlDictionaryReaderQuotas)) + { + return (T)(object)new XmlDictionaryReaderQuotas(); + } + + return null; + } + + protected override bool IsMatch(BindingElement b) + { + if (b == null) + { + return false; + } + TransportBindingElement transport = b as TransportBindingElement; + if (transport == null) + { + return false; + } + if (maxBufferPoolSize != transport.MaxBufferPoolSize) + { + return false; + } + if (maxReceivedMessageSize != transport.MaxReceivedMessageSize) + { + return false; + } + return true; + } + + public virtual Type MiddlewareType { get; } = null; + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/TransportDefaults.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/TransportDefaults.cs new file mode 100644 index 000000000..818b73da8 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/TransportDefaults.cs @@ -0,0 +1,163 @@ +using CoreWCF.Security; +using System; +using System.Net.Security; +using System.Security.Authentication; +using System.Text; +using System.Xml; + +namespace CoreWCF.Channels +{ + internal static class EncoderDefaults + { + internal const int MaxReadPoolSize = 64; + internal const int MaxWritePoolSize = 16; + + internal const int BufferedReadDefaultMaxDepth = 128; + internal const int BufferedReadDefaultMaxStringContentLength = int.MaxValue; + internal const int BufferedReadDefaultMaxArrayLength = int.MaxValue; + internal const int BufferedReadDefaultMaxBytesPerRead = int.MaxValue; + internal const int BufferedReadDefaultMaxNameTableCharCount = int.MaxValue; + + internal const CompressionFormat DefaultCompressionFormat = CompressionFormat.None; + + internal static readonly XmlDictionaryReaderQuotas ReaderQuotas = new XmlDictionaryReaderQuotas(); + + internal static bool IsDefaultReaderQuotas(XmlDictionaryReaderQuotas quotas) + { + return quotas.ModifiedQuotas == 0x00; + } + } + + internal static class TextEncoderDefaults + { + internal static readonly Encoding Encoding = Encoding.GetEncoding(EncodingString, new EncoderExceptionFallback(), + new DecoderExceptionFallback()); + + internal const string EncodingString = "utf-8"; + + internal static readonly Encoding[] SupportedEncodings = + { + Encoding.UTF8, Encoding.Unicode, + Encoding.BigEndianUnicode + }; + + internal static readonly CharSetEncoding[] CharSetEncodings = + { + new CharSetEncoding("utf-8", Encoding.UTF8), + new CharSetEncoding("utf-16LE", Encoding.Unicode), + new CharSetEncoding("utf-16BE", Encoding.BigEndianUnicode), + new CharSetEncoding("utf-16", null), // Ignore. Ambiguous charSet, so autodetect. + new CharSetEncoding(null, null), // CharSet omitted, so autodetect. + }; + + + internal static void ValidateEncoding(Encoding encoding) + { + string charSet = encoding.WebName; + Encoding[] supportedEncodings = SupportedEncodings; + for (int i = 0; i < supportedEncodings.Length; i++) + { + if (charSet == supportedEncodings[i].WebName) + return; + } + + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new ArgumentException(SR.Format(SR.MessageTextEncodingNotSupported, charSet), nameof(encoding))); + } + + internal static string EncodingToCharSet(Encoding encoding) + { + string webName = encoding.WebName; + CharSetEncoding[] charSetEncodings = CharSetEncodings; + for (int i = 0; i < charSetEncodings.Length; i++) + { + Encoding enc = charSetEncodings[i].Encoding; + if (enc == null) + continue; + + if (enc.WebName == webName) + return charSetEncodings[i].CharSet; + } + return null; + } + + internal static bool TryGetEncoding(string charSet, out Encoding encoding) + { + CharSetEncoding[] charSetEncodings = CharSetEncodings; + + // Quick check for exact equality + for (int i = 0; i < charSetEncodings.Length; i++) + { + if (charSetEncodings[i].CharSet == charSet) + { + encoding = charSetEncodings[i].Encoding; + return true; + } + } + + // Check for case insensative match + for (int i = 0; i < charSetEncodings.Length; i++) + { + string compare = charSetEncodings[i].CharSet; + if (compare == null) + continue; + + if (compare.Equals(charSet, StringComparison.OrdinalIgnoreCase)) + { + encoding = charSetEncodings[i].Encoding; + return true; + } + } + + encoding = null; + return false; + } + + internal class CharSetEncoding + { + internal string CharSet; + internal Encoding Encoding; + + internal CharSetEncoding(string charSet, Encoding enc) + { + CharSet = charSet; + Encoding = enc; + } + } + } + + static class BinaryEncoderDefaults + { + internal static EnvelopeVersion EnvelopeVersion { get { return EnvelopeVersion.Soap12; } } + internal static BinaryVersion BinaryVersion { get { return BinaryVersion.Version1; } } + internal const int MaxSessionSize = 2048; + } + + internal static class TransportDefaults + { + internal const bool ExtractGroupsForWindowsAccounts = SspiSecurityTokenProvider.DefaultExtractWindowsGroupClaims; + internal const bool ManualAddressing = false; + internal const long MaxReceivedMessageSize = 65536; + internal const int MaxBufferSize = (int) MaxReceivedMessageSize; + internal const long MaxBufferPoolSize = 512 * 1024; + internal const int MaxFaultSize = MaxBufferSize; + internal const bool RequireClientCertificate = false; + internal const int TcpUriDefaultPort = 808; + + internal const SslProtocols SslProtocols = System.Security.Authentication.SslProtocols.Tls | + System.Security.Authentication.SslProtocols.Tls11 | + System.Security.Authentication.SslProtocols.Tls12; + + internal static MessageEncoderFactory GetDefaultMessageEncoderFactory() + { + return new BinaryMessageEncodingBindingElement().CreateMessageEncoderFactory(); + } + } + + internal static class ConnectionOrientedTransportDefaults + { + internal const bool AllowNtlm = SspiSecurityTokenProvider.DefaultAllowNtlm; + internal const int ConnectionBufferSize = 8192; + internal const ProtectionLevel ProtectionLevel = System.Net.Security.ProtectionLevel.EncryptAndSign; + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/TransportSecurityHelpers.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/TransportSecurityHelpers.cs new file mode 100644 index 000000000..b6cb2f8f5 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/TransportSecurityHelpers.cs @@ -0,0 +1,153 @@ +using CoreWCF.IdentityModel.Selectors; +using CoreWCF.IdentityModel.Tokens; +using CoreWCF.Security; +using CoreWCF.Security.Tokens; +using System; +using System.Collections.Generic; +using System.Net; +using System.Security.Principal; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace CoreWCF.Channels +{ + internal static class TransportSecurityHelpers + { + static async Task GetTokenAsync(SecurityTokenProvider tokenProvider, CancellationToken token) + where T : SecurityToken + { + SecurityToken result = await tokenProvider.GetTokenAsync(token); + if ((result != null) && !(result is T)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format( + SR.InvalidTokenProvided, tokenProvider.GetType(), typeof(T)))); + } + return result as T; + } + + // used by server WindowsStream security (from Open) + public static async Task<(NetworkCredential, bool)> GetSspiCredentialAsync(SecurityTokenManager credentialProvider, + SecurityTokenRequirement sspiTokenRequirement, CancellationToken token) + { + bool extractGroupsForWindowsAccounts = TransportDefaults.ExtractGroupsForWindowsAccounts; + NetworkCredential result = null; + + if (credentialProvider != null) + { + SecurityTokenProvider tokenProvider = credentialProvider.CreateSecurityTokenProvider(sspiTokenRequirement); + if (tokenProvider != null) + { + await SecurityUtils.OpenTokenProviderIfRequiredAsync(tokenProvider, token); + bool success = false; + try + { + TokenImpersonationLevel dummyImpersonationLevel; + bool dummyAllowNtlm; + (result, extractGroupsForWindowsAccounts, dummyImpersonationLevel, dummyAllowNtlm) = await GetSspiCredentialAsync((SspiSecurityTokenProvider)tokenProvider, token); + + success = true; + } + finally + { + if (!success) + { + SecurityUtils.AbortTokenProviderIfRequired(tokenProvider); + } + } + await SecurityUtils.CloseTokenProviderIfRequiredAsync(tokenProvider, token); + } + } + + return (result, extractGroupsForWindowsAccounts); + } + + // core Cred lookup code + static async Task<(NetworkCredential, bool, TokenImpersonationLevel, bool)> GetSspiCredentialAsync(SspiSecurityTokenProvider tokenProvider, CancellationToken cancellationToken) + { + NetworkCredential credential = null; + bool extractGroupsForWindowsAccounts = TransportDefaults.ExtractGroupsForWindowsAccounts; + TokenImpersonationLevel impersonationLevel = TokenImpersonationLevel.Identification; + bool allowNtlm = ConnectionOrientedTransportDefaults.AllowNtlm; + + if (tokenProvider != null) + { + SspiSecurityToken token = await TransportSecurityHelpers.GetTokenAsync(tokenProvider, cancellationToken); + if (token != null) + { + extractGroupsForWindowsAccounts = token.ExtractGroupsForWindowsAccounts; + impersonationLevel = token.ImpersonationLevel; + allowNtlm = token.AllowNtlm; + if (token.NetworkCredential != null) + { + credential = token.NetworkCredential; + SecurityUtils.FixNetworkCredential(ref credential); + } + } + } + + // Initialize to the default value if no token provided. A partial trust app should not have access to the + // default network credentials but should be able to provide credentials. The DefaultNetworkCredentials + // getter will throw under partial trust. + if (credential == null) + { + credential = CredentialCache.DefaultNetworkCredentials; + } + + return (credential, extractGroupsForWindowsAccounts, impersonationLevel, allowNtlm); + } + + public static SecurityTokenRequirement CreateSspiTokenRequirement(string transportScheme, Uri listenUri) + { + RecipientServiceModelSecurityTokenRequirement tokenRequirement = new RecipientServiceModelSecurityTokenRequirement(); + tokenRequirement.TransportScheme = transportScheme; + tokenRequirement.RequireCryptographicToken = false; + tokenRequirement.ListenUri = listenUri; + tokenRequirement.TokenType = ServiceModelSecurityTokenTypes.SspiCredential; + return tokenRequirement; + } + + public static SecurityTokenAuthenticator GetCertificateTokenAuthenticator(SecurityTokenManager tokenManager, string transportScheme, Uri listenUri) + { + RecipientServiceModelSecurityTokenRequirement clientAuthRequirement = new RecipientServiceModelSecurityTokenRequirement(); + clientAuthRequirement.TokenType = SecurityTokenTypes.X509Certificate; + clientAuthRequirement.RequireCryptographicToken = true; + clientAuthRequirement.KeyUsage = SecurityKeyUsage.Signature; + clientAuthRequirement.TransportScheme = transportScheme; + clientAuthRequirement.ListenUri = listenUri; + SecurityTokenResolver dummy; + return tokenManager.CreateSecurityTokenAuthenticator(clientAuthRequirement, out dummy); + } + + public static Uri GetListenUri(Uri baseAddress, string relativeAddress) + { + Uri fullUri = baseAddress; + + // Ensure that baseAddress Path does end with a slash if we have a relative address + if (!string.IsNullOrEmpty(relativeAddress)) + { + if (!baseAddress.AbsolutePath.EndsWith("/", StringComparison.Ordinal)) + { + UriBuilder uriBuilder = new UriBuilder(baseAddress); + FixIpv6Hostname(uriBuilder, baseAddress); + uriBuilder.Path = uriBuilder.Path + "/"; + baseAddress = uriBuilder.Uri; + } + + fullUri = new Uri(baseAddress, relativeAddress); + } + + return fullUri; + } + + // Moved from TcpChannelListener + internal static void FixIpv6Hostname(UriBuilder uriBuilder, Uri originalUri) + { + if (originalUri.HostNameType == UriHostNameType.IPv6) + { + string ipv6Host = originalUri.DnsSafeHost; + uriBuilder.Host = string.Concat("[", ipv6Host, "]"); + } + } + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/UnderstoodHeaders.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/UnderstoodHeaders.cs new file mode 100644 index 000000000..c9ce1a4ff --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/UnderstoodHeaders.cs @@ -0,0 +1,50 @@ +using System.Collections; +using System.Collections.Generic; + +namespace CoreWCF.Channels +{ + internal sealed class UnderstoodHeaders : IEnumerable + { + MessageHeaders messageHeaders; + bool modified; + + internal UnderstoodHeaders(MessageHeaders messageHeaders, bool modified) + { + this.messageHeaders = messageHeaders; + this.modified = modified; + } + + internal bool Modified + { + get { return modified; } + set { modified = value; } + } + + public void Add(MessageHeaderInfo headerInfo) + { + messageHeaders.AddUnderstood(headerInfo); + modified = true; + } + + public bool Contains(MessageHeaderInfo headerInfo) + { + return messageHeaders.IsUnderstood(headerInfo); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public IEnumerator GetEnumerator() + { + return messageHeaders.GetUnderstoodEnumerator(); + } + + public void Remove(MessageHeaderInfo headerInfo) + { + messageHeaders.RemoveUnderstood(headerInfo); + modified = true; + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/UriEx.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/UriEx.cs new file mode 100644 index 000000000..bef267356 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/UriEx.cs @@ -0,0 +1,10 @@ +namespace CoreWCF.Channels +{ + // TODO: Remove once moved to .Net Standard 1.7 + internal class UriEx + { + public const string UriSchemeHttp = "http"; + public const string UriSchemeHttps = "https"; + public const string UriSchemeNetTcp = "net.tcp"; + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/WindowsStreamSecurityBindingElement.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/WindowsStreamSecurityBindingElement.cs new file mode 100644 index 000000000..6bb6a781f --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/WindowsStreamSecurityBindingElement.cs @@ -0,0 +1,69 @@ +using CoreWCF.Security; +using System; +using System.Collections.Generic; +using System.Net.Security; +using System.Text; + +namespace CoreWCF.Channels +{ + public class WindowsStreamSecurityBindingElement : StreamUpgradeBindingElement + { + ProtectionLevel _protectionLevel; + + public WindowsStreamSecurityBindingElement() + : base() + { + _protectionLevel = ConnectionOrientedTransportDefaults.ProtectionLevel; + } + + protected WindowsStreamSecurityBindingElement(WindowsStreamSecurityBindingElement elementToBeCloned) + : base(elementToBeCloned) + { + _protectionLevel = elementToBeCloned._protectionLevel; + } + + public ProtectionLevel ProtectionLevel + { + get + { + return _protectionLevel; + } + set + { + ProtectionLevelHelper.Validate(value); + _protectionLevel = value; + } + } + + public override BindingElement Clone() + { + return new WindowsStreamSecurityBindingElement(this); + } + + public override StreamUpgradeProvider BuildServerStreamUpgradeProvider(BindingContext context) + { + return new WindowsStreamSecurityUpgradeProvider(this, context, false); + } + + public override T GetProperty(BindingContext context) + { + if (context == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(context)); + } + + if (typeof(T) == typeof(ISecurityCapabilities)) + { + return (T)(object)new SecurityCapabilities(true, true, true, _protectionLevel, _protectionLevel); + } + else if (typeof(T) == typeof(IdentityVerifier)) + { + return (T)(object)IdentityVerifier.CreateDefault(); + } + else + { + return context.GetInnerProperty(); + } + } + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Channels/WindowsStreamSecurityUpgradeProvider.cs b/src/CoreWCF.Primitives/src/CoreWCF/Channels/WindowsStreamSecurityUpgradeProvider.cs new file mode 100644 index 000000000..c6c4ecd63 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Channels/WindowsStreamSecurityUpgradeProvider.cs @@ -0,0 +1,192 @@ +using CoreWCF.IdentityModel; +using CoreWCF.IdentityModel.Policy; +using CoreWCF.IdentityModel.Selectors; +using CoreWCF.IdentityModel.Tokens; +using CoreWCF.Description; +using CoreWCF.Security; +using System; +using System.Collections.ObjectModel; +using System.IO; +using System.Net; +using System.Net.Security; +using System.Security.Authentication; +using System.Security.Principal; +using System.Threading; +using System.Threading.Tasks; + +namespace CoreWCF.Channels +{ + class WindowsStreamSecurityUpgradeProvider : StreamSecurityUpgradeProvider + { + EndpointIdentity _identity; + SecurityTokenManager securityTokenManager; + bool isClient; + Uri listenUri; + + public WindowsStreamSecurityUpgradeProvider(WindowsStreamSecurityBindingElement bindingElement, + BindingContext context, bool isClient) + : base(context.Binding) + { + ExtractGroupsForWindowsAccounts = TransportDefaults.ExtractGroupsForWindowsAccounts; + ProtectionLevel = bindingElement.ProtectionLevel; + Scheme = context.Binding.Scheme; + this.isClient = isClient; + listenUri = TransportSecurityHelpers.GetListenUri(context.ListenUriBaseAddress, context.ListenUriRelativeAddress); + + SecurityCredentialsManager credentialProvider = context.BindingParameters.Find(); + if (credentialProvider == null) + { + //if (isClient) + //{ + // credentialProvider = ClientCredentials.CreateDefaultCredentials(); + //} + //else + //{ + credentialProvider = new ServiceCredentials(); //ServiceCredentials.CreateDefaultCredentials(); + //} + } + + + securityTokenManager = credentialProvider.CreateSecurityTokenManager(); + } + + public string Scheme { get; } + + internal bool ExtractGroupsForWindowsAccounts { get; private set; } + + public override EndpointIdentity Identity + { + get + { + // If the server credential is null, then we have not been opened yet and have no identity to expose. + if (ServerCredential != null) + { + if (_identity == null) + { + lock (ThisLock) + { + if (_identity == null) + { + _identity = Security.SecurityUtils.CreateWindowsIdentity(ServerCredential); + } + } + } + } + return _identity; + } + } + + internal IdentityVerifier IdentityVerifier { get; private set; } + + public ProtectionLevel ProtectionLevel { get; } + + NetworkCredential ServerCredential { get; set; } + + public override StreamUpgradeAcceptor CreateUpgradeAcceptor() + { + ThrowIfDisposedOrNotOpen(); + return new WindowsStreamSecurityUpgradeAcceptor(this); + } + + protected override void OnAbort() + { + } + + protected override Task OnCloseAsync(CancellationToken token) + { + return Task.CompletedTask; + } + + protected override async Task OnOpenAsync(CancellationToken token) + { + if (!isClient) + { + SecurityTokenRequirement sspiTokenRequirement = TransportSecurityHelpers.CreateSspiTokenRequirement(Scheme, listenUri); + (ServerCredential, ExtractGroupsForWindowsAccounts) = await + TransportSecurityHelpers.GetSspiCredentialAsync(securityTokenManager, sspiTokenRequirement, token); + } + } + + protected override void OnOpened() + { + base.OnOpened(); + + if (IdentityVerifier == null) + { + IdentityVerifier = IdentityVerifier.CreateDefault(); + } + + if (ServerCredential == null) + { + ServerCredential = CredentialCache.DefaultNetworkCredentials; + } + } + + class WindowsStreamSecurityUpgradeAcceptor : StreamSecurityUpgradeAcceptorBase + { + WindowsStreamSecurityUpgradeProvider parent; + SecurityMessageProperty clientSecurity; + + public WindowsStreamSecurityUpgradeAcceptor(WindowsStreamSecurityUpgradeProvider parent) + : base(FramingUpgradeString.Negotiate) + { + this.parent = parent; + clientSecurity = new SecurityMessageProperty(); + } + + protected override async Task<(Stream, SecurityMessageProperty)> OnAcceptUpgradeAsync(Stream stream) + { + // wrap stream + NegotiateStream negotiateStream = new NegotiateStream(stream); + + // authenticate + try + { + await negotiateStream.AuthenticateAsServerAsync(parent.ServerCredential, parent.ProtectionLevel, + TokenImpersonationLevel.Identification); + } + catch (AuthenticationException exception) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityNegotiationException(exception.Message, + exception)); + } + catch (IOException ioException) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityNegotiationException( + SR.Format(SR.NegotiationFailedIO, ioException.Message), ioException)); + } + + SecurityMessageProperty remoteSecurity = CreateClientSecurity(negotiateStream, parent.ExtractGroupsForWindowsAccounts); + return (negotiateStream, remoteSecurity); + } + + SecurityMessageProperty CreateClientSecurity(NegotiateStream negotiateStream, + bool extractGroupsForWindowsAccounts) + { + WindowsIdentity remoteIdentity = (WindowsIdentity)negotiateStream.RemoteIdentity; + Security.SecurityUtils.ValidateAnonymityConstraint(remoteIdentity, false); + WindowsSecurityTokenAuthenticator authenticator = new WindowsSecurityTokenAuthenticator(extractGroupsForWindowsAccounts); + + // When NegotiateStream returns a WindowsIdentity the AuthenticationType is passed in the constructor to WindowsIdentity + // by it's internal NegoState class. If this changes, then the call to remoteIdentity.AuthenticationType could fail if the + // current process token doesn't have sufficient priviledges. It is a first class exception, and caught by the CLR + // null is returned. + SecurityToken token = new WindowsSecurityToken(remoteIdentity, SecurityUniqueId.Create().Value, remoteIdentity.AuthenticationType); + ReadOnlyCollection authorizationPolicies = authenticator.ValidateToken(token); + clientSecurity = new SecurityMessageProperty(); + clientSecurity.TransportToken = new SecurityTokenSpecification(token, authorizationPolicies); + clientSecurity.ServiceSecurityContext = new ServiceSecurityContext(authorizationPolicies); + return clientSecurity; + } + + public override SecurityMessageProperty GetRemoteSecurity() + { + if (clientSecurity.TransportToken != null) + { + return clientSecurity; + } + return base.GetRemoteSecurity(); + } + } + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Collections/Generic/KeyedByTypeCollection.cs b/src/CoreWCF.Primitives/src/CoreWCF/Collections/Generic/KeyedByTypeCollection.cs new file mode 100644 index 000000000..32deec81a --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Collections/Generic/KeyedByTypeCollection.cs @@ -0,0 +1,122 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using CoreWCF; + +namespace CoreWCF.Collections.Generic +{ + + public class KeyedByTypeCollection : KeyedCollection + { + public KeyedByTypeCollection() + : base(null, 4) + { + } + + public KeyedByTypeCollection(IEnumerable items) + : base(null, 4) + { + if (items == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(items)); + + foreach (TItem item in items) + { + Add(item); + } + } + + public T Find() + { + return Find(false); + } + + public T Remove() + { + return Find(true); + } + + T Find(bool remove) + { + for (int i = 0; i < Count; i++) + { + TItem settings = this[i]; + if (settings is T) + { + if (remove) + { + Remove(settings); + } + return (T)(object)settings; + } + } + return default(T); + } + + public Collection FindAll() + { + return FindAll(false); + } + + public Collection RemoveAll() + { + return FindAll(true); + } + + Collection FindAll(bool remove) + { + Collection result = new Collection(); + foreach (TItem settings in this) + { + if (settings is T) + { + result.Add((T)(object)settings); + } + } + + if (remove) + { + foreach (T settings in result) + { + Remove((TItem)(object)settings); + } + } + + return result; + } + + protected override Type GetKeyForItem(TItem item) + { + if (item == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(item)); + } + + return item.GetType(); + } + + protected override void InsertItem(int index, TItem item) + { + if (item == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(item)); + } + + if (Contains(item.GetType())) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(nameof(item), SR.Format(SR.DuplicateBehavior1, item.GetType().FullName)); + } + + base.InsertItem(index, item); + } + + protected override void SetItem(int index, TItem item) + { + if (item == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(item)); + } + + base.SetItem(index, item); + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Collections/Generic/SynchronizedCollection.cs b/src/CoreWCF.Primitives/src/CoreWCF/Collections/Generic/SynchronizedCollection.cs new file mode 100644 index 000000000..535f7f4b1 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Collections/Generic/SynchronizedCollection.cs @@ -0,0 +1,313 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using CoreWCF; + +namespace CoreWCF.Collections.Generic +{ + public class SynchronizedCollection : IList, IList + { + private readonly List _items; + private readonly object _sync; + + public SynchronizedCollection() + { + _items = new List(); + _sync = new object(); + } + + public SynchronizedCollection(object syncRoot) + { + if (syncRoot == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(syncRoot)); + + _items = new List(); + _sync = syncRoot; + } + + public SynchronizedCollection(object syncRoot, IEnumerable list) + { + if (syncRoot == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(syncRoot)); + if (list == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(list)); + + _items = new List(list); + _sync = syncRoot; + } + + public SynchronizedCollection(object syncRoot, params T[] list) + { + if (syncRoot == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(syncRoot)); + if (list == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(list)); + + _items = new List(list.Length); + foreach (T t in list) + _items.Add(t); + + _sync = syncRoot; + } + + public int Count + { + get { lock (_sync) { return _items.Count; } } + } + + protected List Items + { + get { return _items; } + } + + public object SyncRoot + { + get { return _sync; } + } + + public T this[int index] + { + get + { + lock (_sync) + { + return _items[index]; + } + } + set + { + lock (_sync) + { + if (index < 0 || index >= _items.Count) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(index), index, + SR.Format(SR.ValueMustBeInRange, 0, _items.Count - 1))); + + SetItem(index, value); + } + } + } + + public void Add(T item) + { + lock (_sync) + { + int index = _items.Count; + InsertItem(index, item); + } + } + + public void Clear() + { + lock (_sync) + { + ClearItems(); + } + } + + public void CopyTo(T[] array, int index) + { + lock (_sync) + { + _items.CopyTo(array, index); + } + } + + public bool Contains(T item) + { + lock (_sync) + { + return _items.Contains(item); + } + } + + public IEnumerator GetEnumerator() + { + lock (_sync) + { + return _items.GetEnumerator(); + } + } + + public int IndexOf(T item) + { + lock (_sync) + { + return InternalIndexOf(item); + } + } + + public void Insert(int index, T item) + { + lock (_sync) + { + if (index < 0 || index > _items.Count) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(index), index, + SR.Format(SR.ValueMustBeInRange, 0, _items.Count - 1))); + + InsertItem(index, item); + } + } + + int InternalIndexOf(T item) + { + int count = _items.Count; + + for (int i = 0; i < count; i++) + { + if (Equals(_items[i], item)) + { + return i; + } + } + return -1; + } + + public bool Remove(T item) + { + lock (_sync) + { + int index = InternalIndexOf(item); + if (index < 0) + return false; + + RemoveItem(index); + return true; + } + } + + public void RemoveAt(int index) + { + lock (_sync) + { + if (index < 0 || index >= _items.Count) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(index), index, + SR.Format(SR.ValueMustBeInRange, 0, _items.Count - 1))); + + RemoveItem(index); + } + } + + protected virtual void ClearItems() + { + _items.Clear(); + } + + protected virtual void InsertItem(int index, T item) + { + _items.Insert(index, item); + } + + protected virtual void RemoveItem(int index) + { + _items.RemoveAt(index); + } + + protected virtual void SetItem(int index, T item) + { + _items[index] = item; + } + + bool ICollection.IsReadOnly + { + get { return false; } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return ((IList)_items).GetEnumerator(); + } + + bool ICollection.IsSynchronized + { + get { return true; } + } + + object ICollection.SyncRoot + { + get { return _sync; } + } + + void ICollection.CopyTo(Array array, int index) + { + lock (_sync) + { + ((IList)_items).CopyTo(array, index); + } + } + + object IList.this[int index] + { + get + { + return this[index]; + } + set + { + VerifyValueType(value); + this[index] = (T)value; + } + } + + bool IList.IsReadOnly + { + get { return false; } + } + + bool IList.IsFixedSize + { + get { return false; } + } + + int IList.Add(object value) + { + VerifyValueType(value); + + lock (_sync) + { + Add((T)value); + return Count - 1; + } + } + + bool IList.Contains(object value) + { + VerifyValueType(value); + return Contains((T)value); + } + + int IList.IndexOf(object value) + { + VerifyValueType(value); + return IndexOf((T)value); + } + + void IList.Insert(int index, object value) + { + VerifyValueType(value); + Insert(index, (T)value); + } + + void IList.Remove(object value) + { + VerifyValueType(value); + Remove((T)value); + } + + static void VerifyValueType(object value) + { + if (value == null) + { + if (typeof(T).GetTypeInfo().IsValueType) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.SynchronizedCollectionWrongTypeNull); + } + } + else if (!(value is T)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.Format(SR.SynchronizedCollectionWrongType1, value.GetType().FullName)); + } + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Collections/Generic/SynchronizedKeyedCollection.cs b/src/CoreWCF.Primitives/src/CoreWCF/Collections/Generic/SynchronizedKeyedCollection.cs new file mode 100644 index 000000000..22d9dfa24 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Collections/Generic/SynchronizedKeyedCollection.cs @@ -0,0 +1,268 @@ +using System; +using System.Diagnostics.Contracts; +using CoreWCF.Collections.Generic; +using CoreWCF; +using CoreWCF.Runtime; +using System.Collections.Generic; + +namespace CoreWCF.Collections.Generic +{ + public abstract class SynchronizedKeyedCollection : SynchronizedCollection + { + const int DefaultThreshold = 0; + + IEqualityComparer _comparer; + Dictionary _dictionary; + int _keyCount; + int _threshold; + + protected SynchronizedKeyedCollection() + { + _comparer = EqualityComparer.Default; + _threshold = int.MaxValue; + } + + protected SynchronizedKeyedCollection(object syncRoot) + : base(syncRoot) + { + _comparer = EqualityComparer.Default; + _threshold = int.MaxValue; + } + + protected SynchronizedKeyedCollection(object syncRoot, IEqualityComparer comparer) + : base(syncRoot) + { + if (comparer == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(comparer)); + + _comparer = comparer; + _threshold = int.MaxValue; + } + + protected SynchronizedKeyedCollection(object syncRoot, IEqualityComparer comparer, int dictionaryCreationThreshold) + : base(syncRoot) + { + if (comparer == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(comparer)); + + if (dictionaryCreationThreshold < -1) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(dictionaryCreationThreshold), dictionaryCreationThreshold, + SR.Format(SR.ValueMustBeInRange, -1, int.MaxValue))); + else if (dictionaryCreationThreshold == -1) + _threshold = int.MaxValue; + else + _threshold = dictionaryCreationThreshold; + + _comparer = comparer; + } + + public T this[K key] + { + get + { + if (key == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(key)); + + lock (SyncRoot) + { + if (_dictionary != null) + return _dictionary[key]; + + for (int i = 0; i < Items.Count; i++) + { + T item = Items[i]; + if (_comparer.Equals(key, GetKeyForItem(item))) + return item; + } + + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new KeyNotFoundException()); + } + } + } + + protected IDictionary Dictionary + { + get { return _dictionary; } + } + + void AddKey(K key, T item) + { + if (_dictionary != null) + _dictionary.Add(key, item); + else if (_keyCount == _threshold) + { + CreateDictionary(); + _dictionary.Add(key, item); + } + else + { + if (Contains(key)) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.CannotAddTwoItemsWithTheSameKeyToSynchronizedKeyedCollection0); + + _keyCount++; + } + } + + protected void ChangeItemKey(T item, K newKey) + { + // check if the item exists in the collection + if (!ContainsItem(item)) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.ItemDoesNotExistInSynchronizedKeyedCollection0); + + K oldKey = GetKeyForItem(item); + if (!_comparer.Equals(newKey, oldKey)) + { + if (newKey != null) + AddKey(newKey, item); + + if (oldKey != null) + RemoveKey(oldKey); + } + } + + protected override void ClearItems() + { + base.ClearItems(); + + if (_dictionary != null) + _dictionary.Clear(); + + _keyCount = 0; + } + + public bool Contains(K key) + { + if (key == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(key)); + + lock (SyncRoot) + { + if (_dictionary != null) + return _dictionary.ContainsKey(key); + + if (key != null) + { + for (int i = 0; i < Items.Count; i++) + { + T item = Items[i]; + if (_comparer.Equals(key, GetKeyForItem(item))) + return true; + } + } + return false; + } + } + + bool ContainsItem(T item) + { + K key = default(K); + if ((_dictionary == null) || ((key = GetKeyForItem(item)) == null)) + return Items.Contains(item); + + T itemInDict; + + if (_dictionary.TryGetValue(key, out itemInDict)) + return EqualityComparer.Default.Equals(item, itemInDict); + + return false; + } + + void CreateDictionary() + { + _dictionary = new Dictionary(_comparer); + + foreach (T item in Items) + { + K key = GetKeyForItem(item); + if (key != null) + _dictionary.Add(key, item); + } + } + + protected abstract K GetKeyForItem(T item); + + protected override void InsertItem(int index, T item) + { + K key = GetKeyForItem(item); + + if (key != null) + AddKey(key, item); + + base.InsertItem(index, item); + } + + public bool Remove(K key) + { + if (key == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(key)); + + lock (SyncRoot) + { + if (_dictionary != null) + { + if (_dictionary.ContainsKey(key)) + return Remove(_dictionary[key]); + else + return false; + } + else + { + for (int i = 0; i < Items.Count; i++) + { + if (_comparer.Equals(key, GetKeyForItem(Items[i]))) + { + RemoveItem(i); + return true; + } + } + return false; + } + } + } + + protected override void RemoveItem(int index) + { + K key = GetKeyForItem(Items[index]); + + if (key != null) + RemoveKey(key); + + base.RemoveItem(index); + } + + void RemoveKey(K key) + { + if (!(key != null)) + { + Fx.Assert(false, "key shouldn't be null!"); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(key)); + } + if (_dictionary != null) + _dictionary.Remove(key); + else + _keyCount--; + } + + protected override void SetItem(int index, T item) + { + K newKey = GetKeyForItem(item); + K oldKey = GetKeyForItem(Items[index]); + + if (_comparer.Equals(newKey, oldKey)) + { + if ((newKey != null) && (_dictionary != null)) + _dictionary[newKey] = item; + } + else + { + if (newKey != null) + AddKey(newKey, item); + + if (oldKey != null) + RemoveKey(oldKey); + } + base.SetItem(index, item); + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/CommunicationException.cs b/src/CoreWCF.Primitives/src/CoreWCF/CommunicationException.cs new file mode 100644 index 000000000..a0594b587 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/CommunicationException.cs @@ -0,0 +1,14 @@ +using System; +using System.Runtime.Serialization; + +namespace CoreWCF +{ + [Serializable] + public class CommunicationException : Exception //SystemException + { + public CommunicationException() { } + public CommunicationException(string message) : base(message) { } + public CommunicationException(string message, Exception innerException) : base(message, innerException) { } + protected CommunicationException(SerializationInfo info, StreamingContext context) : base(info, context) { } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/CommunicationObjectAbortedException.cs b/src/CoreWCF.Primitives/src/CoreWCF/CommunicationObjectAbortedException.cs new file mode 100644 index 000000000..6a129939d --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/CommunicationObjectAbortedException.cs @@ -0,0 +1,12 @@ +using System; + +namespace CoreWCF +{ + public class CommunicationObjectAbortedException : CommunicationException + { + public CommunicationObjectAbortedException() { } + public CommunicationObjectAbortedException(string message) : base(message) { } + public CommunicationObjectAbortedException(string message, Exception innerException) : base(message, innerException) { } + //protected CommunicationObjectAbortedException(SerializationInfo info, StreamingContext context) : base(info, context) { } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/CommunicationObjectFaultedException.cs b/src/CoreWCF.Primitives/src/CoreWCF/CommunicationObjectFaultedException.cs new file mode 100644 index 000000000..162ccbea9 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/CommunicationObjectFaultedException.cs @@ -0,0 +1,12 @@ +using System; + +namespace CoreWCF +{ + public class CommunicationObjectFaultedException : CommunicationException + { + public CommunicationObjectFaultedException() { } + public CommunicationObjectFaultedException(string message) : base(message) { } + public CommunicationObjectFaultedException(string message, Exception innerException) : base(message, innerException) { } + //protected CommunicationObjectFaultedException(SerializationInfo info, StreamingContext context) : base(info, context) { } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/CommunicationState.cs b/src/CoreWCF.Primitives/src/CoreWCF/CommunicationState.cs new file mode 100644 index 000000000..3cb490604 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/CommunicationState.cs @@ -0,0 +1,12 @@ +namespace CoreWCF +{ + public enum CommunicationState + { + Faulted = 5, + Closed = 4, + Closing = 3, + Opened = 2, + Opening = 1, + Created = 0, + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/ConcurrencyMode.cs b/src/CoreWCF.Primitives/src/CoreWCF/ConcurrencyMode.cs new file mode 100644 index 000000000..1a8660d86 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/ConcurrencyMode.cs @@ -0,0 +1,20 @@ +namespace CoreWCF +{ + public enum ConcurrencyMode + { + Single, // This is first so it is ConcurrencyMode.default + Reentrant, + Multiple + } + + internal static class ConcurrencyModeHelper + { + static public bool IsDefined(ConcurrencyMode x) + { + return + x == ConcurrencyMode.Single || + x == ConcurrencyMode.Reentrant || + x == ConcurrencyMode.Multiple; + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Configuration/DispatcherBuilderImpl.cs b/src/CoreWCF.Primitives/src/CoreWCF/Configuration/DispatcherBuilderImpl.cs new file mode 100644 index 000000000..5aef409f3 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Configuration/DispatcherBuilderImpl.cs @@ -0,0 +1,24 @@ +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoreWCF.Configuration +{ + internal class DispatcherBuilderImpl : IDispatcherBuilder + { + private IServiceProvider _services; + + public DispatcherBuilderImpl(IServiceProvider services) + { + _services = services; + } + + public List BuildDispatchers(Type serviceType) + { + var serviceConfigInterface = typeof(IServiceConfiguration<>); + var serviceConfig = (IServiceConfiguration)_services.GetRequiredService(serviceConfigInterface.MakeGenericType(serviceType)); + return serviceConfig.GetDispatchers(); + } + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Configuration/IDispatcherBuilder.cs b/src/CoreWCF.Primitives/src/CoreWCF/Configuration/IDispatcherBuilder.cs new file mode 100644 index 000000000..3b9206e46 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Configuration/IDispatcherBuilder.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoreWCF.Configuration +{ + public interface IDispatcherBuilder + { + List BuildDispatchers(Type serviceType); + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Configuration/IService.cs b/src/CoreWCF.Primitives/src/CoreWCF/Configuration/IService.cs new file mode 100644 index 000000000..b304facb2 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Configuration/IService.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoreWCF.Configuration +{ + public interface IService + { + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Configuration/IServiceBuilder.cs b/src/CoreWCF.Primitives/src/CoreWCF/Configuration/IServiceBuilder.cs new file mode 100644 index 000000000..f56a16bd7 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Configuration/IServiceBuilder.cs @@ -0,0 +1,22 @@ +using CoreWCF.Channels; +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoreWCF.Configuration +{ + public interface IServiceBuilder + { + ICollection Services { get; } + ICollection BaseAddresses { get; } + void AddService() where TService : class; + void AddServiceEndpoint(Binding binding, string address); + void AddServiceEndpoint(Binding binding, Uri address); + void AddServiceEndpoint(Binding binding, string address, Uri listenUri); + void AddServiceEndpoint(Binding binding, Uri address, Uri listenUri); + void AddServiceEndpoint(Type implementedContract, Binding binding, string address); + void AddServiceEndpoint(Type implementedContract, Binding binding, Uri address); + void AddServiceEndpoint(Type implementedContract, Binding binding, string address, Uri listenUri); + void AddServiceEndpoint(Type implementedContract, Binding binding, Uri address, Uri listenUri); + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Configuration/IServiceConfiguration.cs b/src/CoreWCF.Primitives/src/CoreWCF/Configuration/IServiceConfiguration.cs new file mode 100644 index 000000000..5e40c7d40 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Configuration/IServiceConfiguration.cs @@ -0,0 +1,19 @@ +using CoreWCF.Channels; +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoreWCF.Configuration +{ + internal interface IServiceConfiguration : IServiceConfiguration where TService : class + { + + } + + internal interface IServiceConfiguration + { + List Endpoints { get; } + List GetDispatchers(); + Type ServiceType { get; } + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Configuration/IServiceDispatcher.cs b/src/CoreWCF.Primitives/src/CoreWCF/Configuration/IServiceDispatcher.cs new file mode 100644 index 000000000..41ea9fce1 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Configuration/IServiceDispatcher.cs @@ -0,0 +1,16 @@ +using CoreWCF.Channels; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace CoreWCF.Configuration +{ + public interface IServiceDispatcher + { + Uri BaseAddress { get; } + Binding Binding { get; } + Task DispatchAsync(RequestContext request, IChannel channel, CancellationToken token); + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Configuration/ServiceBuilder.cs b/src/CoreWCF.Primitives/src/CoreWCF/Configuration/ServiceBuilder.cs new file mode 100644 index 000000000..1bc36dda0 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Configuration/ServiceBuilder.cs @@ -0,0 +1,100 @@ +using Microsoft.Extensions.DependencyInjection; +using CoreWCF.Channels; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace CoreWCF.Configuration +{ + internal class ServiceBuilder : IServiceBuilder + { + private IServiceProvider _serviceProvider; + private IDictionary _services = new Dictionary(); + + public ServiceBuilder(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + } + + public ICollection ServiceConfigurations => _services.Values; + + public ICollection BaseAddresses { get; } = new List(); + + ICollection IServiceBuilder.Services => _services.Keys; + + public void AddService() where TService : class + { + var serviceConfig = _serviceProvider.GetRequiredService>(); + _services[serviceConfig.ServiceType] = serviceConfig; + } + + public void AddServiceEndpoint(Binding binding, string address) + { + AddServiceEndpoint(typeof(TContract), binding, address); + } + + public void AddServiceEndpoint(Binding binding, Uri address) + { + AddServiceEndpoint(typeof(TContract), binding, address); + } + + public void AddServiceEndpoint(Binding binding, string address, Uri listenUri) + { + AddServiceEndpoint(typeof(TContract), binding, address, listenUri); + } + + public void AddServiceEndpoint(Binding binding, Uri address, Uri listenUri) + { + AddServiceEndpoint(typeof(TContract), binding, address, listenUri); + } + + public void AddServiceEndpoint(Type implementedContract, Binding binding, string address) + { + AddServiceEndpoint(implementedContract, binding, address, (Uri)null); + } + + public void AddServiceEndpoint(Type implementedContract, Binding binding, Uri address) + { + AddServiceEndpoint(implementedContract, binding, address, (Uri)null); + } + + public void AddServiceEndpoint(Type implementedContract, Binding binding, string address, Uri listenUri) + { + if (address == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException(nameof(address))); + } + + AddServiceEndpoint(implementedContract, binding, new Uri(address, UriKind.RelativeOrAbsolute), listenUri); + } + + public void AddServiceEndpoint(Type implementedContract, Binding binding, Uri address, Uri listenUri) + { + if (implementedContract == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException(nameof(implementedContract))); + } + + if (binding == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException(nameof(binding))); + } + + if (_services.TryGetValue(typeof(TService), out IServiceConfiguration serviceConfig)) + { + serviceConfig.Endpoints.Add(new ServiceEndpointConfiguration() + { + Address = address, + Binding = binding, + Contract = implementedContract, + ListenUri = listenUri + }); + } + else + { + // TODO: Either find an existing SR to use or create a new one. + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(nameof(TService))); + } + } + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Configuration/ServiceConfiguration.cs b/src/CoreWCF.Primitives/src/CoreWCF/Configuration/ServiceConfiguration.cs new file mode 100644 index 000000000..faeb87e04 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Configuration/ServiceConfiguration.cs @@ -0,0 +1,36 @@ +using CoreWCF.Channels; +using CoreWCF.Description; +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.Extensions.DependencyInjection; + +namespace CoreWCF.Configuration +{ + internal class ServiceConfiguration : IServiceConfiguration where TService : class + { + private ServiceBuilder _serviceBuilder; + private IServiceProvider _services; + private List _dispatchers; + + public ServiceConfiguration(ServiceBuilder serviceBuilder, IServiceProvider services) + { + _serviceBuilder = serviceBuilder; + _services = services; + } + + public Type ServiceType => typeof(TService); + + public List Endpoints { get; } = new List(); + + public List GetDispatchers() + { + if (_dispatchers == null) + { + _dispatchers = DispatcherBuilder.BuildDispatcher(this, _services); + } + + return _dispatchers; + } + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Configuration/ServiceEndpointConfiguration.cs b/src/CoreWCF.Primitives/src/CoreWCF/Configuration/ServiceEndpointConfiguration.cs new file mode 100644 index 000000000..a83c6565d --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Configuration/ServiceEndpointConfiguration.cs @@ -0,0 +1,15 @@ +using CoreWCF.Channels; +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoreWCF.Configuration +{ + internal class ServiceEndpointConfiguration + { + public Uri Address { get; set; } + public Binding Binding { get; set; } + public Type Contract { get; set; } + public Uri ListenUri { get; set; } + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Configuration/ServiceModelApplicationBuilderExtensions.cs b/src/CoreWCF.Primitives/src/CoreWCF/Configuration/ServiceModelApplicationBuilderExtensions.cs new file mode 100644 index 000000000..ba4c803c0 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Configuration/ServiceModelApplicationBuilderExtensions.cs @@ -0,0 +1,57 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; +using CoreWCF.Channels; +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoreWCF.Configuration +{ + public static class ServiceModelApplicationBuilderExtensions + { + public static IApplicationBuilder UseServiceModel(this IApplicationBuilder app, Action configureServices) + { + if (app == null) + { + throw new ArgumentNullException(nameof(app)); + } + + if (configureServices == null) + { + throw new ArgumentNullException(nameof(configureServices)); + } + + var serviceBuilder = app.ApplicationServices.GetRequiredService(); + configureServices(serviceBuilder); + + var transportMiddlewareTypes = new HashSet(); + foreach (var serviceConfig in serviceBuilder.ServiceConfigurations) + { + foreach (var serviceEndpoint in serviceConfig.Endpoints) + { + var be = serviceEndpoint.Binding.CreateBindingElements(); + var tbe = be.Find(); + // TODO : Error handling if TBE doesn't exist + var transportMiddlewareType = tbe.MiddlewareType; + if (transportMiddlewareTypes.Contains(transportMiddlewareType)) + continue; + transportMiddlewareTypes.Add(transportMiddlewareType); + string scheme = tbe.Scheme; + if ("http".Equals(scheme, StringComparison.OrdinalIgnoreCase) || + "https".Equals(scheme, StringComparison.OrdinalIgnoreCase)) + { + app.UseMiddleware(transportMiddlewareType, app); + } + else + { + // Not implemented yet. Commented code is for how things will work in the future. + //svc.UseMiddleware(transportMiddlewareType); + } + + } + } + + return app; + } + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Configuration/ServiceModelServiceCollectionExtensions.cs b/src/CoreWCF.Primitives/src/CoreWCF/Configuration/ServiceModelServiceCollectionExtensions.cs new file mode 100644 index 000000000..0633bc220 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Configuration/ServiceModelServiceCollectionExtensions.cs @@ -0,0 +1,31 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using CoreWCF.Channels; +using CoreWCF.Dispatcher; +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoreWCF.Configuration +{ + public static class ServiceModelServiceCollectionExtensions + { + public static IServiceCollection AddServiceModelServices(this IServiceCollection services) + { + if (services == null) + { + throw new ArgumentNullException(nameof(services)); + } + + services.AddSingleton(); + services.AddSingleton(provider => provider.GetRequiredService()); + services.TryAddSingleton(typeof(IServiceConfiguration<>), typeof(ServiceConfiguration<>)); + services.TryAddSingleton(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + + return services; + } + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/DBNull.cs b/src/CoreWCF.Primitives/src/CoreWCF/DBNull.cs new file mode 100644 index 000000000..7d68deee0 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/DBNull.cs @@ -0,0 +1,14 @@ +namespace CoreWCF +{ + // This class is a substitute for System.DBNull. It's only use with WCF is to allow HopperCache to tell the + // difference between a non-existent value and a null value. + public sealed class DBNull + { + //Package private constructor + private DBNull() + { + } + + public static readonly DBNull Value = new DBNull(); + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/DataContractFormatAttribute.cs b/src/CoreWCF.Primitives/src/CoreWCF/DataContractFormatAttribute.cs new file mode 100644 index 000000000..816277abc --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/DataContractFormatAttribute.cs @@ -0,0 +1,19 @@ +using System; + +namespace CoreWCF +{ + internal sealed class DataContractFormatAttribute : Attribute + { + OperationFormatStyle style; + public OperationFormatStyle Style + { + get { return style; } + set + { + XmlSerializerFormatAttribute.ValidateOperationFormatStyle(style); + style = value; + } + } + + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Description/ConfigLoader.cs b/src/CoreWCF.Primitives/src/CoreWCF/Description/ConfigLoader.cs new file mode 100644 index 000000000..f6b98fc1a --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Description/ConfigLoader.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using CoreWCF.Channels; + +namespace CoreWCF.Description +{ + internal class ConfigLoader + { + Dictionary bindingTable; + IContractResolver contractResolver; + + public ConfigLoader(IContractResolver contractResolver) + { + this.contractResolver = contractResolver; + bindingTable = new Dictionary(); + } + + internal ContractDescription LookupContract(string contractName, string serviceName) + { + ContractDescription contract = LookupContractForStandardEndpoint(contractName, serviceName); + if (contract == null) + { + if (contractName == string.Empty) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SfxReflectedContractKeyNotFoundEmpty, serviceName))); + } + else + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SfxReflectedContractKeyNotFound2, contractName, serviceName))); + } + } + + return contract; + } + + internal ContractDescription LookupContractForStandardEndpoint(string contractName, string serviceName) + { + return contractResolver.ResolveContract(contractName); + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Description/ContractDescription.cs b/src/CoreWCF.Primitives/src/CoreWCF/Description/ContractDescription.cs new file mode 100644 index 000000000..a282a78f3 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Description/ContractDescription.cs @@ -0,0 +1,209 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using CoreWCF.Collections.Generic; + +namespace CoreWCF.Description +{ + public class ContractDescription + { + private Type _callbackContractType; + private string _configurationName; + private Type _contractType; + private XmlName _name; + private string _ns; + private OperationDescriptionCollection _operations; + private SessionMode _sessionMode; + private KeyedByTypeCollection _behaviors = new KeyedByTypeCollection(); + //ProtectionLevel protectionLevel; + //bool hasProtectionLevel; + + + public ContractDescription(string name) + : this(name, null) + { + } + + public ContractDescription(string name, string ns) + { + // the property setter validates given value + Name = name; + if (!string.IsNullOrEmpty(ns)) + NamingHelper.CheckUriParameter(ns, "ns"); + + _operations = new OperationDescriptionCollection(); + _ns = ns ?? NamingHelper.DefaultNamespace; // ns can be "" + } + + public string ConfigurationName + { + get { return _configurationName; } + set { _configurationName = value; } + } + + public Type ContractType + { + get { return _contractType; } + set { _contractType = value; } + } + + public Type CallbackContractType + { + get { return _callbackContractType; } + set { _callbackContractType = value; } + } + + public string Name + { + get { return _name.EncodedName; } + set + { + if (value == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value"); + } + + if (value.Length == 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new ArgumentOutOfRangeException(nameof(value), SR.SFxContractDescriptionNameCannotBeEmpty)); + } + _name = new XmlName(value, true /*isEncoded*/); + } + } + + public string Namespace + { + get { return _ns; } + set + { + if (!string.IsNullOrEmpty(value)) + NamingHelper.CheckUriProperty(value, "Namespace"); + _ns = value; + } + } + + public OperationDescriptionCollection Operations + { + get { return _operations; } + } + + internal bool HasProtectionLevel => false; + + public SessionMode SessionMode + { + get { return _sessionMode; } + set + { + if (!SessionModeHelper.IsDefined(value)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(value))); + } + + _sessionMode = value; + } + } + + public KeyedCollection ContractBehaviors + { + get { return Behaviors; } + } + + internal KeyedByTypeCollection Behaviors + { + get { return _behaviors; } + } + + public Collection GetInheritedContracts() + { + Collection result = new Collection(); + for (int i = 0; i < Operations.Count; i++) + { + OperationDescription od = Operations[i]; + if (od.DeclaringContract != this) + { + ContractDescription inheritedContract = od.DeclaringContract; + if (!result.Contains(inheritedContract)) + { + result.Add(inheritedContract); + } + } + } + return result; + } + + public static ContractDescription GetContract(Type contractType) where TService : class + { + if (contractType == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(contractType)); + + var typeLoader = new TypeLoader(); + ContractDescription description = typeLoader.LoadContractDescription(contractType); + return description; + } + + public static ContractDescription GetContract(Type contractType, TService serviceImplementation) where TService : class + { + if (contractType == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(contractType)); + + if (serviceImplementation == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(serviceImplementation)); + + var typeLoader = new TypeLoader(); + ContractDescription description = typeLoader.LoadContractDescription(contractType, serviceImplementation); + return description; + } + + internal void EnsureInvariants() + { + if (string.IsNullOrEmpty(Name)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException( + SR.AChannelServiceEndpointSContractSNameIsNull0)); + } + if (Namespace == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException( + SR.AChannelServiceEndpointSContractSNamespace0)); + } + if (Operations.Count == 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException( + SR.Format(SR.SFxContractHasZeroOperations, Name))); + } + bool thereIsAtLeastOneInitiatingOperation = false; + for (int i = 0; i < Operations.Count; i++) + { + OperationDescription operationDescription = Operations[i]; + operationDescription.EnsureInvariants(); + if (operationDescription.IsInitiating) + thereIsAtLeastOneInitiatingOperation = true; + if ((!operationDescription.IsInitiating || operationDescription.IsTerminating) + && (SessionMode != SessionMode.Required)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException( + SR.Format(SR.ContractIsNotSelfConsistentItHasOneOrMore2, Name))); + } + } + if (!thereIsAtLeastOneInitiatingOperation) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException( + SR.Format(SR.SFxContractHasZeroInitiatingOperations, Name))); + } + } + + internal bool IsDuplex() + { + for (int i = 0; i < _operations.Count; ++i) + { + if (_operations[i].IsServerInitiated()) + { + return true; + } + } + + return false; + } + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Description/CustomAttributeProvider.cs b/src/CoreWCF.Primitives/src/CoreWCF/Description/CustomAttributeProvider.cs new file mode 100644 index 000000000..fedd64c63 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Description/CustomAttributeProvider.cs @@ -0,0 +1,138 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.Contracts; +using System.Linq; +using System.Reflection; + +namespace CoreWCF.Description +{ + internal class CustomAttributeProvider + { + private enum AttributeProviderType + { + Unknown, + Type, + MethodInfo, + MemberInfo, + ParameterInfo, + }; + + private CustomAttributeProvider(object attrProvider) + { + if (attrProvider is Type) + { + Type = (Type)attrProvider; + TypeInfo = Type.GetTypeInfo(); + ProviderType = AttributeProviderType.Type; + } + else if (attrProvider is MethodInfo) + { + MethodInfo = (MethodInfo)attrProvider; + ProviderType = AttributeProviderType.MethodInfo; + } + else if (attrProvider is MemberInfo) + { + MemberInfo = (MemberInfo)attrProvider; + ProviderType = AttributeProviderType.MemberInfo; + } + else if (attrProvider is ParameterInfo) + { + ParameterInfo = (ParameterInfo)attrProvider; + ProviderType = AttributeProviderType.ParameterInfo; + } + else + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(nameof(attrProvider)); + } + } + + private AttributeProviderType ProviderType { get; set; } + internal Type Type { get; private set; } + internal TypeInfo TypeInfo { get; private set; } + internal MemberInfo MemberInfo { get; private set; } + internal MethodInfo MethodInfo { get; private set; } + internal ParameterInfo ParameterInfo { get; private set; } + + public object[] GetCustomAttributes(bool inherit) + { + switch (ProviderType) + { + case AttributeProviderType.Type: + return Type.GetTypeInfo().GetCustomAttributes(inherit).ToArray(); + case AttributeProviderType.MethodInfo: + return MethodInfo.GetCustomAttributes(inherit).ToArray(); + case AttributeProviderType.MemberInfo: + return MemberInfo.GetCustomAttributes(inherit).ToArray(); + case AttributeProviderType.ParameterInfo: + // ParameterInfo.GetCustomAttributes can return null instead of an empty enumerable + return ParameterInfo.GetCustomAttributes(inherit)?.ToArray(); + } + Contract.Assert(false, "This should never execute."); + throw new InvalidOperationException(); + } + + public object[] GetCustomAttributes(Type attributeType, bool inherit) + { + var attributes = GetCustomAttributes(inherit); + if (attributes == null || attributes.Length == 0) + return attributes; + + return attributes.Where(attribute => attributeType.IsAssignableFrom(attribute.GetType())).ToArray(); + + // TODO: Revert once issue dotnet/coreclr#8794 is fixed + //switch (this.ProviderType) + //{ + // case AttributeProviderType.Type: + // return this.Type.GetTypeInfo().GetCustomAttributes(attributeType, inherit).ToArray(); + // case AttributeProviderType.MethodInfo: + // return this.MethodInfo.GetCustomAttributes(attributeType, inherit).ToArray(); + // case AttributeProviderType.MemberInfo: + // return this.MemberInfo.GetCustomAttributes(attributeType, inherit).ToArray(); + // case AttributeProviderType.ParameterInfo: + // //GetCustomAttributes could return null instead of empty collection for a known System.Relection issue, workaround the issue by explicitly checking the null + // IEnumerable customAttributes = null; + // customAttributes = this.ParameterInfo.GetCustomAttributes(attributeType, inherit); + // return customAttributes == null ? null : customAttributes.ToArray(); + //} + //Contract.Assert(false, "This should never execute."); + //throw new InvalidOperationException(); + } + + public bool IsDefined(Type attributeType, bool inherit) + { + switch (ProviderType) + { + case AttributeProviderType.Type: + return Type.GetTypeInfo().IsDefined(attributeType, inherit); + case AttributeProviderType.MethodInfo: + return MethodInfo.IsDefined(attributeType, inherit); + case AttributeProviderType.MemberInfo: + return MemberInfo.IsDefined(attributeType, inherit); + case AttributeProviderType.ParameterInfo: + return ParameterInfo.IsDefined(attributeType, inherit); + } + Contract.Assert(false, "This should never execute."); + throw new InvalidOperationException(); + } + + public static implicit operator CustomAttributeProvider(MemberInfo attrProvider) + { + return new CustomAttributeProvider(attrProvider); + } + + public static implicit operator CustomAttributeProvider(MethodInfo attrProvider) + { + return new CustomAttributeProvider(attrProvider); + } + + public static implicit operator CustomAttributeProvider(ParameterInfo attrProvider) + { + return new CustomAttributeProvider(attrProvider); + } + + public static implicit operator CustomAttributeProvider(Type attrProvider) + { + return new CustomAttributeProvider(attrProvider); + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Description/DataContractSerializerOperationBehavior.cs b/src/CoreWCF.Primitives/src/CoreWCF/Description/DataContractSerializerOperationBehavior.cs new file mode 100644 index 000000000..b8dea8a0a --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Description/DataContractSerializerOperationBehavior.cs @@ -0,0 +1,164 @@ +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; +using System.Xml; +using CoreWCF.Channels; +using CoreWCF.Dispatcher; + +namespace CoreWCF.Description +{ + internal class DataContractSerializerOperationBehavior : IOperationBehavior //, IWsdlExportExtension + { + readonly bool builtInOperationBehavior; + + OperationDescription operation; + DataContractFormatAttribute dataContractFormatAttribute; + internal bool ignoreExtensionDataObject = DataContractSerializerDefaults.IgnoreExtensionDataObject; + bool ignoreExtensionDataObjectSetExplicit; + internal int maxItemsInObjectGraph = DataContractSerializerDefaults.MaxItemsInObjectGraph; + bool maxItemsInObjectGraphSetExplicit; + DataContractResolver dataContractResolver; + + public DataContractFormatAttribute DataContractFormatAttribute + { + get { return dataContractFormatAttribute; } + } + + public DataContractSerializerOperationBehavior(OperationDescription operation) + : this(operation, null) + { + } + + public DataContractSerializerOperationBehavior(OperationDescription operation, DataContractFormatAttribute dataContractFormatAttribute) + { + this.dataContractFormatAttribute = dataContractFormatAttribute ?? new DataContractFormatAttribute(); + this.operation = operation; + } + + internal DataContractSerializerOperationBehavior(OperationDescription operation, + DataContractFormatAttribute dataContractFormatAttribute, bool builtInOperationBehavior) + : this(operation, dataContractFormatAttribute) + { + this.builtInOperationBehavior = builtInOperationBehavior; + } + + internal bool IsBuiltInOperationBehavior + { + get { return builtInOperationBehavior; } + } + + public int MaxItemsInObjectGraph + { + get { return maxItemsInObjectGraph; } + set + { + maxItemsInObjectGraph = value; + maxItemsInObjectGraphSetExplicit = true; + } + } + + internal bool MaxItemsInObjectGraphSetExplicit + { + get { return maxItemsInObjectGraphSetExplicit; } + set { maxItemsInObjectGraphSetExplicit = value; } + } + + public bool IgnoreExtensionDataObject + { + get { return ignoreExtensionDataObject; } + set + { + ignoreExtensionDataObject = value; + ignoreExtensionDataObjectSetExplicit = true; + } + } + + internal bool IgnoreExtensionDataObjectSetExplicit + { + get { return ignoreExtensionDataObjectSetExplicit; } + set { ignoreExtensionDataObjectSetExplicit = value; } + } + + public DataContractResolver DataContractResolver + { + get { return dataContractResolver; } + set { dataContractResolver = value; } + } + + public virtual XmlObjectSerializer CreateSerializer(Type type, string name, string ns, IList knownTypes) + { + return new DataContractSerializer(type, name, ns, knownTypes); + } + + public virtual XmlObjectSerializer CreateSerializer(Type type, XmlDictionaryString name, XmlDictionaryString ns, IList knownTypes) + { + return new DataContractSerializer(type, name, ns, knownTypes); + } + + internal object GetFormatter(OperationDescription operation, out bool formatRequest, out bool formatReply, bool isProxy) + { + MessageDescription request = operation.Messages[0]; + MessageDescription response = null; + if (operation.Messages.Count == 2) + response = operation.Messages[1]; + + formatRequest = (request != null) && !request.IsUntypedMessage; + formatReply = (response != null) && !response.IsUntypedMessage; + + if (formatRequest || formatReply) + { + if (PrimitiveOperationFormatter.IsContractSupported(operation)) + return new PrimitiveOperationFormatter(operation, dataContractFormatAttribute.Style == OperationFormatStyle.Rpc); + else + return new DataContractSerializerOperationFormatter(operation, dataContractFormatAttribute, this); + } + + return null; + } + + + void IOperationBehavior.Validate(OperationDescription description) + { + } + + void IOperationBehavior.AddBindingParameters(OperationDescription description, BindingParameterCollection parameters) + { + } + + void IOperationBehavior.ApplyDispatchBehavior(OperationDescription description, DispatchOperation dispatch) + { + if (description == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("description"); + + if (dispatch == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("dispatch"); + + if (dispatch.Formatter != null) + return; + + bool formatRequest; + bool formatReply; + dispatch.Formatter = (IDispatchMessageFormatter)GetFormatter(description, out formatRequest, out formatReply, false); + dispatch.DeserializeRequest = formatRequest; + dispatch.SerializeReply = formatReply; + } + + void IOperationBehavior.ApplyClientBehavior(OperationDescription description, ClientOperation proxy) + { + if (description == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("description"); + + if (proxy == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("proxy"); + + if (proxy.Formatter != null) + return; + + bool formatRequest; + bool formatReply; + proxy.Formatter = (IClientMessageFormatter)GetFormatter(description, out formatRequest, out formatReply, true); + proxy.SerializeRequest = formatRequest; + proxy.DeserializeReply = formatReply; + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Description/DispatcherBuilder.cs b/src/CoreWCF.Primitives/src/CoreWCF/Description/DispatcherBuilder.cs new file mode 100644 index 000000000..a5172bd28 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Description/DispatcherBuilder.cs @@ -0,0 +1,1079 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Xml; +using Microsoft.AspNetCore.Hosting.Server.Features; +using Microsoft.Extensions.DependencyInjection; +using CoreWCF.Runtime; +using CoreWCF.Channels; +using CoreWCF.Configuration; +using CoreWCF.Dispatcher; +using CoreWCF.Security; + +namespace CoreWCF.Description +{ + internal class DispatcherBuilder + { + private static void ValidateDescription(ServiceDescription description, ServiceHostBase serviceHost) + { + description.EnsureInvariants(); + //(SecurityValidationBehavior.Instance as IServiceBehavior).Validate(description, serviceHost); + (new UniqueContractNameValidationBehavior() as IServiceBehavior).Validate(description, serviceHost); + for (int i = 0; i < description.Behaviors.Count; i++) + { + IServiceBehavior iServiceBehavior = description.Behaviors[i]; + iServiceBehavior.Validate(description, serviceHost); + } + for (int i = 0; i < description.Endpoints.Count; i++) + { + ServiceEndpoint endpoint = description.Endpoints[i]; + ContractDescription contract = endpoint.Contract; + bool alreadyProcessedThisContract = false; + for (int j = 0; j < i; j++) + { + if (description.Endpoints[j].Contract == contract) + { + alreadyProcessedThisContract = true; + break; + } + } + endpoint.ValidateForService(!alreadyProcessedThisContract); + } + } + + static void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection parameters) + { + foreach (IContractBehavior icb in endpoint.Contract.Behaviors) + { + icb.AddBindingParameters(endpoint.Contract, endpoint, parameters); + } + foreach (IEndpointBehavior ieb in endpoint.Behaviors) + { + ieb.AddBindingParameters(endpoint, parameters); + } + foreach (OperationDescription op in endpoint.Contract.Operations) + { + foreach (IOperationBehavior iob in op.Behaviors) + { + iob.AddBindingParameters(op, parameters); + } + } + } + + Type BuildChannelListener(StuffPerListenUriInfo stuff, + ServiceHostBase serviceHost, + Uri listenUri, + ListenUriMode listenUriMode, + out IChannelListener result) + { + Binding originalBinding = stuff.Endpoints[0].Binding; + CustomBinding binding = new CustomBinding(originalBinding); + BindingParameterCollection parameters = stuff.Parameters; + + Uri listenUriBaseAddress; + string listenUriRelativeAddress; + GetBaseAndRelativeAddresses(serviceHost, listenUri, binding.Scheme, out listenUriBaseAddress, out listenUriRelativeAddress); + + // I don't believe InternalDuplexBindingElement is needed, at least not initially. + // It looks like it's used when a channel factory is used to supply the outgoing channel + // for duplex. I think it binds an incoming listener to an outgoing channel to acheive duplex. + //InternalDuplexBindingElement internalDuplex = null; + //InternalDuplexBindingElement.AddDuplexListenerSupport(binding, ref internalDuplex); + + // All types are supported to start + bool reply = true; + bool replySession = true; + bool input = true; + bool inputSession = true; + bool duplex = true; + bool duplexSession = true; + string sessionContractName = null; + string datagramContractName = null; + // each endpoint adds constraints + for (int i = 0; i < stuff.Endpoints.Count; ++i) + { + ContractDescription contract = stuff.Endpoints[i].Contract; + if (contract.SessionMode == SessionMode.Required) + { + sessionContractName = contract.Name; + } + if (contract.SessionMode == SessionMode.NotAllowed) + { + datagramContractName = contract.Name; + } + + System.Collections.IList endpointTypes = GetSupportedChannelTypes(contract); + if (!endpointTypes.Contains(typeof(IReplyChannel))) + { + reply = false; + } + if (!endpointTypes.Contains(typeof(IReplySessionChannel))) + { + replySession = false; + } + if (!endpointTypes.Contains(typeof(IInputChannel))) + { + input = false; + } + if (!endpointTypes.Contains(typeof(IInputSessionChannel))) + { + inputSession = false; + } + if (!endpointTypes.Contains(typeof(IDuplexChannel))) + { + duplex = false; + } + if (!endpointTypes.Contains(typeof(IDuplexSessionChannel))) + { + duplexSession = false; + } + } + + if ((sessionContractName != null) && (datagramContractName != null)) + { + string text = SR.Format(SR.SFxCannotRequireBothSessionAndDatagram3, datagramContractName, sessionContractName, binding.Name); + Exception error = new InvalidOperationException(text); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(error); + } + + List supportedChannelTypes = new List(); + if (input) + { + supportedChannelTypes.Add(typeof(IInputChannel)); + } + if (inputSession) + { + supportedChannelTypes.Add(typeof(IInputSessionChannel)); + } + if (reply) + { + supportedChannelTypes.Add(typeof(IReplyChannel)); + } + if (replySession) + { + supportedChannelTypes.Add(typeof(IReplySessionChannel)); + } + if (duplex) + { + supportedChannelTypes.Add(typeof(IDuplexChannel)); + } + if (duplexSession) + { + supportedChannelTypes.Add(typeof(IDuplexSessionChannel)); + } + // now we know what channel types we can use to support the contracts at this ListenUri + Type returnValue = MaybeCreateListener(true, supportedChannelTypes.ToArray(), binding, parameters, + listenUriBaseAddress, listenUriRelativeAddress, listenUriMode, out result); + if (result == null) + { + // we put a lot of work into creating a good error message, as this is a common case + Dictionary setOfChannelTypesSupportedByBinding = new Dictionary(); + if (binding.CanBuildChannelListener()) + { + setOfChannelTypesSupportedByBinding.Add(typeof(IInputChannel), 0); + } + if (binding.CanBuildChannelListener()) + { + setOfChannelTypesSupportedByBinding.Add(typeof(IReplyChannel), 0); + } + if (binding.CanBuildChannelListener()) + { + setOfChannelTypesSupportedByBinding.Add(typeof(IDuplexChannel), 0); + } + if (binding.CanBuildChannelListener()) + { + setOfChannelTypesSupportedByBinding.Add(typeof(IInputSessionChannel), 0); + } + if (binding.CanBuildChannelListener()) + { + setOfChannelTypesSupportedByBinding.Add(typeof(IReplySessionChannel), 0); + } + if (binding.CanBuildChannelListener()) + { + setOfChannelTypesSupportedByBinding.Add(typeof(IDuplexSessionChannel), 0); + } + + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(ChannelRequirements.CantCreateListenerException( + setOfChannelTypesSupportedByBinding.Keys, supportedChannelTypes, originalBinding.Name)); + } + return returnValue; + } + + static Type MaybeCreateListener(bool actuallyCreate, Type[] supportedChannels, + Binding binding, BindingParameterCollection parameters, + Uri listenUriBaseAddress, string listenUriRelativeAddress, + ListenUriMode listenUriMode, + out IChannelListener result) + { + // if actuallyCreate is true, then this behaves like CreateListener() + // else this behaves like CanCreateListener() + // result is channel type that was (would be) created, null if can't create + // + // Ugly API helps refactor common code in these two similar-but-different methods + + result = null; + + for (int i = 0; i < supportedChannels.Length; i++) + { + Type channelType = supportedChannels[i]; + + if (channelType == typeof(IInputChannel)) + { + if (binding.CanBuildChannelListener(parameters)) + { + if (actuallyCreate) + { + result = binding.BuildChannelListener(listenUriBaseAddress, listenUriRelativeAddress, listenUriMode, parameters); + } + return typeof(IInputChannel); + } + } + if (channelType == typeof(IReplyChannel)) + { + if (binding.CanBuildChannelListener(parameters)) + { + if (actuallyCreate) + { + result = binding.BuildChannelListener(listenUriBaseAddress, listenUriRelativeAddress, listenUriMode, parameters); + } + return typeof(IReplyChannel); + } + } + if (channelType == typeof(IDuplexChannel)) + { + if (binding.CanBuildChannelListener(parameters)) + { + if (actuallyCreate) + { + result = binding.BuildChannelListener(listenUriBaseAddress, listenUriRelativeAddress, listenUriMode, parameters); + } + return typeof(IDuplexChannel); + } + } + if (channelType == typeof(IInputSessionChannel)) + { + if (binding.CanBuildChannelListener(parameters)) + { + if (actuallyCreate) + { + result = binding.BuildChannelListener(listenUriBaseAddress, listenUriRelativeAddress, listenUriMode, parameters); + } + return typeof(IInputSessionChannel); + } + } + if (channelType == typeof(IReplySessionChannel)) + { + if (binding.CanBuildChannelListener(parameters)) + { + if (actuallyCreate) + { + result = binding.BuildChannelListener(listenUriBaseAddress, listenUriRelativeAddress, listenUriMode, parameters); + } + return typeof(IReplySessionChannel); + } + } + if (channelType == typeof(IDuplexSessionChannel)) + { + if (binding.CanBuildChannelListener(parameters)) + { + if (actuallyCreate) + { + result = binding.BuildChannelListener(listenUriBaseAddress, listenUriRelativeAddress, listenUriMode, parameters); + } + return typeof(IDuplexSessionChannel); + } + } + } + + return null; + } + + private static void EnsureThereAreApplicationEndpoints(ServiceDescription description) + { + foreach (ServiceEndpoint endpoint in description.Endpoints) + { + if (!endpoint.InternalIsSystemEndpoint(description)) + { + return; + } + } + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException( + SR.Format(SR.ServiceHasZeroAppEndpoints, description.ConfigurationName))); + } + + internal static Uri EnsureListenUri(ServiceHostBase serviceHost, ServiceEndpoint endpoint) + { + Uri listenUri = endpoint.ListenUri; + if (listenUri == null) + { + // TODO: Make sure the InternalBaseAddresses are populated with the relevant base address for the transport via DI + listenUri = GetVia(endpoint.Binding.Scheme, ServiceHostBase.EmptyUri, serviceHost.InternalBaseAddresses); + } + if (listenUri == null) + { + // TODO: Plumb through expected scheme and update exception message + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxEndpointNoMatchingScheme, endpoint.Binding.Scheme, endpoint.Binding.Name, ""))); + } + return listenUri; + } + + internal static Uri GetVia(string scheme, Uri address, UriSchemeKeyedCollection baseAddresses) + { + Uri via = address; + if (!via.IsAbsoluteUri) + { + if (!baseAddresses.Contains(scheme)) + { + return null; + } + + via = GetUri(baseAddresses[scheme], address.OriginalString); + } + return via; + } + + internal static Uri GetUri(Uri baseUri, string path) + { + if (path.StartsWith("/", StringComparison.Ordinal) || path.StartsWith("\\", StringComparison.Ordinal)) + { + int i = 1; + for (; i < path.Length; ++i) + { + if (path[i] != '/' && path[i] != '\\') + { + break; + } + } + path = path.Substring(i); + } + + // VSWhidbey#541152: new Uri(Uri, string.Empty) is broken + if (path.Length == 0) + return baseUri; + + if (!baseUri.AbsoluteUri.EndsWith("/", StringComparison.Ordinal)) + { + baseUri = new Uri(baseUri.AbsoluteUri + "/"); + } + return new Uri(baseUri, path); + } + + void GetBaseAndRelativeAddresses(ServiceHostBase serviceHost, Uri listenUri, string scheme, out Uri listenUriBaseAddress, out string listenUriRelativeAddress) + { + // set the ListenUri (old EndpointListener EnsureListenUri() logic) + listenUriBaseAddress = listenUri; + listenUriRelativeAddress = string.Empty; + + if (serviceHost.InternalBaseAddresses.Contains(scheme)) + { + Uri baseAddress = serviceHost.InternalBaseAddresses[scheme]; + if (!baseAddress.AbsoluteUri.EndsWith("/", StringComparison.Ordinal)) + { + baseAddress = new Uri(baseAddress.AbsoluteUri + "/"); + } + string baseAddressString = baseAddress.ToString(); + string thisAddressString = listenUri.ToString(); + if (thisAddressString.StartsWith(baseAddressString, StringComparison.OrdinalIgnoreCase)) + { + listenUriBaseAddress = baseAddress; + listenUriRelativeAddress = thisAddressString.Substring(baseAddressString.Length); + } + } + } + + //This method generates the BindingParameterCollection in the same way it is created during DispatcherBuilder.InitializeServiceHost + internal static BindingParameterCollection GetBindingParameters(ServiceHostBase serviceHost, Collection endpoints) + { + BindingParameterCollection parameters = new BindingParameterCollection(); + parameters.Add(new ThreadSafeMessageFilterTable()); + + foreach (IServiceBehavior behavior in serviceHost.Description.Behaviors) + { + behavior.AddBindingParameters(serviceHost.Description, serviceHost, endpoints, parameters); + } + + foreach (ServiceEndpoint endpoint in endpoints) + { + AddBindingParametersForSecurityContractInformation(endpoint, parameters); + AddBindingParameters(endpoint, parameters); + } + + return parameters; + } + + internal static ListenUriInfo GetListenUriInfoForEndpoint(ServiceHostBase host, ServiceEndpoint endpoint) + { + Uri listenUri = EnsureListenUri(host, endpoint); + return new ListenUriInfo(listenUri, endpoint.ListenUriMode); + } + + internal static void InitializeServiceHost(ServiceDescription description, ServiceHostBase serviceHost) + { + if (serviceHost.ImplementedContracts != null && serviceHost.ImplementedContracts.Count > 0) + { + EnsureThereAreApplicationEndpoints(description); + } + ValidateDescription(description, serviceHost); + + Dictionary stuffPerListenUriInfo + = new Dictionary(); + Dictionary> endpointInfosPerEndpointAddress + = new Dictionary>(); + + // Ensure ListenUri and group endpoints per ListenUri + for (int i = 0; i < description.Endpoints.Count; i++) + { + ServiceEndpoint endpoint = description.Endpoints[i]; + + ListenUriInfo listenUriInfo = GetListenUriInfoForEndpoint(serviceHost, endpoint); + if (!stuffPerListenUriInfo.ContainsKey(listenUriInfo)) + { + stuffPerListenUriInfo.Add(listenUriInfo, new StuffPerListenUriInfo()); + } + stuffPerListenUriInfo[listenUriInfo].Endpoints.Add(endpoint); + } + + foreach (KeyValuePair stuff in stuffPerListenUriInfo) + { + Uri listenUri = stuff.Key.ListenUri; + BindingParameterCollection parameters = stuff.Value.Parameters; + Binding binding = stuff.Value.Endpoints[0].Binding; + EndpointIdentity identity = stuff.Value.Endpoints[0].Address.Identity; + // same EndpointAddressTable instance must be shared between channelDispatcher and parameters + //ThreadSafeMessageFilterTable endpointAddressTable = new ThreadSafeMessageFilterTable(); + //parameters.Add(endpointAddressTable); + + // add service-level binding parameters + foreach (IServiceBehavior behavior in description.Behaviors) + { + behavior.AddBindingParameters(description, serviceHost, stuff.Value.Endpoints, parameters); + } + for (int i = 0; i < stuff.Value.Endpoints.Count; i++) + { + ServiceEndpoint endpoint = stuff.Value.Endpoints[i]; + string viaString = listenUri.AbsoluteUri; + + // ensure all endpoints with this ListenUriInfo have same binding + if (endpoint.Binding != binding) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.ABindingInstanceHasAlreadyBeenAssociatedTo1, viaString))); + } + + // ensure all endpoints with this ListenUriInfo have same identity + if (!object.Equals(endpoint.Address.Identity, identity)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException( + SR.Format(SR.SFxWhenMultipleEndpointsShareAListenUriTheyMustHaveSameIdentity, viaString))); + } + + // add binding parameters (endpoint scope and below) + AddBindingParametersForSecurityContractInformation(endpoint, parameters); + AddBindingParameters(endpoint, parameters); + } + + // build IChannelListener and ChannelDispatcher + //IChannelListener listener; + //Type channelType = BuildChannelListener(stuff.Value, + // serviceHost, + // listenUri, + // listenUriMode, + // out listener); + + XmlQualifiedName bindingQname = new XmlQualifiedName(binding.Name, binding.Namespace); + ChannelDispatcher channelDispatcher = new ChannelDispatcher(listenUri, binding, bindingQname.ToString(), binding); + //channelDispatcher.SetEndpointAddressTable(endpointAddressTable); + stuff.Value.ChannelDispatcher = channelDispatcher; + + for (int i = 0; i < stuff.Value.Endpoints.Count; i++) + { + ServiceEndpoint endpoint = stuff.Value.Endpoints[i]; + string viaString = listenUri.AbsoluteUri; + + //EndpointFilterProvider provider = new EndpointFilterProvider(); + EndpointDispatcher dispatcher = BuildEndpointDispatcher(description, endpoint); + + if (!endpointInfosPerEndpointAddress.ContainsKey(endpoint.Address)) + { + endpointInfosPerEndpointAddress.Add(endpoint.Address, new Collection()); + } + endpointInfosPerEndpointAddress[endpoint.Address].Add(new EndpointInfo(endpoint, dispatcher, /*provider*/ null)); + + channelDispatcher.Endpoints.Add(dispatcher); + + } // end foreach "endpoint" + + serviceHost.ChannelDispatchers.Add(channelDispatcher); + } // end foreach "ListenUri/ChannelDispatcher" group + + // run service behaviors + for (int i = 0; i < description.Behaviors.Count; i++) + { + IServiceBehavior serviceBehavior = description.Behaviors[i]; + serviceBehavior.ApplyDispatchBehavior(description, serviceHost); + } + + foreach (KeyValuePair stuff in stuffPerListenUriInfo) + { + for (int i = 0; i < stuff.Value.Endpoints.Count; i++) + { + ServiceEndpoint endpoint = stuff.Value.Endpoints[i]; + // rediscover which dispatcher goes with this endpoint + Collection infos = endpointInfosPerEndpointAddress[endpoint.Address]; + EndpointInfo info = null; + foreach (EndpointInfo ei in infos) + { + if (ei.Endpoint == endpoint) + { + info = ei; + break; + } + } + EndpointDispatcher dispatcher = info.EndpointDispatcher; + // run contract behaviors + for (int k = 0; k < endpoint.Contract.Behaviors.Count; k++) + { + IContractBehavior behavior = endpoint.Contract.Behaviors[k]; + behavior.ApplyDispatchBehavior(endpoint.Contract, endpoint, dispatcher.DispatchRuntime); + } + // run endpoint behaviors + ApplyBindingInformationFromEndpointToDispatcher(endpoint, dispatcher); + for (int j = 0; j < endpoint.Behaviors.Count; j++) + { + IEndpointBehavior eb = endpoint.Behaviors[j]; + eb.ApplyDispatchBehavior(endpoint, dispatcher); + } + // run operation behaviors + BindOperations(endpoint.Contract, null, dispatcher.DispatchRuntime); + } + } + + EnsureRequiredRuntimeProperties(endpointInfosPerEndpointAddress); + + // Warn about obvious demux conflicts + foreach (Collection endpointInfos in endpointInfosPerEndpointAddress.Values) + { + // all elements of endpointInfos share the same Address (and thus EndpointListener.AddressFilter) + if (endpointInfos.Count > 1) + { + for (int i = 0; i < endpointInfos.Count; i++) + { + for (int j = i + 1; j < endpointInfos.Count; j++) + { + // if not same ListenUri, won't conflict + // if not same ChannelType, may not conflict (some transports demux based on this) + // if they share a ChannelDispatcher, this means same ListenUri and same ChannelType + if (endpointInfos[i].EndpointDispatcher.ChannelDispatcher == + endpointInfos[j].EndpointDispatcher.ChannelDispatcher) + { + EndpointFilterProvider iProvider = endpointInfos[i].FilterProvider; + EndpointFilterProvider jProvider = endpointInfos[j].FilterProvider; + // if not default EndpointFilterProvider, we won't try to throw, you're on your own + string commonAction; + if (iProvider != null && jProvider != null + && HaveCommonInitiatingActions(iProvider, jProvider, out commonAction)) + { + // you will definitely get a MultipleFiltersMatchedException at runtime, + // so let's go ahead and throw now + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new InvalidOperationException( + SR.Format(SR.SFxDuplicateInitiatingActionAtSameVia,endpointInfos[i].Endpoint.ListenUri, commonAction))); + } + } + } + } + } + } + } + + private static void EnsureRequiredRuntimeProperties(Dictionary> endpointInfosPerEndpointAddress) + { + foreach (Collection endpointInfos in endpointInfosPerEndpointAddress.Values) + { + for (int i = 0; i < endpointInfos.Count; i++) + { + DispatchRuntime dispatch = endpointInfos[i].EndpointDispatcher.DispatchRuntime; + + if (dispatch.InstanceContextProvider == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxRequiredRuntimePropertyMissing, "InstanceContextProvider"))); + } + } + } + } + + internal static EndpointDispatcher BuildEndpointDispatcher(ServiceDescription serviceDescription, + ServiceEndpoint endpoint) + { + if (serviceDescription == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(serviceDescription)); + } + + var contractDescription = endpoint.Contract; + if (contractDescription == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(endpoint.Contract)); + } + + EndpointFilterProvider provider = new EndpointFilterProvider(); + + EndpointAddress address = endpoint.Address; + EndpointDispatcher dispatcher = new EndpointDispatcher(address, contractDescription.Name, contractDescription.Namespace, endpoint.Id, endpoint.InternalIsSystemEndpoint(serviceDescription)); + + DispatchRuntime dispatch = dispatcher.DispatchRuntime; + if (contractDescription.CallbackContractType != null) + { + dispatch.CallbackClientRuntime.CallbackClientType = contractDescription.CallbackContractType; + dispatch.CallbackClientRuntime.ContractClientType = contractDescription.ContractType; + } + + for (int i = 0; i < contractDescription.Operations.Count; i++) + { + OperationDescription operation = contractDescription.Operations[i]; + + if (!operation.IsServerInitiated()) + { + BuildDispatchOperation(operation, dispatch, provider); + } + else + { + BuildProxyOperation(operation, dispatch.CallbackClientRuntime); + } + } + + BindOperations(contractDescription, null, dispatch); + + //dispatcher.SetSupportedChannels(DispatcherBuilder.GetSupportedChannelTypes(contractDescription)); + int filterPriority = 0; + dispatcher.ContractFilter = provider.CreateFilter(out filterPriority); + dispatcher.FilterPriority = filterPriority; + + return dispatcher; + } + + static void BuildProxyOperation(OperationDescription operation, ClientRuntime parent) + { + ClientOperation child; + if (operation.Messages.Count == 1) + { + child = new ClientOperation(parent, operation.Name, operation.Messages[0].Action); + } + else + { + child = new ClientOperation(parent, operation.Name, operation.Messages[0].Action, + operation.Messages[1].Action); + } + child.TaskMethod = operation.TaskMethod; + child.TaskTResult = operation.TaskTResult; + child.SyncMethod = operation.SyncMethod; + child.BeginMethod = operation.BeginMethod; + child.EndMethod = operation.EndMethod; + child.IsOneWay = operation.IsOneWay; + child.IsTerminating = operation.IsTerminating; + child.IsInitiating = operation.IsInitiating; + child.IsSessionOpenNotificationEnabled = operation.IsSessionOpenNotificationEnabled; + for (int i = 0; i < operation.Faults.Count; i++) + { + FaultDescription fault = operation.Faults[i]; + child.FaultContractInfos.Add(new FaultContractInfo(fault.Action, fault.DetailType, fault.ElementName, fault.Namespace, operation.KnownTypes)); + } + + parent.Operations.Add(child); + } + + static void BuildDispatchOperation(OperationDescription operation, DispatchRuntime parent, EndpointFilterProvider provider) + { + string requestAction = operation.Messages[0].Action; + DispatchOperation child = null; + if (operation.IsOneWay) + { + child = new DispatchOperation(parent, operation.Name, requestAction); + } + else + { + string replyAction = operation.Messages[1].Action; + child = new DispatchOperation(parent, operation.Name, requestAction, replyAction); + } + + child.HasNoDisposableParameters = operation.HasNoDisposableParameters; + + child.IsTerminating = operation.IsTerminating; + child.IsSessionOpenNotificationEnabled = operation.IsSessionOpenNotificationEnabled; + for (int i = 0; i < operation.Faults.Count; i++) + { + FaultDescription fault = operation.Faults[i]; + child.FaultContractInfos.Add(new FaultContractInfo(fault.Action, fault.DetailType, fault.ElementName, fault.Namespace, operation.KnownTypes)); + } + + if (provider != null) + { + if (operation.IsInitiating) + { + provider.InitiatingActions.Add(requestAction); + } + } + + if (requestAction != MessageHeaders.WildcardAction) + { + parent.Operations.Add(child); + } + else + { + if (parent.HasMatchAllOperation) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.SFxMultipleContractStarOperations0)); + } + + parent.UnhandledDispatchOperation = child; + } + + } + + static void BindOperations(ContractDescription contract, ClientRuntime proxy, DispatchRuntime dispatch) + { + if (!(((proxy == null) != (dispatch == null)))) + { + throw Fx.AssertAndThrowFatal("DispatcherBuilder.BindOperations: ((proxy == null) != (dispatch == null))"); + } + + MessageDirection local = (proxy == null) ? MessageDirection.Input : MessageDirection.Output; + + for (int i = 0; i < contract.Operations.Count; i++) + { + OperationDescription operation = contract.Operations[i]; + MessageDescription first = operation.Messages[0]; + + if (first.Direction != local) + { + if (proxy == null) + { + proxy = dispatch.CallbackClientRuntime; + } + + ClientOperation proxyOperation = proxy.Operations[operation.Name]; + Fx.Assert(proxyOperation != null, ""); + + for (int j = 0; j < operation.Behaviors.Count; j++) + { + IOperationBehavior behavior = operation.Behaviors[j]; + behavior.ApplyClientBehavior(operation, proxyOperation); + } + } + else + { + if (dispatch == null) + { + dispatch = proxy.CallbackDispatchRuntime; + } + + DispatchOperation dispatchOperation = null; + if (dispatch.Operations.Contains(operation.Name)) + { + dispatchOperation = dispatch.Operations[operation.Name]; + } + if (dispatchOperation == null && dispatch.UnhandledDispatchOperation != null && dispatch.UnhandledDispatchOperation.Name == operation.Name) + { + dispatchOperation = dispatch.UnhandledDispatchOperation; + } + + if (dispatchOperation != null) + { + for (int j = 0; j < operation.Behaviors.Count; j++) + { + IOperationBehavior behavior = operation.Behaviors[j]; + behavior.ApplyDispatchBehavior(operation, dispatchOperation); + } + } + } + } + } + + internal static Type[] GetSupportedChannelTypes(ContractDescription contractDescription) + { + if (contractDescription == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(contractDescription)); + } + + ChannelRequirements reqs; + ChannelRequirements.ComputeContractRequirements(contractDescription, out reqs); + Type[] supportedChannels = ChannelRequirements.ComputeRequiredChannels(ref reqs); + // supportedChannels is client-side, need to make server-side + for (int i = 0; i < supportedChannels.Length; i++) + { + if (supportedChannels[i] == typeof(IRequestChannel)) + { + supportedChannels[i] = typeof(IReplyChannel); + } + else if (supportedChannels[i] == typeof(IRequestSessionChannel)) + { + supportedChannels[i] = typeof(IReplySessionChannel); + } + else if (supportedChannels[i] == typeof(IOutputChannel)) + { + supportedChannels[i] = typeof(IInputChannel); + } + else if (supportedChannels[i] == typeof(IOutputSessionChannel)) + { + supportedChannels[i] = typeof(IInputSessionChannel); + } + else if (supportedChannels[i] == typeof(IDuplexChannel)) + { + // no-op; duplex is its own dual + } + else if (supportedChannels[i] == typeof(IDuplexSessionChannel)) + { + // no-op; duplex is its own dual + } + else + { + throw Fx.AssertAndThrowFatal("DispatcherBuilder.GetSupportedChannelTypes: Unexpected channel type"); + } + } + + return supportedChannels; + } + + static bool HaveCommonInitiatingActions(EndpointFilterProvider x, EndpointFilterProvider y, out string commonAction) + { + commonAction = null; + foreach (string action in x.InitiatingActions) + { + if (y.InitiatingActions.Contains(action)) + { + commonAction = action; + return true; + } + } + return false; + } + + internal static List BuildDispatcher(ServiceConfiguration serviceConfig, IServiceProvider services) where TService : class + { + var serviceBuilder = services.GetRequiredService(); + var serverUriAddresses = serviceBuilder.BaseAddresses.ToArray(); + ServiceHostObjectModel < TService > serviceHost; + TService singletonInstance = services.GetService(); + if (singletonInstance == null) + { + serviceHost = new ServiceHostObjectModel(serverUriAddresses); + } + else + { + serviceHost = new ServiceHostObjectModel(singletonInstance, serverUriAddresses); + } + + // Any user supplied IServiceBehaviors can be applied now + var serviceBehaviors = services.GetServices(); + foreach (var behavior in serviceBehaviors) + { + serviceHost.Description.Behaviors.Add(behavior); + } + + // TODO: Create internal behavior which configures any extensibilities which exist in serviceProvider, eg IMessageInspector + foreach (var endpointConfig in serviceConfig.Endpoints) + { + if (!serviceHost.ReflectedContracts.TryGetValue(endpointConfig.Contract, out ContractDescription contract)) + { + throw new ArgumentException($"Service type {typeof(TService)} doesn't implement interface {endpointConfig.Contract}"); + } + + var uri = serviceHost.MakeAbsoluteUri(endpointConfig.Address, endpointConfig.Binding); + var serviceEndpoint = new ServiceEndpoint( + contract, + endpointConfig.Binding, + new EndpointAddress(uri)); + + serviceHost.Description.Endpoints.Add(serviceEndpoint); + } + + InitializeServiceHost(serviceHost.Description, serviceHost); + + // TODO: Add error checking to make sure property chain is correctly populated with objects + var dispatchers = new List(serviceHost.ChannelDispatchers.Count); + foreach (var cdb in serviceHost.ChannelDispatchers) + { + var cd = cdb as ChannelDispatcher; + cd.Init(); + dispatchers.Add(new ServiceDispatcher(cd.ListenUri, cd.Binding, cd.EndpointDispatcherTable)); + } + + return dispatchers; + } + + + //public static Func BuildDispatcher() where TService : class + //{ + // var serviceHost = new ServiceHostObjectModel(); + // // Any user supplied IServiceBehaviors can be applied now + // if (!serviceHost.ReflectedContracts.TryGetValue(typeof(TContract), out ContractDescription contract)) + // { + // throw new ArgumentException($"Service type {typeof(TService)} doesn't implement interface {typeof(TContract)}"); + // } + + // // TODO: Plumb through binding and endpoint address + // var binding = new CustomBinding("BindingName", "BindingNS"); + // var endpointAddress = new EndpointAddress("http://localhost/dummy"); + // var serviceEndpoint = new ServiceEndpoint(contract, binding, endpointAddress); + // serviceHost.Description.Endpoints.Add(serviceEndpoint); + // InitializeServiceHost(serviceHost.Description, serviceHost); + // //var channelDispatcher = new ChannelDispatcher("bindingName", (IDefaultCommunicationTimeouts)null); + // //var endpointDispatcher = BuildEndpointDispatcher(serviceHost.Description, serviceEndpoint); + // //channelDispatcher.Endpoints.Add(endpointDispatcher); + // //serviceHost.ChannelDispatchers.Add(channelDispatcher); + // // TODO: Add error checking to make sure property chain is correctly populated with objects + // var dispatchRuntime = ((ChannelDispatcher)serviceHost.ChannelDispatchers[0]).Endpoints[0].DispatchRuntime; + // return (context, token) => + // { + // return DispatchAsync(context, dispatchRuntime, token); + // }; + //} + + public static void ApplyBindingInformationFromEndpointToDispatcher(ServiceEndpoint serviceEndpoint, EndpointDispatcher endpointDispatcher) + { + endpointDispatcher.ChannelDispatcher.ReceiveSynchronously = false; // No sync code for .Net Core + endpointDispatcher.ChannelDispatcher.ManualAddressing = IsManualAddressing(serviceEndpoint.Binding); + endpointDispatcher.ChannelDispatcher.EnableFaults = true; + endpointDispatcher.ChannelDispatcher.MessageVersion = serviceEndpoint.Binding.MessageVersion; + } + + public static void ApplyBindingInformationFromEndpointToClient(ServiceEndpoint serviceEndpoint, ClientRuntime behavior) + { + behavior.ManualAddressing = IsManualAddressing(serviceEndpoint.Binding); + behavior.EnableFaults = true; + if (serviceEndpoint.Contract.IsDuplex()) + { + behavior.CallbackDispatchRuntime.ChannelDispatcher.MessageVersion = serviceEndpoint.Binding.MessageVersion; + } + } + + private static bool IsManualAddressing(Binding binding) + { + TransportBindingElement transport = binding.CreateBindingElements().Find(); + if (transport == null) + { + string text = SR.Format(SR.SFxBindingMustContainTransport2, binding.Name, binding.Namespace); + Exception error = new InvalidOperationException(text); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(error); + } + return transport.ManualAddressing; + } + + public static void AddBindingParametersForSecurityContractInformation(ServiceEndpoint endpoint, BindingParameterCollection parameters) + { + // get Contract info security needs, and put in BindingParameterCollection + ISecurityCapabilities isc = null; + BindingElementCollection elements = endpoint.Binding.CreateBindingElements(); + for (int i = 0; i < elements.Count; ++i) + { + if (!(elements[i] is ITransportTokenAssertionProvider)) + { + ISecurityCapabilities tmp = elements[i].GetIndividualProperty(); + if (tmp != null) + { + isc = tmp; + break; + } + } + } + if (isc != null) + { + // ensure existence of binding parameter + ChannelProtectionRequirements requirements = parameters.Find(); + if (requirements == null) + { + requirements = new ChannelProtectionRequirements(); + parameters.Add(requirements); + } + + MessageEncodingBindingElement encoding = elements.Find(); + // use endpoint.Binding.Version + if (encoding != null && encoding.MessageVersion.Addressing == AddressingVersion.None) + { + // This binding does not support response actions, so... + requirements.Add(ChannelProtectionRequirements.CreateFromContractAndUnionResponseProtectionRequirements(endpoint.Contract, isc)); + } + else + { + requirements.Add(ChannelProtectionRequirements.CreateFromContract(endpoint.Contract, isc)); + } + } + } + + #region InnerClasses + class EndpointInfo + { + ServiceEndpoint endpoint; + EndpointDispatcher endpointDispatcher; + EndpointFilterProvider provider; + + public EndpointInfo(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher, EndpointFilterProvider provider) + { + this.endpoint = endpoint; + this.endpointDispatcher = endpointDispatcher; + this.provider = provider; + } + public ServiceEndpoint Endpoint { get { return endpoint; } } + public EndpointFilterProvider FilterProvider { get { return provider; } } + public EndpointDispatcher EndpointDispatcher { get { return endpointDispatcher; } } + } + + internal class ListenUriInfo + { + Uri listenUri; + ListenUriMode listenUriMode; + + public ListenUriInfo(Uri listenUri, ListenUriMode listenUriMode) + { + this.listenUri = listenUri; + this.listenUriMode = listenUriMode; + } + + public Uri ListenUri + { + get { return listenUri; } + } + + public ListenUriMode ListenUriMode + { + get { return listenUriMode; } + } + + // implement Equals and GetHashCode so that we can use this as a key in a dictionary + public override bool Equals(object obj) + { + return Equals(obj as ListenUriInfo); + } + + public bool Equals(ListenUriInfo other) + { + if (other == null) + { + return false; + } + + if (object.ReferenceEquals(this, other)) + { + return true; + } + + return (listenUriMode == other.listenUriMode) + && EndpointAddress.UriEquals(listenUri, other.listenUri, true /* ignoreCase */, true /* includeHost */); + } + + public override int GetHashCode() + { + return EndpointAddress.UriGetHashCode(listenUri, true /* includeHost */); + } + } + + class StuffPerListenUriInfo + { + public BindingParameterCollection Parameters = new BindingParameterCollection(); + public Collection Endpoints = new Collection(); + public ChannelDispatcher ChannelDispatcher = null; + } + #endregion // InnerClasses + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Description/FaultDescription.cs b/src/CoreWCF.Primitives/src/CoreWCF/Description/FaultDescription.cs new file mode 100644 index 000000000..afc2e90f0 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Description/FaultDescription.cs @@ -0,0 +1,65 @@ +using System; + +namespace CoreWCF.Description +{ + public class FaultDescription + { + string _action; + Type _detailType; + XmlName _elementName; + XmlName _name; + string _ns; + + public FaultDescription(string action) + { + if (action == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(action)); + + _action = action; + } + + public string Action + { + get { return _action; } + internal set { _action = value; } + } + + // Not serializable on purpose, metadata import/export cannot + // produce it, only available when binding to runtime + public Type DetailType + { + get { return _detailType; } + set { _detailType = value; } + } + + public string Name + { + get { return _name.EncodedName; } + set { SetNameAndElement(new XmlName(value, true /*isEncoded*/)); } + } + + public string Namespace + { + get { return _ns; } + set { _ns = value; } + } + + internal XmlName ElementName + { + get { return _elementName; } + set { _elementName = value; } + } + + internal bool HasProtectionLevel => false; + + internal void SetNameAndElement(XmlName name) + { + _elementName = _name = name; + } + + internal void SetNameOnly(XmlName name) + { + _name = name; + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Description/FaultDescriptionCollection.cs b/src/CoreWCF.Primitives/src/CoreWCF/Description/FaultDescriptionCollection.cs new file mode 100644 index 000000000..da40e3476 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Description/FaultDescriptionCollection.cs @@ -0,0 +1,7 @@ +namespace CoreWCF.Description +{ + public class FaultDescriptionCollection : System.Collections.ObjectModel.Collection + { + internal FaultDescriptionCollection() { } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Description/IContractBehavior.cs b/src/CoreWCF.Primitives/src/CoreWCF/Description/IContractBehavior.cs new file mode 100644 index 000000000..71d953c7d --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Description/IContractBehavior.cs @@ -0,0 +1,10 @@ +namespace CoreWCF.Description +{ + public interface IContractBehavior + { + void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, Channels.BindingParameterCollection bindingParameters); + void ApplyClientBehavior(CoreWCF.Description.ContractDescription contractDescription, CoreWCF.Description.ServiceEndpoint endpoint, CoreWCF.Dispatcher.ClientRuntime clientRuntime); + void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, Dispatcher.DispatchRuntime dispatchRuntime); + void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint); + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Description/IContractBehaviorAttribute.cs b/src/CoreWCF.Primitives/src/CoreWCF/Description/IContractBehaviorAttribute.cs new file mode 100644 index 000000000..b80ca240f --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Description/IContractBehaviorAttribute.cs @@ -0,0 +1,12 @@ +using System; + +namespace CoreWCF.Description +{ + // By default, when the TypeLoader sees an IContractBehavior attribute on a service implementation class, + // it will add that behavior to each contract (endpoint) the service implements. But if the attribute + // implements the interface below, then the TypeLoader will only add the behavior to the applicable contracts. + public interface IContractBehaviorAttribute + { + Type TargetContract { get; } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Description/IContractResolver.cs b/src/CoreWCF.Primitives/src/CoreWCF/Description/IContractResolver.cs new file mode 100644 index 000000000..49bfe6891 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Description/IContractResolver.cs @@ -0,0 +1,7 @@ +namespace CoreWCF.Description +{ + internal interface IContractResolver + { + ContractDescription ResolveContract(string contractName); + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Description/IEndpointBehavior.cs b/src/CoreWCF.Primitives/src/CoreWCF/Description/IEndpointBehavior.cs new file mode 100644 index 000000000..b53038bf8 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Description/IEndpointBehavior.cs @@ -0,0 +1,10 @@ +namespace CoreWCF.Description +{ + public interface IEndpointBehavior + { + void AddBindingParameters(ServiceEndpoint endpoint, Channels.BindingParameterCollection bindingParameters); + void ApplyClientBehavior(CoreWCF.Description.ServiceEndpoint endpoint, CoreWCF.Dispatcher.ClientRuntime clientRuntime); + void ApplyDispatchBehavior(ServiceEndpoint endpoint, Dispatcher.EndpointDispatcher endpointDispatcher); + void Validate(ServiceEndpoint endpoint); + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Description/IOperationBehavior.cs b/src/CoreWCF.Primitives/src/CoreWCF/Description/IOperationBehavior.cs new file mode 100644 index 000000000..d65cd10d0 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Description/IOperationBehavior.cs @@ -0,0 +1,13 @@ +using CoreWCF.Channels; +using CoreWCF.Dispatcher; + +namespace CoreWCF.Description +{ + public interface IOperationBehavior + { + void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters); + void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation); + void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation); + void Validate(OperationDescription operationDescription); + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Description/IServiceBehavior.cs b/src/CoreWCF.Primitives/src/CoreWCF/Description/IServiceBehavior.cs new file mode 100644 index 000000000..2d8e8764c --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Description/IServiceBehavior.cs @@ -0,0 +1,9 @@ +namespace CoreWCF.Description +{ + public interface IServiceBehavior + { + void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase); + void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection endpoints, Channels.BindingParameterCollection bindingParameters); + void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase); + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Description/ListenUriMode.cs b/src/CoreWCF.Primitives/src/CoreWCF/Description/ListenUriMode.cs new file mode 100644 index 000000000..894fbd92f --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Description/ListenUriMode.cs @@ -0,0 +1,17 @@ +namespace CoreWCF.Description +{ + public enum ListenUriMode + { + Explicit, + Unique, + } + + internal static class ListenUriModeHelper + { + static public bool IsDefined(ListenUriMode mode) + { + return mode == ListenUriMode.Explicit + || mode == ListenUriMode.Unique; + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Description/MessageBodyDescription.cs b/src/CoreWCF.Primitives/src/CoreWCF/Description/MessageBodyDescription.cs new file mode 100644 index 000000000..27be40dfa --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Description/MessageBodyDescription.cs @@ -0,0 +1,43 @@ +using System.ComponentModel; + +namespace CoreWCF.Description +{ + public class MessageBodyDescription + { + private XmlName _wrapperName; + private string _wrapperNs; + private MessagePartDescriptionCollection _parts; + private MessagePartDescription _returnValue; + + public MessageBodyDescription() + { + _parts = new MessagePartDescriptionCollection(); + } + + public MessagePartDescriptionCollection Parts + { + get { return _parts; } + } + + [DefaultValue(null)] + public MessagePartDescription ReturnValue + { + get { return _returnValue; } + set { _returnValue = value; } + } + + [DefaultValue(null)] + public string WrapperName + { + get { return _wrapperName == null ? null : _wrapperName.EncodedName; } + set { _wrapperName = new XmlName(value, true /*isEncoded*/); } + } + + [DefaultValue(null)] + public string WrapperNamespace + { + get { return _wrapperNs; } + set { _wrapperNs = value; } + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Description/MessageDescription.cs b/src/CoreWCF.Primitives/src/CoreWCF/Description/MessageDescription.cs new file mode 100644 index 000000000..80bd3314e --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Description/MessageDescription.cs @@ -0,0 +1,158 @@ +using System; +using System.Xml; +using CoreWCF.Channels; + +namespace CoreWCF.Description +{ + public class MessageDescription + { + static Type typeOfUntypedMessage; + string action; + MessageDirection direction; + MessageDescriptionItems items; + XmlName messageName; + Type messageType; + //XmlQualifiedName xsdType; + + public MessageDescription(string action, MessageDirection direction) : this(action, direction, null) { } + + internal MessageDescription(string action, MessageDirection direction, MessageDescriptionItems items) + { + if (!MessageDirectionHelper.IsDefined(direction)) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(direction))); + + this.action = action; + this.direction = direction; + this.items = items; + } + + public string Action + { + get { return action; } + internal set { action = value; } + } + + public MessageBodyDescription Body + { + get { return Items.Body; } + } + + public MessageDirection Direction + { + get { return direction; } + } + + public MessageHeaderDescriptionCollection Headers + { + get { return Items.Headers; } + } + + public MessagePropertyDescriptionCollection Properties + { + get { return Items.Properties; } + } + + internal MessageDescriptionItems Items + { + get + { + if (items == null) + items = new MessageDescriptionItems(); + return items; + } + } + + internal bool HasProtectionLevel => false; + + internal static Type TypeOfUntypedMessage + { + get + { + if (typeOfUntypedMessage == null) + { + typeOfUntypedMessage = typeof(Message); + } + return typeOfUntypedMessage; + } + } + + internal XmlName MessageName + { + get { return messageName; } + set { messageName = value; } + } + + public Type MessageType + { + get { return messageType; } + set { messageType = value; } + } + + internal bool IsTypedMessage + { + get + { + return messageType != null; + } + } + + internal bool IsUntypedMessage + { + get + { + return (Body.ReturnValue != null && Body.Parts.Count == 0 && Body.ReturnValue.Type == TypeOfUntypedMessage) || + (Body.ReturnValue == null && Body.Parts.Count == 1 && Body.Parts[0].Type == TypeOfUntypedMessage); + } + } + + internal bool IsVoid + { + get + { + return !IsTypedMessage && Body.Parts.Count == 0 && (Body.ReturnValue == null || Body.ReturnValue.Type == typeof(void)); + } + } + } + + internal class MessageDescriptionItems + { + MessageHeaderDescriptionCollection headers; + MessageBodyDescription body; + MessagePropertyDescriptionCollection properties; + + internal MessageBodyDescription Body + { + get + { + if (body == null) + body = new MessageBodyDescription(); + return body; + } + set + { + body = value; + } + } + + internal MessageHeaderDescriptionCollection Headers + { + get + { + if (headers == null) + headers = new MessageHeaderDescriptionCollection(); + return headers; + } + } + + internal MessagePropertyDescriptionCollection Properties + { + get + { + if (properties == null) + properties = new MessagePropertyDescriptionCollection(); + return properties; + } + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Description/MessageDescriptionCollection.cs b/src/CoreWCF.Primitives/src/CoreWCF/Description/MessageDescriptionCollection.cs new file mode 100644 index 000000000..0b32c6b01 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Description/MessageDescriptionCollection.cs @@ -0,0 +1,9 @@ +namespace CoreWCF.Description +{ + public class MessageDescriptionCollection : System.Collections.ObjectModel.Collection + { + internal MessageDescriptionCollection() { } + public MessageDescription Find(string action) { return default(MessageDescription); } + public System.Collections.ObjectModel.Collection FindAll(string action) { return default(System.Collections.ObjectModel.Collection); } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Description/MessageDirection.cs b/src/CoreWCF.Primitives/src/CoreWCF/Description/MessageDirection.cs new file mode 100644 index 000000000..0c030642f --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Description/MessageDirection.cs @@ -0,0 +1,21 @@ +namespace CoreWCF.Description +{ + public enum MessageDirection + { + Input = 0, + Output = 1, + } + + static class MessageDirectionHelper + { + internal static bool IsDefined(MessageDirection value) + { + return (value == MessageDirection.Input || value == MessageDirection.Output); + } + + internal static MessageDirection Opposite(MessageDirection d) + { + return d == MessageDirection.Input ? MessageDirection.Output : MessageDirection.Input; + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Description/MessageHeaderDescription.cs b/src/CoreWCF.Primitives/src/CoreWCF/Description/MessageHeaderDescription.cs new file mode 100644 index 000000000..6c35a6c21 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Description/MessageHeaderDescription.cs @@ -0,0 +1,71 @@ +using System.ComponentModel; +using System.Xml; + +namespace CoreWCF.Description +{ + public class MessageHeaderDescription : MessagePartDescription + { + bool mustUnderstand; + bool relay; + string actor; + bool typedHeader; + bool isUnknownHeader; + + public MessageHeaderDescription(string name, string ns) + : base(name, ns) + { + + } + + internal MessageHeaderDescription(MessageHeaderDescription other) + : base(other) + { + MustUnderstand = other.MustUnderstand; + Relay = other.Relay; + Actor = other.Actor; + TypedHeader = other.TypedHeader; + IsUnknownHeaderCollection = other.IsUnknownHeaderCollection; + } + + internal override MessagePartDescription Clone() + { + return new MessageHeaderDescription(this); + } + + public string Actor + { + get { return actor; } + set { actor = value; } + } + + public bool MustUnderstand + { + get { return mustUnderstand; } + set { mustUnderstand = value; } + } + + public bool Relay + { + get { return relay; } + set { relay = value; } + } + + public bool TypedHeader + { + get { return typedHeader; } + set { typedHeader = value; } + } + + internal bool IsUnknownHeaderCollection + { + get + { + return isUnknownHeader || Multiple && (Type == typeof(XmlElement)); + } + set + { + isUnknownHeader = value; + } + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Description/MessageHeaderDescriptionCollection.cs b/src/CoreWCF.Primitives/src/CoreWCF/Description/MessageHeaderDescriptionCollection.cs new file mode 100644 index 000000000..6e304b81a --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Description/MessageHeaderDescriptionCollection.cs @@ -0,0 +1,8 @@ +namespace CoreWCF.Description +{ + public class MessageHeaderDescriptionCollection : System.Collections.ObjectModel.KeyedCollection + { + internal MessageHeaderDescriptionCollection() { } + protected override System.Xml.XmlQualifiedName GetKeyForItem(MessageHeaderDescription item) { return default(System.Xml.XmlQualifiedName); } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Description/MessagePartDescription.cs b/src/CoreWCF.Primitives/src/CoreWCF/Description/MessagePartDescription.cs new file mode 100644 index 000000000..11fcc38dd --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Description/MessagePartDescription.cs @@ -0,0 +1,120 @@ +using System; +using System.Reflection; + +namespace CoreWCF.Description +{ + public class MessagePartDescription + { + XmlName name; + string ns; + int index; + Type type; + int serializationPosition; + //ProtectionLevel protectionLevel; + //bool hasProtectionLevel; + MemberInfo memberInfo; + // TODO: Was ICustomAttributeProvider + CustomAttributeProvider additionalAttributesProvider; + + bool multiple; + //string baseType; + string uniquePartName; + + public MessagePartDescription(string name, string ns) + { + if (name == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("name", SR.SFxParameterNameCannotBeNull); + } + + this.name = new XmlName(name, true /*isEncoded*/); + + if (!string.IsNullOrEmpty(ns)) + { + NamingHelper.CheckUriParameter(ns, "ns"); + } + + this.ns = ns; + } + + internal MessagePartDescription(MessagePartDescription other) + { + name = other.name; + ns = other.ns; + index = other.index; + type = other.type; + serializationPosition = other.serializationPosition; + //this.hasProtectionLevel = other.hasProtectionLevel; + //this.protectionLevel = other.protectionLevel; + memberInfo = other.memberInfo; + multiple = other.multiple; + additionalAttributesProvider = other.additionalAttributesProvider; + //this.baseType = other.baseType; + //this.uniquePartName = other.uniquePartName; + } + + internal virtual MessagePartDescription Clone() + { + return new MessagePartDescription(this); + } + + internal XmlName XmlName + { + get { return name; } + } + + public string Name + { + get { return name.EncodedName; } + } + + public string Namespace + { + get { return ns; } + } + + public Type Type + { + get { return type; } + set { type = value; } + } + + public int Index + { + get { return index; } + set { index = value; } + } + + public bool Multiple + { + get { return multiple; } + set { multiple = value; } + } + + public MemberInfo MemberInfo + { + get { return memberInfo; } + set { memberInfo = value; } + } + + internal bool HasProtectionLevel => false; + + internal CustomAttributeProvider AdditionalAttributesProvider + { + get { return additionalAttributesProvider ?? memberInfo; } + set { additionalAttributesProvider = value; } + } + + internal string UniquePartName + { + get { return uniquePartName; } + set { uniquePartName = value; } + } + + internal int SerializationPosition + { + get { return serializationPosition; } + set { serializationPosition = value; } + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Description/MessagePartDescriptionCollection.cs b/src/CoreWCF.Primitives/src/CoreWCF/Description/MessagePartDescriptionCollection.cs new file mode 100644 index 000000000..0eb8bd7ea --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Description/MessagePartDescriptionCollection.cs @@ -0,0 +1,8 @@ +namespace CoreWCF.Description +{ + public class MessagePartDescriptionCollection : System.Collections.ObjectModel.KeyedCollection + { + internal MessagePartDescriptionCollection() { } + protected override System.Xml.XmlQualifiedName GetKeyForItem(MessagePartDescription item) { return default(System.Xml.XmlQualifiedName); } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Description/MessagePropertyAttribute.cs b/src/CoreWCF.Primitives/src/CoreWCF/Description/MessagePropertyAttribute.cs new file mode 100644 index 000000000..2573cc8af --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Description/MessagePropertyAttribute.cs @@ -0,0 +1,36 @@ +using System; + +namespace CoreWCF.Description +{ + // TODO: Make public and add to contract + [AttributeUsage(ServiceModelAttributeTargets.MessageMember, Inherited = false)] + internal sealed class MessagePropertyAttribute : Attribute + { + string _name; + bool _isNameSetExplicit; + + public MessagePropertyAttribute() + { + } + + public string Name + { + get + { + return _name; + } + set + { + _isNameSetExplicit = true; + _name = value; + } + } + internal bool IsNameSetExplicit + { + get + { + return _isNameSetExplicit; + } + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Description/MessagePropertyDescription.cs b/src/CoreWCF.Primitives/src/CoreWCF/Description/MessagePropertyDescription.cs new file mode 100644 index 000000000..60e7c331a --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Description/MessagePropertyDescription.cs @@ -0,0 +1,7 @@ +namespace CoreWCF.Description +{ + public class MessagePropertyDescription : MessagePartDescription + { + public MessagePropertyDescription(string name) : base(default(string), default(string)) { } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Description/MessagePropertyDescriptionCollection.cs b/src/CoreWCF.Primitives/src/CoreWCF/Description/MessagePropertyDescriptionCollection.cs new file mode 100644 index 000000000..ab9a12e0e --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Description/MessagePropertyDescriptionCollection.cs @@ -0,0 +1,8 @@ +namespace CoreWCF.Description +{ + public class MessagePropertyDescriptionCollection : System.Collections.ObjectModel.KeyedCollection + { + internal MessagePropertyDescriptionCollection() { } + protected override string GetKeyForItem(MessagePropertyDescription item) { return default(string); } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Description/OperationDescription.cs b/src/CoreWCF.Primitives/src/CoreWCF/Description/OperationDescription.cs new file mode 100644 index 000000000..3979c57af --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Description/OperationDescription.cs @@ -0,0 +1,213 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Reflection; +using CoreWCF.Collections.Generic; + +namespace CoreWCF.Description +{ + public class OperationDescription + { + internal const string SessionOpenedAction = "http://schemas.microsoft.com/2011/02/session/onopen"; //Channels.WebSocketTransportSettings.ConnectionOpenedAction; + private XmlName _name; + private bool _isInitiating; + private bool _isTerminating; + bool _isSessionOpenNotificationEnabled; + private ContractDescription _declaringContract; + private FaultDescriptionCollection _faults; + private MessageDescriptionCollection _messages; + private KeyedByTypeCollection _behaviors; + private Collection _knownTypes; + private MethodInfo _beginMethod; + private MethodInfo _endMethod; + private MethodInfo _syncMethod; + private MethodInfo _taskMethod; + private bool _validateRpcWrapperName = true; + private bool _hasNoDisposableParameters; + + public OperationDescription(string name, ContractDescription declaringContract) + { + if (name == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(name)); + } + + if (name.Length == 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new ArgumentOutOfRangeException(nameof(name), SR.SFxOperationDescriptionNameCannotBeEmpty)); + } + + _name = new XmlName(name, true /*isEncoded*/); + if (declaringContract == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(declaringContract)); + } + + _declaringContract = declaringContract; + _isInitiating = true; + _isTerminating = false; + _faults = new FaultDescriptionCollection(); + _messages = new MessageDescriptionCollection(); + _behaviors = new KeyedByTypeCollection(); + _knownTypes = new Collection(); + } + + internal bool HasNoDisposableParameters + { + get { return _hasNoDisposableParameters; } + set { _hasNoDisposableParameters = value; } + } + + // Not serializable on purpose, metadata import/export cannot + // produce it, only available when binding to runtime + public MethodInfo SyncMethod + { + get { return _syncMethod; } + set { _syncMethod = value; } + } + + // Not serializable on purpose, metadata import/export cannot + // produce it, only available when binding to runtime + public MethodInfo BeginMethod + { + get { return _beginMethod; } + set { _beginMethod = value; } + } + + // Not serializable on purpose, metadata import/export cannot + // produce it, only available when binding to runtime + public MethodInfo EndMethod + { + get { return _endMethod; } + set { _endMethod = value; } + } + + // Not serializable on purpose, metadata import/export cannot + // produce it, only available when binding to runtime + public MethodInfo TaskMethod + { + get { return _taskMethod; } + set { _taskMethod = value; } + } + + internal MethodInfo OperationMethod + { + get + { + if (SyncMethod == null) + { + return TaskMethod ?? BeginMethod; + } + else + { + return SyncMethod; + } + } + } + + internal bool HasProtectionLevel => false; + + public ContractDescription DeclaringContract + { + get { return _declaringContract; } + set + { + if (value == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(DeclaringContract)); + } + else + { + _declaringContract = value; + } + } + } + + public FaultDescriptionCollection Faults + { + get { return _faults; } + } + + public bool IsOneWay + { + get { return Messages.Count == 1; } + } + + public Collection KnownTypes + { + get { return _knownTypes; } + } + + // Messages[0] is the 'request' (first of MEP), and for non-oneway MEPs, Messages[1] is the 'response' (second of MEP) + public MessageDescriptionCollection Messages + { + get { return _messages; } + } + + internal string CodeName + { + get { return _name.DecodedName; } + } + + public string Name + { + get { return _name.EncodedName; } + } + + internal bool IsValidateRpcWrapperName { get { return _validateRpcWrapperName; } } + + public KeyedCollection OperationBehaviors + { + get { return _behaviors; } + } + + internal KeyedByTypeCollection Behaviors + { + get { return _behaviors; } + } + + internal XmlName XmlName + { + get { return _name; } + } + + internal bool IsInitiating + { + get { return _isInitiating; } + set { _isInitiating = value; } + } + + internal bool IsServerInitiated() + { + EnsureInvariants(); + return Messages[0].Direction == MessageDirection.Output; + } + + internal bool IsTerminating + { + get { return _isTerminating; } + set { _isTerminating = value; } + } + + internal void EnsureInvariants() + { + if (Messages.Count != 1 && Messages.Count != 2) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxOperationMustHaveOneOrTwoMessages, Name))); + } + } + + internal Type TaskTResult + { + get; + set; + } + + internal bool IsSessionOpenNotificationEnabled + { + get { return _isSessionOpenNotificationEnabled; } + set { _isSessionOpenNotificationEnabled = value; } + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Description/OperationDescriptionCollection.cs b/src/CoreWCF.Primitives/src/CoreWCF/Description/OperationDescriptionCollection.cs new file mode 100644 index 000000000..eb8035a06 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Description/OperationDescriptionCollection.cs @@ -0,0 +1,50 @@ +using System.Collections.ObjectModel; + +namespace CoreWCF.Description +{ + public class OperationDescriptionCollection : Collection + { + internal OperationDescriptionCollection() + { + } + + public OperationDescription Find(string name) + { + for (int i = 0; i < Count; i++) + { + if (this[i].Name == name) + return this[i]; + } + return null; + } + + public Collection FindAll(string name) + { + Collection results = new Collection(); + for (int i = 0; i < Count; i++) + { + if (this[i].Name == name) + results.Add(this[i]); + } + return results; + } + + protected override void InsertItem(int index, OperationDescription item) + { + if (item == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("item"); + } + base.InsertItem(index, item); + } + + protected override void SetItem(int index, OperationDescription item) + { + if (item == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("item"); + } + base.SetItem(index, item); + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Description/ServiceCredentials.cs b/src/CoreWCF.Primitives/src/CoreWCF/Description/ServiceCredentials.cs new file mode 100644 index 000000000..b48a4b2f8 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Description/ServiceCredentials.cs @@ -0,0 +1,204 @@ +using CoreWCF.IdentityModel.Selectors; +using CoreWCF.Channels; +using CoreWCF.Dispatcher; +using CoreWCF.Security; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Security.Cryptography.X509Certificates; +using System.Text; + +namespace CoreWCF.Description +{ + public class ServiceCredentials : SecurityCredentialsManager, IServiceBehavior + { + UserNamePasswordServiceCredential userName; + X509CertificateInitiatorServiceCredential clientCertificate; + X509CertificateRecipientServiceCredential serviceCertificate; + WindowsServiceCredential windows; + //IssuedTokenServiceCredential issuedToken; + //SecureConversationServiceCredential secureConversation; + //bool useIdentityConfiguration = false; + + bool isReadOnly = false; + bool saveBootstrapTokenInSession = true; + + ExceptionMapper exceptionMapper; + + public ServiceCredentials() + { + userName = new UserNamePasswordServiceCredential(); + clientCertificate = new X509CertificateInitiatorServiceCredential(); + serviceCertificate = new X509CertificateRecipientServiceCredential(); + windows = new WindowsServiceCredential(); + //this.issuedToken = new IssuedTokenServiceCredential(); + //this.secureConversation = new SecureConversationServiceCredential(); + exceptionMapper = new ExceptionMapper(); + } + + protected ServiceCredentials(ServiceCredentials other) + { + if (other == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("other"); + } + userName = new UserNamePasswordServiceCredential(other.userName); + clientCertificate = new X509CertificateInitiatorServiceCredential(other.clientCertificate); + serviceCertificate = new X509CertificateRecipientServiceCredential(other.serviceCertificate); + windows = new WindowsServiceCredential(other.windows); + //this.issuedToken = new IssuedTokenServiceCredential(other.issuedToken); + //this.secureConversation = new SecureConversationServiceCredential(other.secureConversation); + saveBootstrapTokenInSession = other.saveBootstrapTokenInSession; + exceptionMapper = other.exceptionMapper; + } + + public UserNamePasswordServiceCredential UserNameAuthentication + { + get + { + return userName; + } + } + + public X509CertificateInitiatorServiceCredential ClientCertificate + { + get + { + return clientCertificate; + } + } + + public X509CertificateRecipientServiceCredential ServiceCertificate + { + get + { + return serviceCertificate; + } + } + + public WindowsServiceCredential WindowsAuthentication + { + get + { + return windows; + } + } + + //public IssuedTokenServiceCredential IssuedTokenAuthentication + //{ + // get + // { + // return this.issuedToken; + // } + //} + + //public SecureConversationServiceCredential SecureConversationAuthentication + //{ + // get + // { + // return this.secureConversation; + // } + //} + + /// + /// Gets or sets the ExceptionMapper to be used when throwing exceptions. + /// + public ExceptionMapper ExceptionMapper + { + get + { + return exceptionMapper; + } + set + { + ThrowIfImmutable(); + if (value == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value"); + } + exceptionMapper = value; + } + } + + internal static ServiceCredentials CreateDefaultCredentials() + { + return new ServiceCredentials(); + } + + internal override SecurityTokenManager CreateSecurityTokenManager() + { + return new ServiceCredentialsSecurityTokenManager(Clone()); + } + + protected virtual ServiceCredentials CloneCore() + { + return new ServiceCredentials(this); + } + + public ServiceCredentials Clone() + { + ServiceCredentials result = CloneCore(); + if (result == null || result.GetType() != GetType()) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException(SR.Format(SR.CloneNotImplementedCorrectly, GetType(), (result != null) ? result.ToString() : "null"))); + } + return result; + } + + void IServiceBehavior.Validate(ServiceDescription description, ServiceHostBase serviceHostBase) + { + } + + void IServiceBehavior.AddBindingParameters(ServiceDescription description, ServiceHostBase serviceHostBase, Collection endpoints, BindingParameterCollection parameters) + { + if (parameters == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("parameters"); + } + // throw if bindingParameters already has a SecurityCredentialsManager + SecurityCredentialsManager otherCredentialsManager = parameters.Find(); + if (otherCredentialsManager != null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.MultipleSecurityCredentialsManagersInServiceBindingParameters, otherCredentialsManager))); + } + parameters.Add(this); + } + + void IServiceBehavior.ApplyDispatchBehavior(ServiceDescription description, ServiceHostBase serviceHostBase) + { + for (int i = 0; i < serviceHostBase.ChannelDispatchers.Count; i++) + { + ChannelDispatcher channelDispatcher = serviceHostBase.ChannelDispatchers[i] as ChannelDispatcher; + // TODO: ServiceMetadataBehavior + //if (channelDispatcher != null && !ServiceMetadataBehavior.IsHttpGetMetadataDispatcher(description, channelDispatcher)) + //{ + // foreach (EndpointDispatcher endpointDispatcher in channelDispatcher.Endpoints) + // { + // DispatchRuntime behavior = endpointDispatcher.DispatchRuntime; + // behavior.RequireClaimsPrincipalOnOperationContext = this.useIdentityConfiguration; + // } + //} + } + } + + internal void MakeReadOnly() + { + isReadOnly = true; + ClientCertificate.MakeReadOnly(); + //this.IssuedTokenAuthentication.MakeReadOnly(); + //this.SecureConversationAuthentication.MakeReadOnly(); + ServiceCertificate.MakeReadOnly(); + UserNameAuthentication.MakeReadOnly(); + WindowsAuthentication.MakeReadOnly(); + } + + void ThrowIfImmutable() + { + if (isReadOnly) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.ObjectIsReadOnly)); + } + } + + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Description/ServiceDescription.cs b/src/CoreWCF.Primitives/src/CoreWCF/Description/ServiceDescription.cs new file mode 100644 index 000000000..15272e2f0 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Description/ServiceDescription.cs @@ -0,0 +1,288 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Reflection; +using System.Security; +using CoreWCF.Collections.Generic; + +namespace CoreWCF.Description +{ + public class ServiceDescription + { + KeyedByTypeCollection _behaviors = new KeyedByTypeCollection(); + private string _configurationName; + ServiceEndpointCollection _endpoints = new ServiceEndpointCollection(); + Type _serviceType; + XmlName _serviceName; + string _serviceNamespace = NamingHelper.DefaultNamespace; + IDictionary _implementedContracts; + ReflectedContractCollection _reflectedContracts; + + public ServiceDescription() { } + + internal ServiceDescription(string serviceName) + { + if (string.IsNullOrEmpty(serviceName)) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(serviceName)); + + Name = serviceName; + } + + public ServiceDescription(IEnumerable endpoints) : this() + { + if (endpoints == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("endpoints"); + + foreach (ServiceEndpoint endpoint in endpoints) + _endpoints.Add(endpoint); + } + + public string Name + { + get + { + if (_serviceName != null) + return _serviceName.EncodedName; + else if (ServiceType != null) + return NamingHelper.XmlName(ServiceType.Name); + else + return NamingHelper.DefaultServiceName; + } + set + { + if (string.IsNullOrEmpty(value)) + { + _serviceName = null; + } + else + { + // the XmlName ctor validate the value + _serviceName = new XmlName(value, true /*isEncoded*/); + } + } + } + + public string Namespace + { + get + { + return _serviceNamespace; + } + set + { + _serviceNamespace = value; + } + } + + // This was KeyedByTypeCollection, maybe change to Collection + public KeyedByTypeCollection Behaviors + { + get { return _behaviors; } + } + + public string ConfigurationName + { + get { return _configurationName; } + set + { + if (value == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(value)); + } + + _configurationName = value; + } + } + + public ServiceEndpointCollection Endpoints + { + get { return _endpoints; } + } + + // TODO: I made this internal, evaluate making it public as it is on full framework + internal Type ServiceType + { + get { return _serviceType; } + set { _serviceType = value; } + } + + internal static void AddBehaviors(ServiceDescription serviceDescription) where TService : class + { + + TypeLoader.ApplyServiceInheritance>( + serviceDescription.Behaviors, GetIServiceBehaviorAttributes); + + ServiceBehaviorAttribute serviceBehavior = EnsureBehaviorAttribute(serviceDescription); + + if (serviceBehavior.Name != null) + serviceDescription.Name = new XmlName(serviceBehavior.Name).EncodedName; + if (serviceBehavior.Namespace != null) + serviceDescription.Namespace = serviceBehavior.Namespace; + + if (string.IsNullOrEmpty(serviceBehavior.ConfigurationName)) + { + serviceDescription.ConfigurationName = typeof(TService).FullName; + } + else + { + serviceDescription.ConfigurationName = serviceBehavior.ConfigurationName; + } + } + + public static ServiceDescription GetService() where TService : class + { + // TODO: Make ServiceDescription generic? + var description = new ServiceDescription(); + description.ServiceType = typeof(TService); + + AddBehaviors(description); + SetupSingleton(description, (TService)null, false); + return description; + } + + public static ServiceDescription GetService(TService serviceImplementation) where TService : class + { + if (serviceImplementation == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(serviceImplementation)); + } + + // TODO: What if the concrete type is different that the generic type? + Type serviceType = typeof(TService); //serviceImplementation.GetType(); + ServiceDescription description = new ServiceDescription(); + description.ServiceType = serviceType; + + if (serviceImplementation is IServiceBehavior) + { + description.Behaviors.Add((IServiceBehavior)serviceImplementation); + } + + AddBehaviors(description); + SetupSingleton(description, serviceImplementation, true); + return description; + } + + internal static TService CreateImplementation() where TService : class + { + var constructor = typeof(TService).GetConstructor(TypeLoader.DefaultBindingFlags, null, Type.EmptyTypes, null); + if (constructor == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException( + SR.SFxNoDefaultConstructor)); + } + + var implementation = constructor.Invoke( + TypeLoader.DefaultBindingFlags, null, null, System.Globalization.CultureInfo.InvariantCulture) + as TService; + return implementation; + } + + //internal static object CreateImplementation(Type serviceType) + //{ + // var constructors = serviceType.GetConstructors(TypeLoader.DefaultBindingFlags); + // ConstructorInfo constructor = null; + // foreach (var constr in constructors) + // { + // if (constr.GetParameters().Length == 0) + // { + // constructor = constr; + // break; + // } + // } + + // if (constructor == null) + // { + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException( + // SR.SFxNoDefaultConstructor)); + // } + + // return constructor.Invoke(null, null); + //} + + static ServiceBehaviorAttribute EnsureBehaviorAttribute(ServiceDescription description) + { + ServiceBehaviorAttribute attr = ((KeyedByTypeCollection)description.Behaviors).Find(); + + if (attr == null) + { + attr = new ServiceBehaviorAttribute(); + description.Behaviors.Insert(0, attr); + } + + return attr; + } + + // This method ensures that the description object graph is structurally sound and that none + // of the fundamental SFx framework assumptions have been violated. + internal void EnsureInvariants() + { + for (int i = 0; i < Endpoints.Count; i++) + { + ServiceEndpoint endpoint = Endpoints[i]; + if (endpoint == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.AChannelServiceEndpointIsNull0)); + } + endpoint.EnsureInvariants(); + } + } + + static void GetIServiceBehaviorAttributes(Type currentServiceType, KeyedByTypeCollection behaviors) + { + foreach (IServiceBehavior behaviorAttribute in ServiceReflector.GetCustomAttributes(currentServiceType, typeof(IServiceBehavior))) + { + behaviors.Add(behaviorAttribute); + } + } + + static void SetupSingleton(ServiceDescription serviceDescription, TService implementation, bool isWellKnown) where TService : class + { + ServiceBehaviorAttribute serviceBehavior = EnsureBehaviorAttribute(serviceDescription); + Type type = serviceDescription.ServiceType; + if ((implementation == null) && (serviceBehavior.InstanceContextMode == InstanceContextMode.Single)) + { + implementation = CreateImplementation(); + } + + if (isWellKnown) + { + serviceBehavior.SetWellKnownSingleton(implementation); + } + else if ((implementation != null) && (serviceBehavior.InstanceContextMode == InstanceContextMode.Single)) + { + serviceBehavior.SetWellKnownSingleton(implementation); + } + } + + class ReflectedContractCollection : KeyedCollection + { + public ReflectedContractCollection() + : base(null, 4) + { + } + + protected override Type GetKeyForItem(ContractDescription item) + { + if (item == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(item)); + + return item.ContractType; + } + + public IDictionary ToImplementedContracts() + { + Dictionary implementedContracts = new Dictionary(); + foreach (ContractDescription contract in Items) + { + implementedContracts.Add(GetConfigKey(contract), contract); + } + return implementedContracts; + } + + internal static string GetConfigKey(ContractDescription contract) + { + return contract.ConfigurationName; + } + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Description/ServiceEndpoint.cs b/src/CoreWCF.Primitives/src/CoreWCF/Description/ServiceEndpoint.cs new file mode 100644 index 000000000..c56ac7669 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Description/ServiceEndpoint.cs @@ -0,0 +1,252 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Globalization; +using CoreWCF.Collections.Generic; +using CoreWCF.Channels; + +namespace CoreWCF.Description +{ + public class ServiceEndpoint + { + EndpointAddress _address; + Binding _binding; + ContractDescription _contract; + Uri _listenUri; + ListenUriMode _listenUriMode = ListenUriMode.Explicit; + KeyedByTypeCollection _behaviors; + string _id; + XmlName _name; + bool _isEndpointFullyConfigured = false; + + public ServiceEndpoint(ContractDescription contract) + { + if (contract == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("contract"); + _contract = contract; + } + + public ServiceEndpoint(ContractDescription contract, Binding binding, EndpointAddress address) + { + if (contract == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("contract"); + + _contract = contract; + _binding = binding; + _address = address; + } + + public EndpointAddress Address + { + get { return _address; } + set { _address = value; } + } + + public KeyedCollection EndpointBehaviors + { + get { return Behaviors; } + } + + internal KeyedByTypeCollection Behaviors + { + get + { + if (_behaviors == null) + { + _behaviors = new KeyedByTypeCollection(); + } + + return _behaviors; + } + } + + public Binding Binding + { + get { return _binding; } + set { _binding = value; } + } + + public ContractDescription Contract + { + get { return _contract; } + set + { + if (value == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value"); + } + _contract = value; + } + } + + internal bool IsSystemEndpoint + { + get; + set; + } + + public string Name + { + get + { + if (!XmlName.IsNullOrEmpty(_name)) + { + return _name.EncodedName; + } + else if (_binding != null) + { + return string.Format(CultureInfo.InvariantCulture, "{0}_{1}", new XmlName(Binding.Name).EncodedName, Contract.Name); + } + else + { + return Contract.Name; + } + } + set + { + _name = new XmlName(value, true /*isEncoded*/); + } + } + + internal Uri ListenUri + { + get + { + if (_listenUri == null) + { + if (_address == null) + { + return null; + } + else + { + return _address.Uri; + } + } + else + { + return _listenUri; + } + } + set + { + if (value != null && !value.IsAbsoluteUri) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("value", SR.UriMustBeAbsolute); + } + _listenUri = value; + } + } + + internal ListenUriMode ListenUriMode + { + get { return _listenUriMode; } + set + { + if (!ListenUriModeHelper.IsDefined(value)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(value))); + } + _listenUriMode = value; + } + } + + internal string Id + { + get + { + if (_id == null) + _id = Guid.NewGuid().ToString(); + return _id; + } + } + + internal Uri UnresolvedAddress + { + get; + set; + } + + internal Uri UnresolvedListenUri + { + get; + set; + } + + // This method ensures that the description object graph is structurally sound and that none + // of the fundamental SFx framework assumptions have been violated. + internal void EnsureInvariants() + { + if (Binding == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.AChannelServiceEndpointSBindingIsNull0)); + } + if (Contract == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.AChannelServiceEndpointSContractIsNull0)); + } + Contract.EnsureInvariants(); + //Binding.EnsureInvariants(Contract.Name); + } + + internal void ValidateForClient() + { + Validate(true, false); + } + + internal void ValidateForService(bool runOperationValidators) + { + Validate(runOperationValidators, true); + } + + internal bool IsFullyConfigured + { + get { return _isEndpointFullyConfigured; } + set { _isEndpointFullyConfigured = value; } + } + + // for V1 legacy reasons, a mex endpoint is considered a system endpoint even if IsSystemEndpoint = false + internal bool InternalIsSystemEndpoint(ServiceDescription description) + { + //if (ServiceMetadataBehavior.IsMetadataEndpoint(description, this)) + //{ + // return true; + //} + return IsSystemEndpoint; + } + + // This method runs validators (both builtin and ones in description). + // Precondition: EnsureInvariants() should already have been called. + void Validate(bool runOperationValidators, bool isForService) + { + // contract behaviors + ContractDescription contract = Contract; + for (int j = 0; j < contract.ContractBehaviors.Count; j++) + { + IContractBehavior iContractBehavior = contract.ContractBehaviors[j]; + iContractBehavior.Validate(contract, this); + } + // endpoint behaviors + for (int j = 0; j < EndpointBehaviors.Count; j++) + { + IEndpointBehavior ieb = EndpointBehaviors[j]; + ieb.Validate(this); + } + // operation behaviors + if (runOperationValidators) + { + for (int j = 0; j < contract.Operations.Count; j++) + { + OperationDescription op = contract.Operations[j]; + TaskOperationDescriptionValidator.Validate(op, isForService); + for (int k = 0; k < op.OperationBehaviors.Count; k++) + { + IOperationBehavior iob = op.OperationBehaviors[k]; + iob.Validate(op); + } + } + } + } + + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Description/ServiceEndpointCollection.cs b/src/CoreWCF.Primitives/src/CoreWCF/Description/ServiceEndpointCollection.cs new file mode 100644 index 000000000..8199a5551 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Description/ServiceEndpointCollection.cs @@ -0,0 +1,175 @@ +using System; +using System.Collections.ObjectModel; +using System.Xml; + +namespace CoreWCF.Description +{ + public class ServiceEndpointCollection : Collection + { + internal ServiceEndpointCollection() + { + } + + public ServiceEndpoint Find(Type contractType) + { + if (contractType == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("contractType"); + } + + foreach (ServiceEndpoint endpoint in this) + { + if (endpoint != null && endpoint.Contract.ContractType == contractType) + { + return endpoint; + } + } + + return null; + } + + public ServiceEndpoint Find(XmlQualifiedName contractName) + { + if (contractName == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("contractName"); + } + + foreach (ServiceEndpoint endpoint in this) + { + if (endpoint != null && endpoint.Contract.Name == contractName.Name && endpoint.Contract.Namespace == contractName.Namespace) + { + return endpoint; + } + } + + return null; + } + + public ServiceEndpoint Find(Type contractType, XmlQualifiedName bindingName) + { + if (contractType == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("contractType"); + } + if (bindingName == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("bindingName"); + } + + foreach (ServiceEndpoint endpoint in this) + { + if (endpoint != null && endpoint.Contract.ContractType == contractType && + endpoint.Binding.Name == bindingName.Name && + endpoint.Binding.Namespace == bindingName.Namespace) + { + return endpoint; + } + } + + return null; + } + + public ServiceEndpoint Find(XmlQualifiedName contractName, XmlQualifiedName bindingName) + { + if (contractName == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("contractName"); + } + if (bindingName == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("bindingName"); + } + + foreach (ServiceEndpoint endpoint in this) + { + if (endpoint != null && endpoint.Contract.Name == contractName.Name && + endpoint.Contract.Namespace == contractName.Namespace && + endpoint.Binding.Name == bindingName.Name && + endpoint.Binding.Namespace == bindingName.Namespace) + { + return endpoint; + } + } + + return null; + } + + public ServiceEndpoint Find(Uri address) + { + if (address == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("address"); + } + + foreach (ServiceEndpoint endpoint in this) + { + if (endpoint != null && endpoint.Address.Uri == address) + { + return endpoint; + } + } + + return null; + } + + public Collection FindAll(Type contractType) + { + if (contractType == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("contractType"); + } + + Collection results = new Collection(); + + foreach (ServiceEndpoint endpoint in this) + { + if (endpoint != null && endpoint.Contract.ContractType == contractType) + { + results.Add(endpoint); + } + } + + return results; + } + + public Collection FindAll(XmlQualifiedName contractName) + { + if (contractName == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("contractName"); + } + + Collection results = new Collection(); + + foreach (ServiceEndpoint endpoint in this) + { + if (endpoint != null && endpoint.Contract.Name == contractName.Name && endpoint.Contract.Namespace == contractName.Namespace) + { + results.Add(endpoint); + } + } + + return results; + } + + protected override void InsertItem(int index, ServiceEndpoint item) + { + if (item == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("item"); + } + base.InsertItem(index, item); + } + + protected override void SetItem(int index, ServiceEndpoint item) + { + if (item == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("item"); + } + base.SetItem(index, item); + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Description/ServiceReflector.cs b/src/CoreWCF.Primitives/src/CoreWCF/Description/ServiceReflector.cs new file mode 100644 index 000000000..427f5e0c3 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Description/ServiceReflector.cs @@ -0,0 +1,1003 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.Linq; +using System.Reflection; +using CoreWCF.Runtime; +using CoreWCF; +//using System.Xml; +using System.Threading.Tasks; +using System.Threading; +using System.Xml; + +namespace CoreWCF.Description +{ + internal static class NamingHelper + { + internal const string DefaultNamespace = "http://tempuri.org/"; + internal const string DefaultServiceName = "service"; + internal const string MSNamespace = "http://schemas.microsoft.com/2005/07/ServiceModel"; + + // simplified rules for appending paths to base URIs. note that this differs from new Uri(baseUri, string) + // 1) CombineUriStrings("http://foo/bar/z", "baz") ==> "http://foo/bar/z/baz" + // 2) CombineUriStrings("http://foo/bar/z/", "baz") ==> "http://foo/bar/z/baz" + // 3) CombineUriStrings("http://foo/bar/z", "/baz") ==> "http://foo/bar/z/baz" + // 4) CombineUriStrings("http://foo/bar/z", "http://baz/q") ==> "http://baz/q" + // 5) CombineUriStrings("http://foo/bar/z", "") ==> "" + + internal static string CombineUriStrings(string baseUri, string path) + { + if (Uri.IsWellFormedUriString(path, UriKind.Absolute) || path == string.Empty) + { + return path; + } + else + { + // combine + if (baseUri.EndsWith("/", StringComparison.Ordinal)) + { + return baseUri + (path.StartsWith("/", StringComparison.Ordinal) ? path.Substring(1) : path); + } + else + { + return baseUri + (path.StartsWith("/", StringComparison.Ordinal) ? path : "/" + path); + } + } + } + + internal static string TypeName(Type type) + { + var t = type.GetTypeInfo(); + if (t.IsGenericType || t.ContainsGenericParameters) + { + Type[] args = type.GetGenericArguments(); + int nameEnd = t.Name.IndexOf('`'); + string result = nameEnd > 0 ? t.Name.Substring(0, nameEnd) : t.Name; + result += "Of"; + for (int i = 0; i < args.Length; ++i) + { + result = result + "_" + TypeName(args[i]); + } + return result; + } + else if (t.IsArray) + { + return "ArrayOf" + TypeName(t.GetElementType()); + } + else + { + return t.Name; + } + } + + // name, ns could have any combination of nulls + internal static XmlQualifiedName GetContractName(Type contractType, string name, string ns) + { + XmlName xmlName = new XmlName(name ?? TypeName(contractType)); + // ns can be empty + if (ns == null) + { + ns = DefaultNamespace; + } + return new XmlQualifiedName(xmlName.EncodedName, ns); + } + + // name could be null + // logicalMethodName is MethodInfo.Name with Begin removed for async pattern + // return encoded version to be used in OperationDescription + internal static XmlName GetOperationName(string logicalMethodName, string name) + { + return new XmlName(string.IsNullOrEmpty(name) ? logicalMethodName : name); + } + + //internal static string GetMessageAction(OperationDescription operation, bool isResponse) + //{ + // ContractDescription contract = operation.DeclaringContract; + // XmlQualifiedName contractQname = new XmlQualifiedName(contract.Name, contract.Namespace); + // return GetMessageAction(contractQname, operation.CodeName, null, isResponse); + //} + + // name could be null + // logicalMethodName is MethodInfo.Name with Begin removed for async pattern + internal static string GetMessageAction(XmlQualifiedName contractName, string opname, string action, bool isResponse) + { + if (action != null) + { + return action; + } + + System.Text.StringBuilder actionBuilder = new System.Text.StringBuilder(64); + if (string.IsNullOrEmpty(contractName.Namespace)) + { + actionBuilder.Append("urn:"); + } + else + { + actionBuilder.Append(contractName.Namespace); + if (!contractName.Namespace.EndsWith("/", StringComparison.Ordinal)) + { + actionBuilder.Append('/'); + } + } + actionBuilder.Append(contractName.Name); + actionBuilder.Append('/'); + action = isResponse ? opname + "Response" : opname; + + return CombineUriStrings(actionBuilder.ToString(), action); + } + + //internal delegate bool DoesNameExist(string name, object nameCollection); + //internal static string GetUniqueName(string baseName, DoesNameExist doesNameExist, object nameCollection) + //{ + // for (int i = 0; i < Int32.MaxValue; i++) + // { + // string name = i > 0 ? baseName + i : baseName; + // if (!doesNameExist(name, nameCollection)) + // { + // return name; + // } + // } + // Fx.Assert("Too Many Names"); + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Cannot generate unique name for name {0}", baseName))); + //} + + internal static void CheckUriProperty(string ns, string propName) + { + Uri uri; + if (!Uri.TryCreate(ns, UriKind.RelativeOrAbsolute, out uri)) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.Format(SR.SFXUnvalidNamespaceValue, ns, propName)); + } + + internal static void CheckUriParameter(string ns, string paramName) + { + Uri uri; + if (!Uri.TryCreate(ns, UriKind.RelativeOrAbsolute, out uri)) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(paramName, SR.Format(SR.SFXUnvalidNamespaceParam, ns)); + } + + // Converts names that contain characters that are not permitted in XML names to valid names. + internal static string XmlName(string name) + { + if (string.IsNullOrEmpty(name)) + return name; + if (IsAsciiLocalName(name)) + return name; + if (IsValidNCName(name)) + return name; + return XmlConvert.EncodeLocalName(name); + } + + // Transforms an XML name into an object name. + internal static string CodeName(string name) + { + return XmlConvert.DecodeName(name); + } + + static bool IsAlpha(char ch) + { + return (ch >= 'A' && ch <= 'Z' || ch >= 'a' && ch <= 'z'); + } + + static bool IsDigit(char ch) + { + return (ch >= '0' && ch <= '9'); + } + + static bool IsAsciiLocalName(string localName) + { + Fx.Assert(null != localName, ""); + if (!IsAlpha(localName[0])) + return false; + for (int i = 1; i < localName.Length; i++) + { + char ch = localName[i]; + if (!IsAlpha(ch) && !IsDigit(ch)) + return false; + } + return true; + } + + internal static bool IsValidNCName(string name) + { + try + { + XmlConvert.VerifyNCName(name); + return true; + } + catch (XmlException) + { + return false; + } + } + } + + [DebuggerDisplay("{_encoded ?? _decoded}")] + internal class XmlName + { + string _decoded; + string _encoded; + + internal XmlName(string name) + : this(name, false) + { + } + + internal XmlName(string name, bool isEncoded) + { + if (isEncoded) + { + ValidateEncodedName(name, true /*allowNull*/); + _encoded = name; + } + else + { + _decoded = name; + } + } + + internal string EncodedName + { + get + { + if (_encoded == null) + _encoded = NamingHelper.XmlName(_decoded); + return _encoded; + } + } + + internal string DecodedName + { + get + { + if (_decoded == null) + _decoded = NamingHelper.CodeName(_encoded); + return _decoded; + } + } + + static void ValidateEncodedName(string name, bool allowNull) + { + if (allowNull && name == null) + return; + + try + { + XmlConvert.VerifyNCName(name); + } + catch (XmlException e) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(nameof(name), e.Message); + } + } + + bool IsEmpty { get { return string.IsNullOrEmpty(_encoded) && string.IsNullOrEmpty(_decoded); } } + + internal static bool IsNullOrEmpty(XmlName xmlName) + { + return xmlName == null || xmlName.IsEmpty; + } + + // bool Matches(XmlName xmlName) + // { + // return string.Equals(this.EncodedName, xmlName.EncodedName, StringComparison.Ordinal); + // } + + // public override bool Equals(object obj) + // { + // if (object.ReferenceEquals(obj, this)) + // { + // return true; + // } + + // if (object.ReferenceEquals(obj, null)) + // { + // return false; + // } + + // XmlName xmlName = obj as XmlName; + // if (xmlName == null) + // { + // return false; + // } + + // return Matches(xmlName); + // } + + // public override int GetHashCode() + // { + // if (string.IsNullOrEmpty(EncodedName)) + // return 0; + // return EncodedName.GetHashCode(); + // } + + // public override string ToString() + // { + // if (encoded == null && decoded == null) + // return null; + // if (encoded != null) + // return encoded; + // return decoded; + // } + + // public static bool operator ==(XmlName a, XmlName b) + // { + // if (object.ReferenceEquals(a, null)) + // { + // return object.ReferenceEquals(b, null); + // } + + // return (a.Equals(b)); + // } + + // public static bool operator !=(XmlName a, XmlName b) + // { + // return !(a == b); + // } + } + + static internal class ServiceReflector + { + internal const BindingFlags ServiceModelBindingFlags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance; + internal const string BeginMethodNamePrefix = "Begin"; + internal const string EndMethodNamePrefix = "End"; + internal static readonly Type VoidType = typeof(void); + internal const string AsyncMethodNameSuffix = "Async"; + internal static readonly Type taskType = typeof(Task); + internal static readonly Type taskTResultType = typeof(Task<>); + internal static readonly Type CancellationTokenType = typeof(CancellationToken); + internal static readonly Type IProgressType = typeof(IProgress<>); + static readonly Type asyncCallbackType = typeof(AsyncCallback); + static readonly Type asyncResultType = typeof(IAsyncResult); + static readonly Type objectType = typeof(object); + static readonly Type OperationContractAttributeType = typeof(OperationContractAttribute); + + + + static internal Type GetOperationContractProviderType(MethodInfo method) + { + if (GetSingleAttribute(method) != null) + { + return OperationContractAttributeType; + } + + IOperationContractAttributeProvider provider = GetFirstAttribute(method); + if (provider != null) + { + return provider.GetType(); + } + + return null; + } + + // returns the set of root interfaces for the service class (meaning doesn't include callback ifaces) + static internal List GetInterfaces() where TService : class + { + List types = new List(); + bool implicitContract = false; + if (typeof(TService).IsDefined(typeof(ServiceContractAttribute), false)) + { + implicitContract = true; + types.Add(typeof(TService)); + } + + if (!implicitContract) + { + Type t = GetAncestorImplicitContractClass(); + if (t != null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxContractInheritanceRequiresInterfaces2, typeof(TService), t))); + } + foreach (MethodInfo method in GetMethodsInternal()) + { + Type operationContractProviderType = GetOperationContractProviderType(method); + if (operationContractProviderType == OperationContractAttributeType) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.ServicesWithoutAServiceContractAttributeCan2, operationContractProviderType.Name, method.Name, typeof(TService).FullName))); + } + } + } + foreach (Type t in typeof(TService).GetInterfaces()) + { + if (t.IsDefined(typeof(ServiceContractAttribute), false)) + { + if (implicitContract) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxContractInheritanceRequiresInterfaces, typeof(TService), t))); + } + + types.Add(t); + } + } + + return types; + } + + static Type GetAncestorImplicitContractClass() where TService : class + { + for (var service = typeof(TService).BaseType; service != null; service = service.BaseType) + { + if (GetSingleAttribute(service) != null) + { + return service; + } + } + + return null; + } + + static internal List GetInheritedContractTypes(Type service) + { + List types = new List(); + foreach (Type t in service.GetInterfaces()) + { + if (GetSingleAttribute(t.GetTypeInfo()) != null) + { + types.Add(t); + } + } + for (service = service.GetTypeInfo().BaseType; service != null; service = service.GetTypeInfo().BaseType) + { + if (GetSingleAttribute(service.GetTypeInfo()) != null) + { + types.Add(service); + } + } + return types; + } + + static internal object[] GetCustomAttributes(CustomAttributeProvider attrProvider, Type attrType) + { + return GetCustomAttributes(attrProvider, attrType, false); + } + + static internal object[] GetCustomAttributes(CustomAttributeProvider attrProvider, Type attrType, bool inherit) + { + try + { + return attrProvider.GetCustomAttributes(attrType, inherit) ?? Array.Empty(); + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + + // where the exception is CustomAttributeFormatException and the InnerException is a TargetInvocationException, + // drill into the InnerException as this will provide a better error experience (fewer nested InnerExceptions) + // CustomerAttributeFormatException only exists from .Net Standard 1.7, so using base type FormatException instead. + if (e is FormatException && e.InnerException != null) + { + e = e.InnerException; + if (e is TargetInvocationException && e.InnerException != null) + { + e = e.InnerException; + } + } + + TypeInfo typeInfo = attrProvider.TypeInfo; + MethodInfo method = attrProvider.MethodInfo; + //ParameterInfo param = attrProvider as ParameterInfo; + // there is no good way to know if this is a return type attribute + if (typeInfo != null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException( + SR.Format(SR.SFxErrorReflectingOnType2, attrType.Name, typeInfo.Name), e)); + } + else if (method != null) + { + // Changed ReflectedType to DeclaringType as ReflectedType isn't available until .Net standard 1.7 + // TODO: Consider changing back if project is changed to target .Net standard 1.7 or later + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException( + SR.Format(SR.SFxErrorReflectingOnMethod3, + attrType.Name, method.Name, method.DeclaringType.Name), e)); + + } + //else if (param != null) + //{ + // method = param.Member as MethodInfo; + // if (method != null) + // { + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException( + // SR.Format(SR.SFxErrorReflectingOnParameter4, + // attrType.Name, param.Name, method.Name, method.ReflectedType.Name), e)); + // } + //} + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException( + SR.Format(SR.SFxErrorReflectionOnUnknown1, attrType.Name), e)); + } + } + + static internal T GetFirstAttribute(CustomAttributeProvider attrProvider) + where T : class + { + Type attrType = typeof(T); + object[] attrs = GetCustomAttributes(attrProvider, attrType); + if (attrs.Length == 0) + { + return null; + } + else + { + return attrs[0] as T; + } + } + + static internal T GetSingleAttribute(CustomAttributeProvider attrProvider) where T : class + { + Type attrType = typeof(T); + object[] attrs = GetCustomAttributes(attrProvider, attrType); + if (attrs.Length == 0) + { + return null; + } + else if (attrs.Length > 1) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.tooManyAttributesOfTypeOn2, attrType, attrProvider.ToString()))); + } + else + { + return attrs[0] as T; + } + } + + //static internal T GetSingleAttribute(MethodInfo attrProvider) + // where T : class + //{ + // Type attrType = typeof(T); + // object[] attrs = GetCustomAttributes(attrProvider, attrType); + // if (attrs.Length == 0) + // { + // return null; + // } + // else if (attrs.Length > 1) + // { + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.tooManyAttributesOfTypeOn2, attrType, attrProvider.ToString()))); + // } + // else + // { + // return attrs[0] as T; + // } + //} + //static internal T GetSingleAttribute(ICustomAttributeProvider attrProvider) + // where T : class + //{ + // Type attrType = typeof(T); + // object[] attrs = GetCustomAttributes(attrProvider, attrType); + // if (attrs.Length == 0) + // { + // return null; + // } + // else if (attrs.Length > 1) + // { + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.tooManyAttributesOfTypeOn2, attrType, attrProvider.ToString()))); + // } + // else + // { + // return attrs[0] as T; + // } + //} + + static internal T GetRequiredSingleAttribute(CustomAttributeProvider attrProvider) + where T : class + { + T result = GetSingleAttribute(attrProvider); + if (result == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.couldnTFindRequiredAttributeOfTypeOn2, typeof(T), attrProvider.ToString()))); + } + return result; + } + + static internal T GetSingleAttribute(CustomAttributeProvider attrProvider, Type[] attrTypeGroup) + where T : class + { + T result = GetSingleAttribute(attrProvider); + if (result != null) + { + Type attrType = typeof(T); + foreach (Type otherType in attrTypeGroup) + { + if (otherType == attrType) + { + continue; + } + object[] attrs = GetCustomAttributes(attrProvider, otherType); + if (attrs != null && attrs.Length > 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxDisallowedAttributeCombination, attrProvider, attrType.FullName, otherType.FullName))); + } + } + } + return result; + } + + static internal T GetRequiredSingleAttribute(CustomAttributeProvider attrProvider, Type[] attrTypeGroup) + where T : class + { + T result = GetSingleAttribute(attrProvider, attrTypeGroup); + if (result == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.couldnTFindRequiredAttributeOfTypeOn2, typeof(T), attrProvider.ToString()))); + } + return result; + } + + // static internal Type GetContractType(Type interfaceType) + // { + // ServiceContractAttribute contractAttribute; + // return GetContractTypeAndAttribute(interfaceType, out contractAttribute); + // } + + static internal Type GetContractTypeAndAttribute(Type interfaceType, out ServiceContractAttribute contractAttribute) + { + contractAttribute = GetSingleAttribute(interfaceType.GetTypeInfo()); + if (contractAttribute != null) + { + return interfaceType; + } + + List types = new List(GetInheritedContractTypes(interfaceType)); + if (types.Count == 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.AttemptedToGetContractTypeForButThatTypeIs1, interfaceType.Name))); + } + + + foreach (Type potentialContractRoot in types) + { + bool mayBeTheRoot = true; + foreach (Type t in types) + { + if (!t.IsAssignableFrom(potentialContractRoot)) + { + mayBeTheRoot = false; + } + } + if (mayBeTheRoot) + { + contractAttribute = GetSingleAttribute(potentialContractRoot.GetTypeInfo()); + return potentialContractRoot; + } + } + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException( + SR.Format(SR.SFxNoMostDerivedContract, interfaceType.Name))); + } + + static List GetMethodsInternal() where TService : class + { + List methods = new List(); + foreach (MethodInfo mi in typeof(TService).GetMethods(ServiceModelBindingFlags)) + { + if (GetSingleAttribute(mi) != null) + { + methods.Add(mi); + } + else if (GetFirstAttribute(mi) != null) + { + methods.Add(mi); + } + } + return methods; + } + + // The metadata for "in" versus "out" seems to be inconsistent, depending upon what compiler generates it. + // The following code assumes this is the truth table that all compilers will obey: + // + // True Parameter Type .IsIn .IsOut .ParameterType.IsByRef + // + // in F F F ...OR... + // in T F F + // + // in/out T T T ...OR... + // in/out F F T + // + // out F T T + static internal void ValidateParameterMetadata(MethodInfo methodInfo) + { + ParameterInfo[] parameters = methodInfo.GetParameters(); + foreach (ParameterInfo parameter in parameters) + { + if (!parameter.ParameterType.IsByRef) + { + if (parameter.IsOut) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new InvalidOperationException(SR.Format(SR.SFxBadByValueParameterMetadata, + methodInfo.Name, methodInfo.DeclaringType.Name))); + } + } + else + { + if (parameter.IsIn && !parameter.IsOut) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new InvalidOperationException(SR.Format(SR.SFxBadByReferenceParameterMetadata, + methodInfo.Name, methodInfo.DeclaringType.Name))); + } + } + } + } + + static internal bool FlowsIn(ParameterInfo paramInfo) // conceptually both "in" and "in/out" params return true + { + return !paramInfo.IsOut || paramInfo.IsIn; + } + static internal bool FlowsOut(ParameterInfo paramInfo) // conceptually both "out" and "in/out" params return true + { + return paramInfo.ParameterType.IsByRef; + } + + // for async method is the begin method + static internal ParameterInfo[] GetInputParameters(MethodInfo method, bool asyncPattern) + { + int count = 0; + ParameterInfo[] parameters = method.GetParameters(); + + // length of parameters we care about (-2 for async) + int len = parameters.Length; + if (asyncPattern) + { + len -= 2; + } + + // count the ins + for (int i = 0; i < len; i++) + { + if (FlowsIn(parameters[i])) + { + count++; + } + } + + // grab the ins + ParameterInfo[] result = new ParameterInfo[count]; + int pos = 0; + for (int i = 0; i < len; i++) + { + ParameterInfo param = parameters[i]; + if (FlowsIn(param)) + { + result[pos++] = param; + } + } + return result; + } + + // for async method is the end method + static internal ParameterInfo[] GetOutputParameters(MethodInfo method, bool asyncPattern) + { + int count = 0; + ParameterInfo[] parameters = method.GetParameters(); + + // length of parameters we care about (-1 for async) + int len = parameters.Length; + if (asyncPattern) + { + len -= 1; + } + + // count the outs + for (int i = 0; i < len; i++) + { + if (FlowsOut(parameters[i])) + { + count++; + } + } + + // grab the outs + ParameterInfo[] result = new ParameterInfo[count]; + int pos = 0; + for (int i = 0; i < len; i++) + { + ParameterInfo param = parameters[i]; + if (FlowsOut(param)) + { + result[pos++] = param; + } + } + return result; + } + + static internal bool HasOutputParameters(MethodInfo method, bool asyncPattern) + { + ParameterInfo[] parameters = method.GetParameters(); + + // length of parameters we care about (-1 for async) + int len = parameters.Length; + if (asyncPattern) + { + len -= 1; + } + + // count the outs + for (int i = 0; i < len; i++) + { + if (FlowsOut(parameters[i])) + { + return true; + } + } + + return false; + } + + static MethodInfo GetEndMethodInternal(MethodInfo beginMethod) + { + string logicalName = GetLogicalName(beginMethod); + string endMethodName = EndMethodNamePrefix + logicalName; + MemberInfo[] endMethods = beginMethod.DeclaringType.GetMember(endMethodName, ServiceModelBindingFlags); + if (endMethods.Length == 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.NoEndMethodFoundForAsyncBeginMethod3, beginMethod.Name, beginMethod.DeclaringType.FullName, endMethodName))); + } + if (endMethods.Length > 1) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.MoreThanOneEndMethodFoundForAsyncBeginMethod3, beginMethod.Name, beginMethod.DeclaringType.FullName, endMethodName))); + } + return (MethodInfo)endMethods[0]; + } + + static internal MethodInfo GetEndMethod(MethodInfo beginMethod) + { + MethodInfo endMethod = GetEndMethodInternal(beginMethod); + + if (!HasEndMethodShape(endMethod)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.InvalidAsyncEndMethodSignatureForMethod2, endMethod.Name, endMethod.DeclaringType.FullName))); + } + + return endMethod; + } + + // static internal XmlName GetOperationName(MethodInfo method) + // { + // OperationContractAttribute operationAttribute = GetOperationContractAttribute(method); + // return NamingHelper.GetOperationName(GetLogicalName(method), operationAttribute.Name); + // } + + + static internal bool HasBeginMethodShape(MethodInfo method) + { + ParameterInfo[] parameters = method.GetParameters(); + if (!method.Name.StartsWith(BeginMethodNamePrefix, StringComparison.Ordinal) || + parameters.Length < 2 || + parameters[parameters.Length - 2].ParameterType != asyncCallbackType || + parameters[parameters.Length - 1].ParameterType != objectType || + method.ReturnType != asyncResultType) + { + return false; + } + return true; + } + + static internal bool IsBegin(OperationContractAttribute opSettings, MethodInfo method) + { + if (opSettings.AsyncPattern) + { + if (!HasBeginMethodShape(method)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.InvalidAsyncBeginMethodSignatureForMethod2, method.Name, method.DeclaringType.FullName))); + } + + return true; + } + return false; + } + + static internal bool IsTask(MethodInfo method) + { + if (method.ReturnType == taskType) + { + return true; + } + if (method.ReturnType.GetTypeInfo().IsGenericType && method.ReturnType.GetGenericTypeDefinition() == taskTResultType) + { + return true; + } + return false; + } + + static internal bool IsTask(MethodInfo method, out Type taskTResult) + { + taskTResult = null; + Type methodReturnType = method.ReturnType; + if (methodReturnType == taskType) + { + taskTResult = VoidType; + return true; + } + + if (methodReturnType.GetTypeInfo().IsGenericType && methodReturnType.GetGenericTypeDefinition() == taskTResultType) + { + taskTResult = methodReturnType.GetGenericArguments()[0]; + return true; + } + + return false; + } + + static internal bool HasEndMethodShape(MethodInfo method) + { + ParameterInfo[] parameters = method.GetParameters(); + if (!method.Name.StartsWith(EndMethodNamePrefix, StringComparison.Ordinal) || + parameters.Length < 1 || + parameters[parameters.Length - 1].ParameterType != asyncResultType) + { + return false; + } + return true; + } + + internal static OperationContractAttribute GetOperationContractAttribute(MethodInfo method) + { + OperationContractAttribute operationContractAttribute = GetSingleAttribute(method); + if (operationContractAttribute != null) + { + return operationContractAttribute; + } + IOperationContractAttributeProvider operationContractProvider = GetFirstAttribute(method); + if (operationContractProvider != null) + { + return operationContractProvider.GetOperationContractAttribute(); + } + return null; + } + + static internal bool IsBegin(MethodInfo method) + { + OperationContractAttribute opSettings = GetOperationContractAttribute(method); + if (opSettings == null) + return false; + return IsBegin(opSettings, method); + } + + static internal string GetLogicalName(MethodInfo method) + { + bool isAsync = IsBegin(method); + bool isTask = isAsync ? false : IsTask(method); + return GetLogicalName(method, isAsync, isTask); + } + + static internal string GetLogicalName(MethodInfo method, bool isAsync, bool isTask) + { + if (isAsync) + { + return method.Name.Substring(BeginMethodNamePrefix.Length); + } + else if (isTask && method.Name.EndsWith(AsyncMethodNameSuffix, StringComparison.Ordinal)) + { + return method.Name.Substring(0, method.Name.Length - AsyncMethodNameSuffix.Length); + } + else + { + return method.Name; + } + } + + static internal bool HasNoDisposableParameters(MethodInfo methodInfo) + { + foreach (ParameterInfo inputInfo in methodInfo.GetParameters()) + { + if (IsParameterDisposable(inputInfo.ParameterType)) + { + return false; + } + } + + if (methodInfo.ReturnParameter != null) + { + return (!IsParameterDisposable(methodInfo.ReturnParameter.ParameterType)); + } + + return true; + } + + static internal bool IsParameterDisposable(Type type) + { + return ((!type.GetTypeInfo().IsSealed) || typeof(IDisposable).IsAssignableFrom(type)); + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Description/TaskOperationDescriptionValidator.cs b/src/CoreWCF.Primitives/src/CoreWCF/Description/TaskOperationDescriptionValidator.cs new file mode 100644 index 000000000..b95b018a2 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Description/TaskOperationDescriptionValidator.cs @@ -0,0 +1,71 @@ +using System; +using System.Reflection; + +namespace CoreWCF.Description +{ + internal static class TaskOperationDescriptionValidator + { + internal static void Validate(OperationDescription operationDescription, bool isForService) + { + MethodInfo taskMethod = operationDescription.TaskMethod; + if (taskMethod != null) + { + if (isForService) + { + // no other method (sync, async) is allowed to co-exist with a task-based method on the server-side. + EnsureNoSyncMethod(operationDescription); + EnsureNoBeginEndMethod(operationDescription); + } + else + { + // no out/ref parameter is allowed on the client-side. + EnsureNoOutputParameters(taskMethod); + } + + EnsureParametersAreSupported(taskMethod); + } + } + + private static void EnsureNoSyncMethod(OperationDescription operation) + { + if (operation.SyncMethod != null) + { + string method1Name = operation.TaskMethod.Name; + string method2Name = operation.SyncMethod.Name; + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.CannotHaveTwoOperationsWithTheSameName3, method1Name, method2Name, operation.DeclaringContract.ContractType))); + } + } + + private static void EnsureNoBeginEndMethod(OperationDescription operation) + { + if (operation.BeginMethod != null) + { + string method1Name = operation.TaskMethod.Name; + string method2Name = operation.BeginMethod.Name; + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.CannotHaveTwoOperationsWithTheSameName3, method1Name, method2Name, operation.DeclaringContract.ContractType))); + } + } + + private static void EnsureParametersAreSupported(MethodInfo method) + { + foreach (ParameterInfo parameter in method.GetParameters()) + { + Type parameterType = parameter.ParameterType; + if ((parameterType == ServiceReflector.CancellationTokenType) || + (parameterType.GetTypeInfo().IsGenericType && parameterType.GetGenericTypeDefinition() == ServiceReflector.IProgressType)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.Format(SR.TaskMethodParameterNotSupported, parameterType))); + } + } + } + + private static void EnsureNoOutputParameters(MethodInfo method) + { + if (ServiceReflector.HasOutputParameters(method, false)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.TaskMethodMustNotHaveOutParameter)); + } + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Description/TypeLoader.cs b/src/CoreWCF.Primitives/src/CoreWCF/Description/TypeLoader.cs new file mode 100644 index 000000000..212dcacb4 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Description/TypeLoader.cs @@ -0,0 +1,2064 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics.Contracts; +//using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Linq; +using System.Reflection; +using System.Runtime; +using CoreWCF; +using CoreWCF.Channels; +using CoreWCF.Dispatcher; +using System.Xml; +using CoreWCF.Collections.Generic; +using CoreWCF.Runtime; + +namespace CoreWCF.Description +{ + internal class TypeLoader where TService : class + { + static Type[] messageContractMemberAttributes = { + typeof(MessageHeaderAttribute), + typeof(MessageBodyMemberAttribute), + typeof(MessagePropertyAttribute), + }; + + static Type[] formatterAttributes = { + typeof(XmlSerializerFormatAttribute), + typeof(DataContractFormatAttribute) + }; + + //static Type[] knownTypesMethodParamType = new Type[] { typeof(ICustomAttributeProvider) }; + + internal static DataContractFormatAttribute DefaultDataContractFormatAttribute = new DataContractFormatAttribute(); + //internal static XmlSerializerFormatAttribute DefaultXmlSerializerFormatAttribute = new XmlSerializerFormatAttribute(); + + static readonly Type OperationContractAttributeType = typeof(OperationContractAttribute); + + internal const string ReturnSuffix = "Result"; + internal const string ResponseSuffix = "Response"; + internal const string FaultSuffix = "Fault"; + + readonly object thisLock; + readonly Dictionary contracts; + readonly Dictionary messages; + + public TypeLoader() + { + thisLock = new object(); + contracts = new Dictionary(); + messages = new Dictionary(); + } + + ContractDescription LoadContractDescriptionHelper(Type contractType, TService serviceImplementation) + { + ContractDescription contractDescription; + if (contractType == typeof(IOutputChannel)) + { + contractDescription = LoadOutputChannelContractDescription(); + } + else if (contractType == typeof(IRequestChannel)) + { + contractDescription = LoadRequestChannelContractDescription(); + } + else + { + ServiceContractAttribute actualContractAttribute; + Type actualContractType = ServiceReflector.GetContractTypeAndAttribute(contractType, out actualContractAttribute); + lock (thisLock) + { + if (!contracts.TryGetValue(actualContractType, out contractDescription)) + { + EnsureNoInheritanceWithContractClasses(actualContractType); + EnsureNoOperationContractsOnNonServiceContractTypes(actualContractType); + ContractReflectionInfo reflectionInfo; + contractDescription = CreateContractDescription(actualContractAttribute, actualContractType, out reflectionInfo, serviceImplementation); + // IContractBehaviors + if (serviceImplementation != null && serviceImplementation is IContractBehavior) + { + contractDescription.ContractBehaviors.Add((IContractBehavior)serviceImplementation); + } + UpdateContractDescriptionWithAttributesFromServiceType(contractDescription); + foreach (ContractDescription inheritedContract in contractDescription.GetInheritedContracts()) + { + UpdateContractDescriptionWithAttributesFromServiceType(inheritedContract); + } + UpdateOperationsWithInterfaceAttributes(contractDescription, reflectionInfo); + AddBehaviors(contractDescription, false, reflectionInfo); + + contracts.Add(actualContractType, contractDescription); + } + } + } + return contractDescription; + } + + void EnsureNoInheritanceWithContractClasses(Type actualContractType) + { + var ti = actualContractType; + if (ti.IsClass) + { + // we only need to check base _classes_ here, the check for interfaces happens elsewhere + for (Type service = ti.BaseType; service != null; service = service.BaseType) + { + if (ServiceReflector.GetSingleAttribute(service) != null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException( + SR.Format(SR.SFxContractInheritanceRequiresInterfaces, actualContractType, service))); + } + } + } + } + + void EnsureNoOperationContractsOnNonServiceContractTypes(Type actualContractType) + { + foreach (Type t in actualContractType.GetInterfaces()) + { + EnsureNoOperationContractsOnNonServiceContractTypes_Helper(t); + } + for (Type u = actualContractType.BaseType; u != null; u = u.BaseType) + { + EnsureNoOperationContractsOnNonServiceContractTypes_Helper(u); + } + } + + void EnsureNoOperationContractsOnNonServiceContractTypes_Helper(Type aParentType) + { + // if not [ServiceContract] + if (ServiceReflector.GetSingleAttribute(aParentType) == null) + { + foreach (MethodInfo methodInfo in aParentType.GetMethods(TypeLoader.DefaultBindingFlags)) + { + // but does have an OperationContractAttribute + Type operationContractProviderType = ServiceReflector.GetOperationContractProviderType(methodInfo); + if (operationContractProviderType != null) + { + if (operationContractProviderType == OperationContractAttributeType) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format( + SR.SFxOperationContractOnNonServiceContract, methodInfo.Name, aParentType.Name))); + } + else + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format( + SR.SFxOperationContractProviderOnNonServiceContract, operationContractProviderType.Name, methodInfo.Name, aParentType.Name))); + } + } + } + } + } + + //public ContractDescription LoadContractDescription(Type contractType) + //{ + // Fx.Assert(contractType != null, ""); + + // return LoadContractDescriptionHelper(contractType, null, null); + //} + + public ContractDescription LoadContractDescription(Type contractType) + { + Fx.Assert(contractType != null, ""); + + return LoadContractDescriptionHelper(contractType, null); + } + + public ContractDescription LoadContractDescription(Type contractType, TService serviceImplementation) + { + Fx.Assert(contractType != null, ""); + Fx.Assert(serviceImplementation != null, ""); + + return LoadContractDescriptionHelper(contractType, serviceImplementation); + } + + ContractDescription LoadOutputChannelContractDescription() + { + Type channelType = typeof(IOutputChannel); + XmlQualifiedName contractName = NamingHelper.GetContractName(channelType, null, NamingHelper.MSNamespace); + ContractDescription contract = new ContractDescription(contractName.Name, contractName.Namespace); + contract.ContractType = channelType; + contract.ConfigurationName = channelType.FullName; + contract.SessionMode = SessionMode.NotAllowed; + OperationDescription operation = new OperationDescription("Send", contract); + MessageDescription message = new MessageDescription(MessageHeaders.WildcardAction, MessageDirection.Input); + operation.Messages.Add(message); + contract.Operations.Add(operation); + return contract; + } + + ContractDescription LoadRequestChannelContractDescription() + { + Type channelType = typeof(IRequestChannel); + XmlQualifiedName contractName = NamingHelper.GetContractName(channelType, null, NamingHelper.MSNamespace); + ContractDescription contract = new ContractDescription(contractName.Name, contractName.Namespace); + contract.ContractType = channelType; + contract.ConfigurationName = channelType.FullName; + contract.SessionMode = SessionMode.NotAllowed; + OperationDescription operation = new OperationDescription("Request", contract); + MessageDescription request = new MessageDescription(MessageHeaders.WildcardAction, MessageDirection.Input); + MessageDescription reply = new MessageDescription(MessageHeaders.WildcardAction, MessageDirection.Output); + operation.Messages.Add(request); + operation.Messages.Add(reply); + contract.Operations.Add(operation); + return contract; + } + + void AddBehaviors(ContractDescription contractDesc, bool implIsCallback, ContractReflectionInfo reflectionInfo) + { + ServiceContractAttribute contractAttr = ServiceReflector.GetRequiredSingleAttribute(reflectionInfo.iface); + for (int i = 0; i < contractDesc.Operations.Count; i++) + { + OperationDescription operationDescription = contractDesc.Operations[i]; + bool isInherited = operationDescription.DeclaringContract != contractDesc; + if (!isInherited) + { + operationDescription.OperationBehaviors.Add(new OperationInvokerBehavior()); + } + } + contractDesc.ContractBehaviors.Add(new OperationSelectorBehavior()); + + for (int i = 0; i < contractDesc.Operations.Count; i++) + { + OperationDescription opDesc = contractDesc.Operations[i]; + bool isInherited = opDesc.DeclaringContract != contractDesc; + Type targetIface = implIsCallback ? opDesc.DeclaringContract.CallbackContractType : opDesc.DeclaringContract.ContractType; + + // look for IOperationBehaviors on implementation methods in service class hierarchy + ApplyServiceInheritance( + opDesc.Behaviors, + delegate (Type currentType, KeyedByTypeCollection behaviors) + { + KeyedByTypeCollection toAdd = + GetIOperationBehaviorAttributesFromType(opDesc, targetIface, currentType); + for (int j = 0; j < toAdd.Count; j++) + { + behaviors.Add(toAdd[j]); + } + }); + // then look for IOperationBehaviors on interface type + if (!isInherited) + { + AddBehaviorsAtOneScope( + targetIface, opDesc.Behaviors, + delegate (Type currentType, KeyedByTypeCollection behaviors) + { + KeyedByTypeCollection toAdd = + GetIOperationBehaviorAttributesFromType(opDesc, targetIface, null); + for (int j = 0; j < toAdd.Count; j++) + { + behaviors.Add(toAdd[j]); + } + }); + } + } + + for (int i = 0; i < contractDesc.Operations.Count; i++) + { + OperationDescription opDesc = contractDesc.Operations[i]; + OperationBehaviorAttribute operationBehavior = opDesc.Behaviors.Find(); + if (operationBehavior == null) + { + operationBehavior = new OperationBehaviorAttribute(); + opDesc.Behaviors.Add(operationBehavior); + } + } + + Type targetInterface = implIsCallback ? reflectionInfo.callbackiface : reflectionInfo.iface; + AddBehaviorsAtOneScope>(targetInterface, contractDesc.Behaviors, + GetIContractBehaviorsFromInterfaceType); + + bool hasXmlSerializerMethod = false; + for (int i = 0; i < contractDesc.Operations.Count; i++) + { + OperationDescription operationDescription = contractDesc.Operations[i]; + bool isInherited = operationDescription.DeclaringContract != contractDesc; + MethodInfo opMethod = operationDescription.OperationMethod; + Attribute formattingAttribute = GetFormattingAttribute(opMethod, + GetFormattingAttribute(operationDescription.DeclaringContract.ContractType, + DefaultDataContractFormatAttribute)); + DataContractFormatAttribute dataContractFormatAttribute = formattingAttribute as DataContractFormatAttribute; + if (dataContractFormatAttribute != null) + { + if (!isInherited) + { + operationDescription.Behaviors.Add(new DataContractSerializerOperationBehavior(operationDescription, dataContractFormatAttribute, true)); + //operationDescription.Behaviors.Add(new DataContractSerializerOperationGenerator()); + } + } + else if (formattingAttribute != null && formattingAttribute is XmlSerializerFormatAttribute) + { + hasXmlSerializerMethod = true; + } + } + if (hasXmlSerializerMethod) + { + XmlSerializerOperationBehavior.AddBuiltInBehaviors(contractDesc); + } + } + + void GetIContractBehaviorsFromInterfaceType(Type interfaceType, KeyedByTypeCollection behaviors) + { + object[] ifaceAttributes = ServiceReflector.GetCustomAttributes(interfaceType, typeof(IContractBehavior), false); + for (int i = 0; i < ifaceAttributes.Length; i++) + { + IContractBehavior behavior = (IContractBehavior)ifaceAttributes[i]; + behaviors.Add(behavior); + } + } + + static void UpdateContractDescriptionWithAttributesFromServiceType(ContractDescription description) + { + ApplyServiceInheritance( + description.Behaviors, + delegate (Type currentType, KeyedByTypeCollection behaviors) + { + + foreach (IContractBehavior iContractBehavior in ServiceReflector.GetCustomAttributes(currentType, typeof(IContractBehavior), false)) + { + IContractBehaviorAttribute iContractBehaviorAttribute = iContractBehavior as IContractBehaviorAttribute; + if (iContractBehaviorAttribute == null + || (iContractBehaviorAttribute.TargetContract == null) + || (iContractBehaviorAttribute.TargetContract == description.ContractType)) + { + behaviors.Add(iContractBehavior); + } + } + }); + } + + void UpdateOperationsWithInterfaceAttributes(ContractDescription contractDesc, ContractReflectionInfo reflectionInfo) + { + object[] customAttributes = ServiceReflector.GetCustomAttributes(reflectionInfo.iface, typeof(ServiceKnownTypeAttribute), false); + IEnumerable knownTypes = GetKnownTypes(customAttributes, reflectionInfo.iface); + foreach (Type knownType in knownTypes) + { + foreach (OperationDescription operationDescription in contractDesc.Operations) + { + if (!operationDescription.IsServerInitiated()) + operationDescription.KnownTypes.Add(knownType); + } + } + + if (reflectionInfo.callbackiface != null) + { + customAttributes = ServiceReflector.GetCustomAttributes(reflectionInfo.callbackiface, typeof(ServiceKnownTypeAttribute), false); + knownTypes = GetKnownTypes(customAttributes, reflectionInfo.callbackiface); + foreach (Type knownType in knownTypes) + { + foreach (OperationDescription operationDescription in contractDesc.Operations) + { + if (operationDescription.IsServerInitiated()) + operationDescription.KnownTypes.Add(knownType); + } + } + } + } + + private IEnumerable GetKnownTypes(object[] knownTypeAttributes, CustomAttributeProvider provider) + { + // The named method must take a parameter of ICustomAttributeProvider which isn't available so this can only specify known types by Type + //if (knownTypeAttributes.Length == 1) + //{ + // ServiceKnownTypeAttribute knownTypeAttribute = (ServiceKnownTypeAttribute)knownTypeAttributes[0]; + // if (!string.IsNullOrEmpty(knownTypeAttribute.MethodName)) + // { + // Type type = knownTypeAttribute.DeclaringType; + // if (type == null) + // { + // type = (provider as TypeInfo)?.AsType(); + // if (type == null) + // type = ((MethodInfo)provider).DeclaringType; + // } + // type.GetMethods() + // MethodInfo method = type.GetMethod(knownTypeAttribute.MethodName, BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public, null, knownTypesMethodParamType, null); + // if (method == null) + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxKnownTypeAttributeUnknownMethod3, provider, knownTypeAttribute.MethodName, type.FullName))); + + // if (!typeof(IEnumerable).IsAssignableFrom(method.ReturnType)) + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxKnownTypeAttributeReturnType3, provider, knownTypeAttribute.MethodName, type.FullName))); + + // return (IEnumerable)method.Invoke(null, new object[] { provider }); + // } + //} + + List knownTypes = new List(); + for (int i = 0; i < knownTypeAttributes.Length; ++i) + { + ServiceKnownTypeAttribute knownTypeAttribute = (ServiceKnownTypeAttribute)knownTypeAttributes[i]; + if (knownTypeAttribute.Type == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxKnownTypeAttributeInvalid1, provider.ToString()))); + knownTypes.Add(knownTypeAttribute.Type); + } + return knownTypes; + } + + KeyedByTypeCollection GetIOperationBehaviorAttributesFromType(OperationDescription opDesc, Type targetIface, Type implType) + { + var result = new KeyedByTypeCollection(); + var ifaceMap = default(InterfaceMapping); + bool useImplAttrs = false; + if (implType != null) + { + if (targetIface.IsAssignableFrom(implType) && targetIface.IsInterface) + { + ifaceMap = implType.GetInterfaceMap(targetIface); + useImplAttrs = true; + } + else + { + // implType does not implement any methods from the targetIface, so there is nothing to do + return result; + } + } + MethodInfo opMethod = opDesc.OperationMethod; + ProcessOpMethod(opMethod, true, opDesc, result, ifaceMap, useImplAttrs); + if (opDesc.SyncMethod != null && opDesc.BeginMethod != null) + { + ProcessOpMethod(opDesc.BeginMethod, false, opDesc, result, ifaceMap, useImplAttrs); + } + else if (opDesc.SyncMethod != null && opDesc.TaskMethod != null) + { + ProcessOpMethod(opDesc.TaskMethod, false, opDesc, result, ifaceMap, useImplAttrs); + } + else if (opDesc.TaskMethod != null && opDesc.BeginMethod != null) + { + ProcessOpMethod(opDesc.BeginMethod, false, opDesc, result, ifaceMap, useImplAttrs); + } + return result; + } + + void ProcessOpMethod(MethodInfo opMethod, bool canHaveBehaviors, + OperationDescription opDesc, KeyedByTypeCollection result, + InterfaceMapping ifaceMap, bool useImplAttrs) + { + MethodInfo method = null; + if (useImplAttrs) + { + int methodIndex = Array.IndexOf(ifaceMap.InterfaceMethods, opMethod); + // if opMethod doesn't exist in the interfacemap, it means opMethod was on + // the "other" interface (not the one implemented by implType) + if (methodIndex != -1) + { + MethodInfo implMethod = ifaceMap.TargetMethods[methodIndex]; + // C++ allows you to create abstract classes that have missing interface method + // implementations, which shows up as nulls in the interfacemapping + if (implMethod != null) + { + method = implMethod; + } + } + if (method == null) + { + return; + } + } + else + { + method = opMethod; + } + + object[] methodAttributes = ServiceReflector.GetCustomAttributes(method, typeof(IOperationBehavior), false); + for (int k = 0; k < methodAttributes.Length; k++) + { + IOperationBehavior opBehaviorAttr = (IOperationBehavior)methodAttributes[k]; + if (canHaveBehaviors) + { + result.Add(opBehaviorAttr); + } + else + { + if (opDesc.SyncMethod != null && opDesc.BeginMethod != null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new InvalidOperationException(SR.Format(SR.SyncAsyncMatchConsistency_Attributes6, + opDesc.SyncMethod.Name, + opDesc.SyncMethod.DeclaringType, + opDesc.BeginMethod.Name, + opDesc.EndMethod.Name, + opDesc.Name, + opBehaviorAttr.GetType().FullName))); + } + else if (opDesc.SyncMethod != null && opDesc.TaskMethod != null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new InvalidOperationException(SR.Format(SR.SyncTaskMatchConsistency_Attributes6, + opDesc.SyncMethod.Name, + opDesc.SyncMethod.DeclaringType, + opDesc.TaskMethod.Name, + opDesc.Name, + opBehaviorAttr.GetType().FullName))); + } + else if (opDesc.TaskMethod != null && opDesc.BeginMethod != null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new InvalidOperationException(SR.Format(SR.TaskAsyncMatchConsistency_Attributes6, + opDesc.TaskMethod.Name, + opDesc.TaskMethod.DeclaringType, + opDesc.BeginMethod.Name, + opDesc.EndMethod.Name, + opDesc.Name, + opBehaviorAttr.GetType().FullName))); + } + Fx.Assert("Invalid state. No exception for canHaveBehaviors = false"); + } + } + } + + // Returns true if the given methods match in name and parameter types + private static bool MethodsMatch(MethodInfo method1, MethodInfo method2) + { + Contract.Assert(method1 != null); + Contract.Assert(method2 != null); + + if (method1.Equals(method2)) + { + return true; + } + + if (method1.ReturnType != method2.ReturnType || + !string.Equals(method1.Name, method2.Name, StringComparison.Ordinal) || + !ParameterInfosMatch(method1.ReturnParameter, method2.ReturnParameter)) + { + return false; + } + + ParameterInfo[] parameters1 = method1.GetParameters(); + ParameterInfo[] parameters2 = method2.GetParameters(); + if (parameters1.Length != parameters2.Length) + { + return false; + } + + for (int i = 0; i < parameters1.Length; ++i) + { + if (!ParameterInfosMatch(parameters1[i], parameters2[i])) + { + return false; + } + } + + return true; + } + + // Returns true if 2 ParameterInfo's match in signature with respect + // to the MemberInfo's in which they are declared. Position is required + // to match but name is not. + private static bool ParameterInfosMatch(ParameterInfo parameterInfo1, ParameterInfo parameterInfo2) + { + // Null is possible for a ParameterInfo from MethodInfo.ReturnParameter. + // If both are null, we have no information to compare and say they are equal. + if (parameterInfo1 == null && parameterInfo2 == null) + { + return true; + } + + if (parameterInfo1 == null || parameterInfo2 == null) + { + return false; + } + + if (parameterInfo1.Equals(parameterInfo2)) + { + return true; + } + + return ((parameterInfo1.ParameterType == parameterInfo2.ParameterType) && + (parameterInfo1.IsIn == parameterInfo2.IsIn) && + (parameterInfo1.IsOut == parameterInfo2.IsOut) && + (parameterInfo1.IsRetval == parameterInfo2.IsRetval) && + (parameterInfo1.Position == parameterInfo2.Position)); + } + + // internal void AddBehaviorsSFx(ServiceEndpoint serviceEndpoint, Type contractType) + // { + // if (serviceEndpoint.Contract.IsDuplex()) + // { + // CallbackBehaviorAttribute attr = serviceEndpoint.Behaviors.Find(); + // if (attr == null) + // { + // serviceEndpoint.Behaviors.Insert(0, new CallbackBehaviorAttribute()); + // } + // } + // } + + // internal void AddBehaviorsFromImplementationType(ServiceEndpoint serviceEndpoint, Type implementationType) + // { + // foreach (IEndpointBehavior behaviorAttribute in ServiceReflector.GetCustomAttributes(implementationType, typeof(IEndpointBehavior), false)) + // { + // if (behaviorAttribute is CallbackBehaviorAttribute) + // { + // serviceEndpoint.Behaviors.Insert(0, behaviorAttribute); + // } + // else + // { + // serviceEndpoint.Behaviors.Add(behaviorAttribute); + // } + // } + // foreach (IContractBehavior behaviorAttribute in ServiceReflector.GetCustomAttributes(implementationType, typeof(IContractBehavior), false)) + // { + // serviceEndpoint.Contract.Behaviors.Add(behaviorAttribute); + // } + // Type targetIface = serviceEndpoint.Contract.CallbackContractType; + // for (int i = 0; i < serviceEndpoint.Contract.Operations.Count; i++) + // { + // OperationDescription opDesc = serviceEndpoint.Contract.Operations[i]; + // KeyedByTypeCollection opBehaviors = new KeyedByTypeCollection(); + // // look for IOperationBehaviors on implementation methods in callback class hierarchy + // ApplyServiceInheritance>( + // implementationType, opBehaviors, + // delegate (Type currentType, KeyedByTypeCollection behaviors) + // { + // KeyedByTypeCollection toAdd = + // GetIOperationBehaviorAttributesFromType(opDesc, targetIface, currentType); + // for (int j = 0; j < toAdd.Count; j++) + // { + // behaviors.Add(toAdd[j]); + // } + // }); + // // a bunch of default IOperationBehaviors have already been added, which we may need to replace + // for (int k = 0; k < opBehaviors.Count; k++) + // { + // IOperationBehavior behavior = opBehaviors[k]; + // Type t = behavior.GetType(); + // if (opDesc.Behaviors.Contains(t)) + // { + // opDesc.Behaviors.Remove(t); + // } + // opDesc.Behaviors.Add(behavior); + // } + // } + // } + + internal static int CompareMessagePartDescriptions(MessagePartDescription a, MessagePartDescription b) + { + int posCmp = a.SerializationPosition - b.SerializationPosition; + if (posCmp != 0) + { + return posCmp; + } + + int nsCmp = string.Compare(a.Namespace, b.Namespace, StringComparison.Ordinal); + if (nsCmp != 0) + { + return nsCmp; + } + + return string.Compare(a.Name, b.Name, StringComparison.Ordinal); + } + + internal static XmlName GetBodyWrapperResponseName(string operationName) + { +#if DEBUG + Fx.Assert(NamingHelper.IsValidNCName(operationName), "operationName value has to be a valid NCName."); +#endif + return new XmlName(operationName + ResponseSuffix); + } + + internal static XmlName GetBodyWrapperResponseName(XmlName operationName) + { + return new XmlName(operationName.EncodedName + ResponseSuffix, true /*isEncoded*/); + } + + void CreateOperationDescriptions(ContractDescription contractDescription, + ContractReflectionInfo reflectionInfo, + Type contractToGetMethodsFrom, + ContractDescription declaringContract, + MessageDirection direction + ) + { + MessageDirection otherDirection = MessageDirectionHelper.Opposite(direction); + if (!(declaringContract.ContractType.IsAssignableFrom(contractDescription.ContractType))) + { + Fx.Assert("bad contract inheritance"); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException( + string.Format(CultureInfo.InvariantCulture, "Bad contract inheritence. Contract {0} does not implement {1}", declaringContract.ContractType.Name, contractDescription.ContractType.Name) + )); + } + + foreach (MethodInfo methodInfo in contractToGetMethodsFrom.GetMethods(TypeLoader.DefaultBindingFlags)) + { + if (contractToGetMethodsFrom.IsInterface) + { + object[] attrs = ServiceReflector.GetCustomAttributes(methodInfo, typeof(OperationBehaviorAttribute), false); + if (attrs.Length != 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException( + SR.Format(SR.SFxOperationBehaviorAttributeOnlyOnServiceClass, methodInfo.Name, contractToGetMethodsFrom.Name))); + } + } + ServiceReflector.ValidateParameterMetadata(methodInfo); + OperationDescription operation = CreateOperationDescription(contractDescription, methodInfo, direction, reflectionInfo, declaringContract); + if (operation != null) + { + contractDescription.Operations.Add(operation); + } + } + } + + //Checks whether that the Callback contract provided on a ServiceContract follows rules + //1. It has to be a interface + //2. If its a class then it needs to implement MarshallByRefObject + internal static void EnsureCallbackType(Type callbackType) + { + if (callbackType != null && !callbackType.IsInterface && !callbackType.IsMarshalByRef) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.Format(SR.SFxInvalidCallbackContractType, callbackType.Name)); + } + } + + // checks a contract for substitutability (in the Liskov Substitution Principle sense), throws on error + internal static void EnsureSubcontract(ServiceContractAttribute svcContractAttr, Type contractType) + { + Type callbackType = svcContractAttr.CallbackContract; + + List types = ServiceReflector.GetInheritedContractTypes(contractType); + for (int i = 0; i < types.Count; i++) + { + Type inheritedContractType = types[i]; + ServiceContractAttribute inheritedContractAttr = ServiceReflector.GetRequiredSingleAttribute(inheritedContractType); + // we must be covariant in our callbacks + if (inheritedContractAttr.CallbackContract != null) + { + if (callbackType == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException( + SR.Format(SR.InAContractInheritanceHierarchyIfParentHasCallbackChildMustToo, + inheritedContractType.Name, inheritedContractAttr.CallbackContract.Name, contractType.Name))); + } + if (!inheritedContractAttr.CallbackContract.IsAssignableFrom(callbackType)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException( + SR.Format(SR.InAContractInheritanceHierarchyTheServiceContract3_2, + inheritedContractType.Name, contractType.Name))); + } + } + } + } + + ContractDescription CreateContractDescription(ServiceContractAttribute contractAttr, Type contractType, out ContractReflectionInfo reflectionInfo, TService serviceImplementation) + { + reflectionInfo = new ContractReflectionInfo(); + + XmlQualifiedName contractName = NamingHelper.GetContractName(contractType, contractAttr.Name, contractAttr.Namespace); + ContractDescription contractDescription = new ContractDescription(contractName.Name, contractName.Namespace); + contractDescription.ContractType = contractType; + + // MessageSecurity not supported + //if (contractAttr.HasProtectionLevel) + //{ + // contractDescription.ProtectionLevel = contractAttr.ProtectionLevel; + //} + + Type callbackType = contractAttr.CallbackContract; + + EnsureCallbackType(callbackType); + + EnsureSubcontract(contractAttr, contractType); + + // reflect the methods in contractType and add OperationDescriptions to ContractDescription + reflectionInfo.iface = contractType; + reflectionInfo.callbackiface = callbackType; + + contractDescription.SessionMode = contractAttr.SessionMode; + contractDescription.CallbackContractType = callbackType; + contractDescription.ConfigurationName = contractAttr.ConfigurationName ?? contractType.FullName; + + // get inherited operations + List types = ServiceReflector.GetInheritedContractTypes(contractType); + List inheritedCallbackTypes = new List(); + for (int i = 0; i < types.Count; i++) + { + Type inheritedContractType = types[i]; + ServiceContractAttribute inheritedContractAttr = ServiceReflector.GetRequiredSingleAttribute(inheritedContractType); + ContractDescription inheritedContractDescription = LoadContractDescriptionHelper(inheritedContractType, serviceImplementation); + foreach (OperationDescription op in inheritedContractDescription.Operations) + { + if (!contractDescription.Operations.Contains(op)) // in a diamond hierarchy, ensure we don't add same op twice from two different parents + { + // ensure two different parents don't try to add conflicting operations + Collection existingOps = contractDescription.Operations.FindAll(op.Name); + foreach (OperationDescription existingOp in existingOps) + { + if (existingOp.Messages[0].Direction == op.Messages[0].Direction) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException( + SR.Format(SR.CannotInheritTwoOperationsWithTheSameName3, + op.Name, inheritedContractDescription.Name, existingOp.DeclaringContract.Name))); + } + } + contractDescription.Operations.Add(op); + } + } + if (inheritedContractDescription.CallbackContractType != null) + { + inheritedCallbackTypes.Add(inheritedContractDescription.CallbackContractType); + } + } + + // this contract + CreateOperationDescriptions(contractDescription, reflectionInfo, contractType, contractDescription, MessageDirection.Input); + // CallbackContract + if (callbackType != null && !inheritedCallbackTypes.Contains(callbackType)) + { + CreateOperationDescriptions(contractDescription, reflectionInfo, callbackType, contractDescription, MessageDirection.Output); + } + + return contractDescription; + } + + internal static Attribute GetFormattingAttribute(CustomAttributeProvider attrProvider, Attribute defaultFormatAttribute) + { + if (attrProvider != null) + { + if (attrProvider.IsDefined(typeof(XmlSerializerFormatAttribute), false)) + { + return ServiceReflector.GetSingleAttribute(attrProvider, formatterAttributes); + } + if (attrProvider.IsDefined(typeof(DataContractFormatAttribute), false)) + { + return ServiceReflector.GetSingleAttribute(attrProvider, formatterAttributes); + } + } + return defaultFormatAttribute; + } + + //Sync and Async should follow the rules: + // 1. Parameter match + // 2. Async cannot have behaviors (verification happens later in ProcessOpMethod - behaviors haven't yet been loaded here) + // 3. Async cannot have known types + // 4. Async cannot have known faults + // 5. Sync and Async have to match on OneWay status + // 6. Sync and Async have to match Action and ReplyAction + void VerifyConsistency(OperationConsistencyVerifier verifier) + { + verifier.VerifyParameterLength(); + verifier.VerifyParameterType(); + verifier.VerifyOutParameterType(); + verifier.VerifyReturnType(); + verifier.VerifyFaultContractAttribute(); + verifier.VerifyKnownTypeAttribute(); + verifier.VerifyIsOneWayStatus(); + verifier.VerifyActionAndReplyAction(); + } + + // "direction" is the "direction of the interface" (from the perspective of the server, as usual): + // proxy interface on client: MessageDirection.Input + // callback interface on client: MessageDirection.Output + // service interface (or class) on server: MessageDirection.Input + // callback interface on server: MessageDirection.Output + OperationDescription CreateOperationDescription(ContractDescription contractDescription, MethodInfo methodInfo, MessageDirection direction, + ContractReflectionInfo reflectionInfo, ContractDescription declaringContract) + { + OperationContractAttribute opAttr = ServiceReflector.GetOperationContractAttribute(methodInfo); + if (opAttr == null) + { + return null; + } + + if (ServiceReflector.HasEndMethodShape(methodInfo)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException( + SR.Format(SR.EndMethodsCannotBeDecoratedWithOperationContractAttribute, + methodInfo.Name, reflectionInfo.iface))); + } + + Type taskTResult; + bool isTask = ServiceReflector.IsTask(methodInfo, out taskTResult); + bool isAsync = !isTask && ServiceReflector.IsBegin(opAttr, methodInfo); + + XmlName operationName = NamingHelper.GetOperationName(ServiceReflector.GetLogicalName(methodInfo, isAsync, isTask), opAttr.Name); + + opAttr.EnsureInvariants(methodInfo, operationName.EncodedName); + + Collection operations = contractDescription.Operations.FindAll(operationName.EncodedName); + for (int i = 0; i < operations.Count; i++) + { + OperationDescription existingOp = operations[i]; + if (existingOp.Messages[0].Direction == direction) + { + // if we have already seen a task-based method with the same name, we need to throw. + if (existingOp.TaskMethod != null && isTask) + { + string method1Name = existingOp.OperationMethod.Name; + string method2Name = methodInfo.Name; + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.CannotHaveTwoOperationsWithTheSameName3, method1Name, method2Name, reflectionInfo.iface))); + } + if (isAsync && (existingOp.BeginMethod != null)) + { + string method1Name = existingOp.BeginMethod.Name; + string method2Name = methodInfo.Name; + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.CannotHaveTwoOperationsWithTheSameName3, method1Name, method2Name, reflectionInfo.iface))); + } + if (!isAsync && !isTask && (existingOp.SyncMethod != null)) + { + string method1Name = existingOp.SyncMethod.Name; + string method2Name = methodInfo.Name; + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.CannotHaveTwoOperationsWithTheSameName3, method1Name, method2Name, reflectionInfo.iface))); + } + + contractDescription.Operations.Remove(existingOp); + OperationDescription newOp = CreateOperationDescription(contractDescription, + methodInfo, + direction, + reflectionInfo, + declaringContract); + + newOp.HasNoDisposableParameters = ServiceReflector.HasNoDisposableParameters(methodInfo); + + if (isTask) + { + existingOp.TaskMethod = newOp.TaskMethod; + existingOp.TaskTResult = newOp.TaskTResult; + if (existingOp.SyncMethod != null) + { + // Task vs. Sync + VerifyConsistency(new SyncTaskOperationConsistencyVerifier(existingOp, newOp)); + } + else + { + // Task vs. Async + VerifyConsistency(new TaskAsyncOperationConsistencyVerifier(newOp, existingOp)); + } + return existingOp; + } + else if (isAsync) + { + existingOp.BeginMethod = newOp.BeginMethod; + existingOp.EndMethod = newOp.EndMethod; + if (existingOp.SyncMethod != null) + { + // Async vs. Sync + VerifyConsistency(new SyncAsyncOperationConsistencyVerifier(existingOp, newOp)); + } + else + { + // Async vs. Task + VerifyConsistency(new TaskAsyncOperationConsistencyVerifier(existingOp, newOp)); + } + return existingOp; + } + else + { + newOp.BeginMethod = existingOp.BeginMethod; + newOp.EndMethod = existingOp.EndMethod; + newOp.TaskMethod = existingOp.TaskMethod; + newOp.TaskTResult = existingOp.TaskTResult; + if (existingOp.TaskMethod != null) + { + // Sync vs. Task + VerifyConsistency(new SyncTaskOperationConsistencyVerifier(newOp, existingOp)); + } + else + { + // Sync vs. Async + VerifyConsistency(new SyncAsyncOperationConsistencyVerifier(newOp, existingOp)); + } + return newOp; + } + } + } + + OperationDescription operationDescription = new OperationDescription(operationName.EncodedName, declaringContract); + //operationDescription.IsInitiating = opAttr.IsInitiating; + //operationDescription.IsTerminating = opAttr.IsTerminating; + operationDescription.IsSessionOpenNotificationEnabled = opAttr.IsSessionOpenNotificationEnabled; + + operationDescription.HasNoDisposableParameters = ServiceReflector.HasNoDisposableParameters(methodInfo); + + //if (opAttr.HasProtectionLevel) + //{ + // operationDescription.ProtectionLevel = opAttr.ProtectionLevel; + //} + + XmlQualifiedName contractQname = new XmlQualifiedName(declaringContract.Name, declaringContract.Namespace); + + object[] methodAttributes = ServiceReflector.GetCustomAttributes(methodInfo, typeof(FaultContractAttribute), false); + + if (opAttr.IsOneWay && methodAttributes.Length > 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.OneWayAndFaultsIncompatible2, methodInfo.DeclaringType.FullName, operationName.EncodedName))); + } + + for (int i = 0; i < methodAttributes.Length; i++) + { + FaultContractAttribute knownFault = (FaultContractAttribute)methodAttributes[i]; + FaultDescription faultDescription = CreateFaultDescription(knownFault, contractQname, declaringContract.Namespace, operationDescription.XmlName); + CheckDuplicateFaultContract(operationDescription.Faults, faultDescription, operationName.EncodedName); + operationDescription.Faults.Add(faultDescription); + } + + methodAttributes = ServiceReflector.GetCustomAttributes(methodInfo, typeof(ServiceKnownTypeAttribute), false); + IEnumerable knownTypes = GetKnownTypes(methodAttributes, methodInfo); + foreach (Type knownType in knownTypes) + operationDescription.KnownTypes.Add(knownType); + + MessageDirection requestDirection = direction; + MessageDirection responseDirection = MessageDirectionHelper.Opposite(direction); + + string requestAction = NamingHelper.GetMessageAction(contractQname, + operationDescription.CodeName, + opAttr.Action, + false); + + string responseAction = NamingHelper.GetMessageAction(contractQname, + operationDescription.CodeName, + opAttr.ReplyAction, + true); + XmlName wrapperName = operationName; + XmlName wrapperResponseName = GetBodyWrapperResponseName(operationName); + string wrapperNamespace = declaringContract.Namespace; + + MessageDescription requestDescription = CreateMessageDescription(methodInfo, + isAsync, + isTask, + null, + null, + contractDescription.Namespace, + requestAction, + wrapperName, + wrapperNamespace, + requestDirection); + MessageDescription responseDescription = null; + operationDescription.Messages.Add(requestDescription); + MethodInfo outputMethod = methodInfo; + if (isTask) + { + operationDescription.TaskMethod = methodInfo; + operationDescription.TaskTResult = taskTResult; + } + else if (!isAsync) + { + operationDescription.SyncMethod = methodInfo; + } + else + { + outputMethod = ServiceReflector.GetEndMethod(methodInfo); + operationDescription.EndMethod = outputMethod; + operationDescription.BeginMethod = methodInfo; + } + + if (!opAttr.IsOneWay) + { + XmlName returnValueName = GetReturnValueName(operationName); + responseDescription = CreateMessageDescription(outputMethod, + isAsync, + isTask, + taskTResult, + returnValueName, + contractDescription.Namespace, + responseAction, + wrapperResponseName, + wrapperNamespace, + responseDirection); + operationDescription.Messages.Add(responseDescription); + } + else + { + if ((!isTask && outputMethod.ReturnType != ServiceReflector.VoidType) || (isTask && taskTResult != ServiceReflector.VoidType) || + ServiceReflector.HasOutputParameters(outputMethod, isAsync)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.ServiceOperationsMarkedWithIsOneWayTrueMust0)); + } + + if (opAttr.ReplyAction != null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.OneWayOperationShouldNotSpecifyAReplyAction1, operationName))); + } + } + + if (!opAttr.IsOneWay) + { + if (responseDescription.IsVoid && + (requestDescription.IsUntypedMessage || requestDescription.IsTypedMessage)) + { + responseDescription.Body.WrapperName = responseDescription.Body.WrapperNamespace = null; + } + else if (requestDescription.IsVoid && + (responseDescription.IsUntypedMessage || responseDescription.IsTypedMessage)) + { + requestDescription.Body.WrapperName = requestDescription.Body.WrapperNamespace = null; + } + } + return operationDescription; + } + + private void CheckDuplicateFaultContract(FaultDescriptionCollection faultDescriptionCollection, FaultDescription fault, string operationName) + { + foreach (FaultDescription existingFault in faultDescriptionCollection) + { + if (XmlName.IsNullOrEmpty(existingFault.ElementName) && XmlName.IsNullOrEmpty(fault.ElementName) && existingFault.DetailType == fault.DetailType) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException( + SR.Format(SR.SFxFaultContractDuplicateDetailType, operationName, fault.DetailType))); + if (!XmlName.IsNullOrEmpty(existingFault.ElementName) && !XmlName.IsNullOrEmpty(fault.ElementName) && existingFault.ElementName == fault.ElementName && existingFault.Namespace == fault.Namespace) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException( + SR.Format(SR.SFxFaultContractDuplicateElement, operationName, fault.ElementName, fault.Namespace))); + } + } + + FaultDescription CreateFaultDescription(FaultContractAttribute attr, + XmlQualifiedName contractName, + string contractNamespace, + XmlName operationName) + { + XmlName faultName = new XmlName(attr.Name ?? NamingHelper.TypeName(attr.DetailType) + FaultSuffix); + FaultDescription fault = new FaultDescription(NamingHelper.GetMessageAction(contractName, operationName.DecodedName + faultName.DecodedName, attr.Action, false/*isResponse*/)); + if (attr.Name != null) + fault.SetNameAndElement(faultName); + else + fault.SetNameOnly(faultName); + fault.Namespace = attr.Namespace ?? contractNamespace; + fault.DetailType = attr.DetailType; + //if (attr.HasProtectionLevel) + //{ + // fault.ProtectionLevel = attr.ProtectionLevel; + //} + return fault; + } + + MessageDescription CreateMessageDescription(MethodInfo methodInfo, + bool isAsync, + bool isTask, + Type taskTResult, + XmlName returnValueName, + string defaultNS, + string action, + XmlName wrapperName, + string wrapperNamespace, + MessageDirection direction) + { + string methodName = methodInfo.Name; + MessageDescription messageDescription; + if (returnValueName == null) + { + ParameterInfo[] parameters = ServiceReflector.GetInputParameters(methodInfo, isAsync); + if (parameters.Length == 1 && parameters[0].ParameterType.IsDefined(typeof(MessageContractAttribute), false)) + { + messageDescription = CreateTypedMessageDescription(parameters[0].ParameterType, + null, + null, + defaultNS, + action, + direction); + } + else + { + messageDescription = CreateParameterMessageDescription(parameters, + null, + null, + null, + methodName, + defaultNS, + action, + wrapperName, + wrapperNamespace, + direction); + } + } + else + { + ParameterInfo[] parameters = ServiceReflector.GetOutputParameters(methodInfo, isAsync); + Type responseType = isTask ? taskTResult : methodInfo.ReturnType; + if (responseType.IsDefined(typeof(MessageContractAttribute), false) && parameters.Length == 0) + { + messageDescription = CreateTypedMessageDescription(responseType, + methodInfo.ReturnParameter, + returnValueName, + defaultNS, + action, + direction); + } + else + { + messageDescription = CreateParameterMessageDescription(parameters, + responseType, + methodInfo.ReturnParameter, + returnValueName, + methodName, + defaultNS, + action, + wrapperName, + wrapperNamespace, + direction); + } + } + + bool hasUnknownHeaders = false; + for (int i = 0; i < messageDescription.Headers.Count; i++) + { + MessageHeaderDescription header = messageDescription.Headers[i]; + if (header.IsUnknownHeaderCollection) + { + if (hasUnknownHeaders) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxMultipleUnknownHeaders, methodInfo, methodInfo.DeclaringType))); + } + else + { + hasUnknownHeaders = true; + } + } + } + return messageDescription; + } + + MessageDescription CreateParameterMessageDescription(ParameterInfo[] parameters, + Type returnType, + CustomAttributeProvider returnAttrProvider, + XmlName returnValueName, + string methodName, + string defaultNS, + string action, + XmlName wrapperName, + string wrapperNamespace, + MessageDirection direction) + { + foreach (ParameterInfo param in parameters) + { + if (TypeLoader.GetParameterType(param).IsDefined(typeof(MessageContractAttribute), false/*inherit*/)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxInvalidMessageContractSignature, methodName))); + } + } + if (returnType != null && returnType.IsDefined(typeof(MessageContractAttribute), false/*inherit*/)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxInvalidMessageContractSignature, methodName))); + } + + MessageDescription messageDescription = new MessageDescription(action, direction); + MessagePartDescriptionCollection partDescriptionCollection = messageDescription.Body.Parts; + for (int index = 0; index < parameters.Length; index++) + { + MessagePartDescription partDescription = CreateParameterPartDescription(new XmlName(parameters[index].Name), defaultNS, index, parameters[index], TypeLoader.GetParameterType(parameters[index])); + if (partDescriptionCollection.Contains(new XmlQualifiedName(partDescription.Name, partDescription.Namespace))) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidMessageContractException(SR.Format(SR.SFxDuplicateMessageParts, partDescription.Name, partDescription.Namespace))); + messageDescription.Body.Parts.Add(partDescription); + } + + if (returnType != null) + { + messageDescription.Body.ReturnValue = CreateParameterPartDescription(returnValueName, defaultNS, 0, returnAttrProvider, returnType); + } + if (messageDescription.IsUntypedMessage) + { + messageDescription.Body.WrapperName = null; + messageDescription.Body.WrapperNamespace = null; + } + else + { + messageDescription.Body.WrapperName = wrapperName.EncodedName; + messageDescription.Body.WrapperNamespace = wrapperNamespace; + } + + return messageDescription; + } + + private static MessagePartDescription CreateParameterPartDescription(XmlName defaultName, string defaultNS, int index, CustomAttributeProvider attrProvider, Type type) + { + MessagePartDescription parameterPart; + MessageParameterAttribute paramAttr = ServiceReflector.GetSingleAttribute(attrProvider); + + XmlName name = paramAttr == null || !paramAttr.IsNameSetExplicit ? defaultName : new XmlName(paramAttr.Name); + parameterPart = new MessagePartDescription(name.EncodedName, defaultNS); + parameterPart.Type = type; + parameterPart.Index = index; + parameterPart.AdditionalAttributesProvider = attrProvider; + return parameterPart; + } + + internal MessageDescription CreateTypedMessageDescription(Type typedMessageType, + CustomAttributeProvider returnAttrProvider, + XmlName returnValueName, + string defaultNS, + string action, + MessageDirection direction) + { + + + MessageDescription messageDescription; + bool messageItemsInitialized = false; + MessageDescriptionItems messageItems; + MessageContractAttribute messageContractAttribute = ServiceReflector.GetSingleAttribute(typedMessageType); + if (messages.TryGetValue(typedMessageType, out messageItems)) + { + messageDescription = new MessageDescription(action, direction, messageItems); + messageItemsInitialized = true; + } + else + messageDescription = new MessageDescription(action, direction, null); + messageDescription.MessageType = typedMessageType; + messageDescription.MessageName = new XmlName(NamingHelper.TypeName(typedMessageType)); + if (messageContractAttribute.IsWrapped) + { + messageDescription.Body.WrapperName = GetWrapperName(messageContractAttribute.WrapperName, messageDescription.MessageName).EncodedName; + messageDescription.Body.WrapperNamespace = messageContractAttribute.WrapperNamespace ?? defaultNS; + } + List contractMembers = new List(); + + for (Type baseType = typedMessageType; baseType != null && baseType != typeof(object) && baseType != typeof(ValueType); baseType = baseType.BaseType) + { + if (!baseType.IsDefined(typeof(MessageContractAttribute), false/*inherit*/)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxMessageContractBaseTypeNotValid, baseType, typedMessageType))); + } + if (messageItemsInitialized) + continue; + foreach (MemberInfo memberInfo in baseType.GetMembers(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) + { + if (!(memberInfo is FieldInfo) && + !(memberInfo is PropertyInfo)) + { + continue; + } + PropertyInfo property = memberInfo as PropertyInfo; + if (property != null) + { + MethodInfo getMethod = property.GetGetMethod(true); + if (getMethod != null && IsMethodOverriding(getMethod)) + { + continue; + } + MethodInfo setMethod = property.GetSetMethod(true); + if (setMethod != null && IsMethodOverriding(setMethod)) + { + continue; + } + } + + if (memberInfo.IsDefined(typeof(MessageBodyMemberAttribute), false) || + memberInfo.IsDefined(typeof(MessageHeaderAttribute), false) || + memberInfo.IsDefined(typeof(MessageHeaderArrayAttribute), false) || + memberInfo.IsDefined(typeof(MessagePropertyAttribute), false) + ) + { + contractMembers.Add(memberInfo); + } + } + } + + if (messageItemsInitialized) + return messageDescription; + + List bodyPartDescriptionList = new List(); + List headerPartDescriptionList = new List(); + for (int i = 0; i < contractMembers.Count; i++) + { + MemberInfo memberInfo = contractMembers[i]; + + Type memberType; + if (memberInfo is PropertyInfo) + { + memberType = ((PropertyInfo)memberInfo).PropertyType; + } + else + { + memberType = ((FieldInfo)memberInfo).FieldType; + } + + if (memberInfo.IsDefined(typeof(MessageHeaderArrayAttribute), false) + || memberInfo.IsDefined(typeof(MessageHeaderAttribute), false)) + { + headerPartDescriptionList.Add(CreateMessageHeaderDescription(memberType, + memberInfo, + new XmlName(memberInfo.Name), + defaultNS, + i, + -1)); + } + else if (memberInfo.IsDefined(typeof(MessagePropertyAttribute), false)) + { + messageDescription.Properties.Add(CreateMessagePropertyDescription(memberInfo, + new XmlName(memberInfo.Name), + i)); + } + else + { + bodyPartDescriptionList.Add(CreateMessagePartDescription(memberType, + memberInfo, + new XmlName(memberInfo.Name), + defaultNS, + i, + -1)); + } + } + + if (returnAttrProvider != null) + { + messageDescription.Body.ReturnValue = CreateMessagePartDescription(typeof(void), + returnAttrProvider, + returnValueName, + defaultNS, + 0, + 0); + } + + AddSortedParts(bodyPartDescriptionList, messageDescription.Body.Parts); + AddSortedParts(headerPartDescriptionList, messageDescription.Headers); + messages.Add(typedMessageType, messageDescription.Items); + + return messageDescription; + } + + static bool IsMethodOverriding(MethodInfo method) + { + return method.IsVirtual && ((method.Attributes & MethodAttributes.NewSlot) == 0); + } + + + + + MessagePartDescription CreateMessagePartDescription(Type bodyType, + CustomAttributeProvider attrProvider, + XmlName defaultName, + string defaultNS, + int parameterIndex, + int serializationIndex) + { + MessagePartDescription partDescription = null; + MessageBodyMemberAttribute bodyAttr = ServiceReflector.GetSingleAttribute(attrProvider, messageContractMemberAttributes); + + if (bodyAttr == null) + { + partDescription = new MessagePartDescription(defaultName.EncodedName, defaultNS); + partDescription.SerializationPosition = serializationIndex; + } + else + { + XmlName partName = bodyAttr.IsNameSetExplicit ? new XmlName(bodyAttr.Name) : defaultName; + string partNs = bodyAttr.IsNamespaceSetExplicit ? bodyAttr.Namespace : defaultNS; + partDescription = new MessagePartDescription(partName.EncodedName, partNs); + partDescription.SerializationPosition = bodyAttr.Order < 0 ? serializationIndex : bodyAttr.Order; + } + + if (attrProvider.MemberInfo != null) + { + partDescription.MemberInfo = attrProvider.MemberInfo; + } + partDescription.Type = bodyType; + partDescription.Index = parameterIndex; + return partDescription; + } + + MessageHeaderDescription CreateMessageHeaderDescription(Type headerParameterType, + CustomAttributeProvider attrProvider, + XmlName defaultName, + string defaultNS, + int parameterIndex, + int serializationPosition) + { + MessageHeaderDescription headerDescription = null; + MessageHeaderAttribute headerAttr = ServiceReflector.GetRequiredSingleAttribute(attrProvider, messageContractMemberAttributes); + XmlName headerName = headerAttr.IsNameSetExplicit ? new XmlName(headerAttr.Name) : defaultName; + string headerNs = headerAttr.IsNamespaceSetExplicit ? headerAttr.Namespace : defaultNS; + headerDescription = new MessageHeaderDescription(headerName.EncodedName, headerNs); + headerDescription.UniquePartName = defaultName.EncodedName; + + if (headerAttr is MessageHeaderArrayAttribute) + { + if (!headerParameterType.IsArray || headerParameterType.GetArrayRank() != 1) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxInvalidMessageHeaderArrayType, defaultName))); + } + headerDescription.Multiple = true; + headerParameterType = headerParameterType.GetElementType(); + } + headerDescription.Type = TypedHeaderManager.GetHeaderType(headerParameterType); + headerDescription.TypedHeader = (headerParameterType != headerDescription.Type); + if (headerDescription.TypedHeader) + { + if (headerAttr.IsMustUnderstandSet || headerAttr.IsRelaySet || headerAttr.Actor != null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxStaticMessageHeaderPropertiesNotAllowed, defaultName))); + } + } + else + { + headerDescription.Actor = headerAttr.Actor; + headerDescription.MustUnderstand = headerAttr.MustUnderstand; + headerDescription.Relay = headerAttr.Relay; + } + headerDescription.SerializationPosition = serializationPosition; + if (attrProvider.MemberInfo != null) + { + headerDescription.MemberInfo = attrProvider.MemberInfo; + } + + headerDescription.Index = parameterIndex; + return headerDescription; + } + + MessagePropertyDescription CreateMessagePropertyDescription(CustomAttributeProvider attrProvider, + XmlName defaultName, + int parameterIndex) + { + MessagePropertyAttribute attr = ServiceReflector.GetSingleAttribute(attrProvider, messageContractMemberAttributes); + XmlName propertyName = attr.IsNameSetExplicit ? new XmlName(attr.Name) : defaultName; + MessagePropertyDescription propertyDescription = new MessagePropertyDescription(propertyName.EncodedName); + propertyDescription.Index = parameterIndex; + + if (attrProvider.MemberInfo != null) + { + propertyDescription.MemberInfo = attrProvider.MemberInfo; + } + + return propertyDescription; + } + + internal static XmlName GetReturnValueName(XmlName methodName) + { + return new XmlName(methodName.EncodedName + ReturnSuffix, true); + } + + internal static XmlName GetReturnValueName(string methodName) + { + return new XmlName(methodName + ReturnSuffix); + } + + internal static XmlName GetWrapperName(string wrapperName, XmlName defaultName) + { + if (string.IsNullOrEmpty(wrapperName)) + return defaultName; + return new XmlName(wrapperName); + } + + void AddSortedParts(List partDescriptionList, KeyedCollection partDescriptionCollection) + where T : MessagePartDescription + { + MessagePartDescription[] partDescriptions = partDescriptionList.ToArray(); + if (partDescriptions.Length > 1) + { + Array.Sort(partDescriptions, CompareMessagePartDescriptions); + } + foreach (T partDescription in partDescriptions) + { + if (partDescriptionCollection.Contains(new XmlQualifiedName(partDescription.Name, partDescription.Namespace))) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidMessageContractException(SR.Format(SR.SFxDuplicateMessageParts, partDescription.Name, partDescription.Namespace))); + } + partDescriptionCollection.Add(partDescription); + } + } + + private abstract class OperationConsistencyVerifier + { + public virtual void VerifyParameterLength() { } + public virtual void VerifyParameterType() { } + public virtual void VerifyOutParameterType() { } + public virtual void VerifyReturnType() { } + public virtual void VerifyFaultContractAttribute() { } + public virtual void VerifyKnownTypeAttribute() { } + public virtual void VerifyIsOneWayStatus() { } + public virtual void VerifyActionAndReplyAction() { } + } + + private class SyncAsyncOperationConsistencyVerifier : OperationConsistencyVerifier + { + OperationDescription syncOperation; + OperationDescription asyncOperation; + ParameterInfo[] syncInputs; + ParameterInfo[] asyncInputs; + ParameterInfo[] syncOutputs; + ParameterInfo[] asyncOutputs; + + public SyncAsyncOperationConsistencyVerifier(OperationDescription syncOperation, OperationDescription asyncOperation) + { + this.syncOperation = syncOperation; + this.asyncOperation = asyncOperation; + syncInputs = ServiceReflector.GetInputParameters(this.syncOperation.SyncMethod, false); + asyncInputs = ServiceReflector.GetInputParameters(this.asyncOperation.BeginMethod, true); + syncOutputs = ServiceReflector.GetOutputParameters(this.syncOperation.SyncMethod, false); + asyncOutputs = ServiceReflector.GetOutputParameters(this.asyncOperation.EndMethod, true); + } + + public override void VerifyParameterLength() + { + if (syncInputs.Length != asyncInputs.Length || syncOutputs.Length != asyncOutputs.Length) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new InvalidOperationException(SR.Format(SR.SyncAsyncMatchConsistency_Parameters5, + syncOperation.SyncMethod.Name, + syncOperation.SyncMethod.DeclaringType, + asyncOperation.BeginMethod.Name, + asyncOperation.EndMethod.Name, + syncOperation.Name))); + } + } + + public override void VerifyParameterType() + { + for (int i = 0; i < syncInputs.Length; i++) + { + if (syncInputs[i].ParameterType != asyncInputs[i].ParameterType) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new InvalidOperationException(SR.Format(SR.SyncAsyncMatchConsistency_Parameters5, + syncOperation.SyncMethod.Name, + syncOperation.SyncMethod.DeclaringType, + asyncOperation.BeginMethod.Name, + asyncOperation.EndMethod.Name, + syncOperation.Name))); + } + } + } + + public override void VerifyOutParameterType() + { + for (int i = 0; i < syncOutputs.Length; i++) + { + if (syncOutputs[i].ParameterType != asyncOutputs[i].ParameterType) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new InvalidOperationException(SR.Format(SR.SyncAsyncMatchConsistency_Parameters5, + syncOperation.SyncMethod.Name, + syncOperation.SyncMethod.DeclaringType, + asyncOperation.BeginMethod.Name, + asyncOperation.EndMethod.Name, + syncOperation.Name))); + } + } + } + + public override void VerifyReturnType() + { + if (syncOperation.SyncMethod.ReturnType != syncOperation.EndMethod.ReturnType) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new InvalidOperationException(SR.Format(SR.SyncAsyncMatchConsistency_ReturnType5, + syncOperation.SyncMethod.Name, + syncOperation.SyncMethod.DeclaringType, + asyncOperation.BeginMethod.Name, + asyncOperation.EndMethod.Name, + syncOperation.Name))); + } + } + + public override void VerifyFaultContractAttribute() + { + if (asyncOperation.Faults.Count != 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new InvalidOperationException(SR.Format(SR.SyncAsyncMatchConsistency_Attributes6, + syncOperation.SyncMethod.Name, + syncOperation.SyncMethod.DeclaringType, + asyncOperation.BeginMethod.Name, + asyncOperation.EndMethod.Name, + syncOperation.Name, + typeof(FaultContractAttribute).Name))); + + } + } + + public override void VerifyKnownTypeAttribute() + { + if (asyncOperation.KnownTypes.Count != 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new InvalidOperationException(SR.Format(SR.SyncAsyncMatchConsistency_Attributes6, + syncOperation.SyncMethod.Name, + syncOperation.SyncMethod.DeclaringType, + asyncOperation.BeginMethod.Name, + asyncOperation.EndMethod.Name, + syncOperation.Name, + typeof(ServiceKnownTypeAttribute).Name))); + } + } + + public override void VerifyIsOneWayStatus() + { + if (syncOperation.Messages.Count != asyncOperation.Messages.Count) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new InvalidOperationException(SR.Format(SR.SyncAsyncMatchConsistency_Property6, + syncOperation.SyncMethod.Name, + syncOperation.SyncMethod.DeclaringType, + asyncOperation.BeginMethod.Name, + asyncOperation.EndMethod.Name, + syncOperation.Name, + "IsOneWay"))); + } + } + + public override void VerifyActionAndReplyAction() + { + for (int index = 0; index < syncOperation.Messages.Count; ++index) + { + if (syncOperation.Messages[index].Action != asyncOperation.Messages[index].Action) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new InvalidOperationException(SR.Format(SR.SyncAsyncMatchConsistency_Property6, + syncOperation.SyncMethod.Name, + syncOperation.SyncMethod.DeclaringType, + asyncOperation.BeginMethod.Name, + asyncOperation.EndMethod.Name, + syncOperation.Name, + index == 0 ? "Action" : "ReplyAction"))); + } + } + } + } + + private class SyncTaskOperationConsistencyVerifier : OperationConsistencyVerifier + { + OperationDescription syncOperation; + OperationDescription taskOperation; + ParameterInfo[] syncInputs; + ParameterInfo[] taskInputs; + + public SyncTaskOperationConsistencyVerifier(OperationDescription syncOperation, OperationDescription taskOperation) + { + this.syncOperation = syncOperation; + this.taskOperation = taskOperation; + syncInputs = ServiceReflector.GetInputParameters(this.syncOperation.SyncMethod, false); + taskInputs = ServiceReflector.GetInputParameters(this.taskOperation.TaskMethod, false); + } + + public override void VerifyParameterLength() + { + if (syncInputs.Length != taskInputs.Length) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new InvalidOperationException(SR.Format(SR.SyncTaskMatchConsistency_Parameters5, + syncOperation.SyncMethod.Name, + syncOperation.SyncMethod.DeclaringType, + taskOperation.TaskMethod.Name, + syncOperation.Name))); + } + } + + public override void VerifyParameterType() + { + for (int i = 0; i < syncInputs.Length; i++) + { + if (syncInputs[i].ParameterType != taskInputs[i].ParameterType) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new InvalidOperationException(SR.Format(SR.SyncTaskMatchConsistency_Parameters5, + syncOperation.SyncMethod.Name, + syncOperation.SyncMethod.DeclaringType, + taskOperation.TaskMethod.Name, + syncOperation.Name))); + } + } + } + + public override void VerifyReturnType() + { + if (syncOperation.SyncMethod.ReturnType != syncOperation.TaskTResult) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new InvalidOperationException(SR.Format(SR.SyncTaskMatchConsistency_ReturnType5, + syncOperation.SyncMethod.Name, + syncOperation.SyncMethod.DeclaringType, + taskOperation.TaskMethod.Name, + syncOperation.Name))); + } + } + + public override void VerifyFaultContractAttribute() + { + if (taskOperation.Faults.Count != 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new InvalidOperationException(SR.Format(SR.SyncTaskMatchConsistency_Attributes6, + syncOperation.SyncMethod.Name, + syncOperation.SyncMethod.DeclaringType, + taskOperation.TaskMethod.Name, + syncOperation.Name, + typeof(FaultContractAttribute).Name))); + + } + } + + public override void VerifyKnownTypeAttribute() + { + if (taskOperation.KnownTypes.Count != 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new InvalidOperationException(SR.Format(SR.SyncTaskMatchConsistency_Attributes6, + syncOperation.SyncMethod.Name, + syncOperation.SyncMethod.DeclaringType, + taskOperation.TaskMethod.Name, + syncOperation.Name, + typeof(ServiceKnownTypeAttribute).Name))); + } + } + + public override void VerifyIsOneWayStatus() + { + if (syncOperation.Messages.Count != taskOperation.Messages.Count) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new InvalidOperationException(SR.Format(SR.SyncTaskMatchConsistency_Property6, + syncOperation.SyncMethod.Name, + syncOperation.SyncMethod.DeclaringType, + taskOperation.TaskMethod.Name, + syncOperation.Name, + "IsOneWay"))); + } + } + + public override void VerifyActionAndReplyAction() + { + for (int index = 0; index < syncOperation.Messages.Count; ++index) + { + if (syncOperation.Messages[index].Action != taskOperation.Messages[index].Action) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new InvalidOperationException(SR.Format(SR.SyncTaskMatchConsistency_Property6, + syncOperation.SyncMethod.Name, + syncOperation.SyncMethod.DeclaringType, + taskOperation.TaskMethod.Name, + syncOperation.Name, + index == 0 ? "Action" : "ReplyAction"))); + } + } + } + } + + private class TaskAsyncOperationConsistencyVerifier : OperationConsistencyVerifier + { + OperationDescription taskOperation; + OperationDescription asyncOperation; + ParameterInfo[] taskInputs; + ParameterInfo[] asyncInputs; + + public TaskAsyncOperationConsistencyVerifier(OperationDescription taskOperation, OperationDescription asyncOperation) + { + this.taskOperation = taskOperation; + this.asyncOperation = asyncOperation; + taskInputs = ServiceReflector.GetInputParameters(this.taskOperation.TaskMethod, false); + asyncInputs = ServiceReflector.GetInputParameters(this.asyncOperation.BeginMethod, true); + } + + public override void VerifyParameterLength() + { + if (taskInputs.Length != asyncInputs.Length) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new InvalidOperationException(SR.Format(SR.TaskAsyncMatchConsistency_Parameters5, + taskOperation.TaskMethod.Name, + taskOperation.TaskMethod.DeclaringType, + asyncOperation.BeginMethod.Name, + asyncOperation.EndMethod.Name, + taskOperation.Name))); + } + } + + public override void VerifyParameterType() + { + for (int i = 0; i < taskInputs.Length; i++) + { + if (taskInputs[i].ParameterType != asyncInputs[i].ParameterType) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new InvalidOperationException(SR.Format(SR.TaskAsyncMatchConsistency_Parameters5, + taskOperation.TaskMethod.Name, + taskOperation.TaskMethod.DeclaringType, + asyncOperation.BeginMethod.Name, + asyncOperation.EndMethod.Name, + taskOperation.Name))); + } + } + } + + public override void VerifyReturnType() + { + if (taskOperation.TaskTResult != asyncOperation.EndMethod.ReturnType) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new InvalidOperationException(SR.Format(SR.TaskAsyncMatchConsistency_ReturnType5, + taskOperation.TaskMethod.Name, + taskOperation.TaskMethod.DeclaringType, + asyncOperation.BeginMethod.Name, + asyncOperation.EndMethod.Name, + taskOperation.Name))); + } + } + + public override void VerifyFaultContractAttribute() + { + if (asyncOperation.Faults.Count != 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new InvalidOperationException(SR.Format(SR.TaskAsyncMatchConsistency_Attributes6, + taskOperation.TaskMethod.Name, + taskOperation.TaskMethod.DeclaringType, + asyncOperation.BeginMethod.Name, + asyncOperation.EndMethod.Name, + taskOperation.Name, + typeof(FaultContractAttribute).Name))); + + } + } + + public override void VerifyKnownTypeAttribute() + { + if (asyncOperation.KnownTypes.Count != 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new InvalidOperationException(SR.Format(SR.TaskAsyncMatchConsistency_Attributes6, + taskOperation.TaskMethod.Name, + taskOperation.TaskMethod.DeclaringType, + asyncOperation.BeginMethod.Name, + asyncOperation.EndMethod.Name, + taskOperation.Name, + typeof(ServiceKnownTypeAttribute).Name))); + } + } + + public override void VerifyIsOneWayStatus() + { + if (taskOperation.Messages.Count != asyncOperation.Messages.Count) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new InvalidOperationException(SR.Format(SR.TaskAsyncMatchConsistency_Property6, + taskOperation.TaskMethod.Name, + taskOperation.TaskMethod.DeclaringType, + asyncOperation.BeginMethod.Name, + asyncOperation.EndMethod.Name, + taskOperation.Name, + "IsOneWay"))); + } + } + + public override void VerifyActionAndReplyAction() + { + for (int index = 0; index < taskOperation.Messages.Count; ++index) + { + if (taskOperation.Messages[index].Action != asyncOperation.Messages[index].Action) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new InvalidOperationException(SR.Format(SR.TaskAsyncMatchConsistency_Property6, + taskOperation.TaskMethod.Name, + taskOperation.TaskMethod.DeclaringType, + asyncOperation.BeginMethod.Name, + asyncOperation.EndMethod.Name, + taskOperation.Name, + index == 0 ? "Action" : "ReplyAction"))); + } + } + } + } + + class ContractReflectionInfo + { + internal Type iface; + internal Type callbackiface; + } + + // This function factors out the logic of how programming model attributes interact with service inheritance. + // + // To use this, just call ApplyServiceInheritance() with + // - the service type you want to pull behavior attributes from + // - the "destination" behavior collection, where all the right behavior attributes should be added to + // - a delegate + // The delegate is just a function you write that behaves like this: + // imagine that "currentType" was the only type (imagine there was no inheritance hierarchy) + // find desired behavior attributes on this type, and add them to "behaviors" + // ApplyServiceInheritance then uses the logic you provide for getting behavior attributes from a single type, + // and it walks the actual type hierarchy and does the inheritance/override logic for you. + public static void ApplyServiceInheritance( + TBehaviorCollection descriptionBehaviors, + ServiceInheritanceCallback callback) + where IBehavior : class + where TBehaviorCollection : KeyedByTypeCollection + { + // work our way up the class hierarchy, looking for attributes; adding "bottom up" so that for each + // type of attribute, we only pick up the bottom-most one (the one attached to most-derived class) + for (Type currentType = typeof(TService); currentType != null; currentType = currentType.BaseType) + { + AddBehaviorsAtOneScope(currentType, descriptionBehaviors, callback); + } + } + + public delegate void ServiceInheritanceCallback(Type currentType, KeyedByTypeCollection behaviors); + + // To use this, just call AddBehaviorsAtOneScope() with + // - the type you want to pull behavior attributes from + // - the "destination" behavior collection, where all the right behavior attributes should be added to + // - a delegate + // The delegate is just a function you write that behaves like this: + // imagine that "currentType" was the only type (imagine there was no inheritance hierarchy) + // find desired behavior attributes on this type, and add them to "behaviors" + // AddBehaviorsAtOneScope then uses the logic you provide for getting behavior attributes from a single type, + // and it does the override logic for you (only add the behavior if it wasn't already in the descriptionBehaviors) + static void AddBehaviorsAtOneScope( + Type type, + TBehaviorCollection descriptionBehaviors, + ServiceInheritanceCallback callback) + where IBehavior : class + where TBehaviorCollection : KeyedByTypeCollection + { + KeyedByTypeCollection toAdd = new KeyedByTypeCollection(); + callback(type, toAdd); + // toAdd now contains the set of behaviors we'd add if this type (scope) were the only source of behaviors + + for (int i = 0; i < toAdd.Count; i++) + { + IBehavior behavior = toAdd[i]; + if (!descriptionBehaviors.Contains(behavior.GetType())) + { + // if we didn't already see this type of attribute at a previous scope + // then it belongs in the final result + if (behavior is ServiceBehaviorAttribute || behavior is CallbackBehaviorAttribute) + { + descriptionBehaviors.Insert(0, behavior); + } + else + { + descriptionBehaviors.Add(behavior); + } + } + } + } + } + + internal class TypeLoader + { + internal const BindingFlags DefaultBindingFlags = BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public; + static Type[] s_formatterAttributes = { + typeof(XmlSerializerFormatAttribute), + typeof(DataContractFormatAttribute) + }; + + internal const string ResponseSuffix = "Response"; + + internal static Attribute GetFormattingAttribute(CustomAttributeProvider attrProvider, Attribute defaultFormatAttribute) + { + if (attrProvider != null) + { + if (attrProvider.IsDefined(typeof(XmlSerializerFormatAttribute), false)) + { + return ServiceReflector.GetSingleAttribute(attrProvider, s_formatterAttributes); + } + if (attrProvider.IsDefined(typeof(DataContractFormatAttribute), false)) + { + return ServiceReflector.GetSingleAttribute(attrProvider, s_formatterAttributes); + } + } + return defaultFormatAttribute; + } + + internal static Type GetParameterType(ParameterInfo parameterInfo) + { + Type parameterType = parameterInfo.ParameterType; + if (parameterType.IsByRef) + { + return parameterType.GetElementType(); + } + else + { + return parameterType; + } + } + + internal static XmlName GetBodyWrapperResponseName(string operationName) + { +#if DEBUG + Fx.Assert(NamingHelper.IsValidNCName(operationName), "operationName value has to be a valid NCName."); +#endif + return new XmlName(operationName + ResponseSuffix); + } + + + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Description/XmlSerializerOperationBehavior.cs b/src/CoreWCF.Primitives/src/CoreWCF/Description/XmlSerializerOperationBehavior.cs new file mode 100644 index 000000000..49432cc18 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Description/XmlSerializerOperationBehavior.cs @@ -0,0 +1,1049 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Reflection; +using System.Security; +using System.Xml; +using System.Xml.Serialization; +using CoreWCF.Collections.Generic; +using CoreWCF.Runtime; +using CoreWCF.Runtime.Serialization; +using CoreWCF.Channels; +using CoreWCF.Dispatcher; + +namespace CoreWCF.Description +{ + internal class XmlSerializerOperationBehavior : IOperationBehavior + { + readonly Reflector.OperationReflector reflector; + readonly bool builtInOperationBehavior; + + public XmlSerializerOperationBehavior(OperationDescription operation) + : this(operation, null) + { + } + + public XmlSerializerOperationBehavior(OperationDescription operation, XmlSerializerFormatAttribute attribute) + { + if (operation == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("operation"); + Reflector parentReflector = new Reflector(operation.DeclaringContract.Namespace, operation.DeclaringContract.ContractType); + reflector = parentReflector.ReflectOperation(operation, attribute ?? new XmlSerializerFormatAttribute()); + } + + internal XmlSerializerOperationBehavior(OperationDescription operation, XmlSerializerFormatAttribute attribute, Reflector parentReflector) + : this(operation, attribute) + { + // used by System.ServiceModel.Web + reflector = parentReflector.ReflectOperation(operation, attribute ?? new XmlSerializerFormatAttribute()); + } + + XmlSerializerOperationBehavior(Reflector.OperationReflector reflector, bool builtInOperationBehavior) + { + Fx.Assert(reflector != null, ""); + this.reflector = reflector; + this.builtInOperationBehavior = builtInOperationBehavior; + } + + internal Reflector.OperationReflector OperationReflector + { + get { return reflector; } + } + + internal bool IsBuiltInOperationBehavior + { + get { return builtInOperationBehavior; } + } + + public XmlSerializerFormatAttribute XmlSerializerFormatAttribute + { + get + { + return reflector.Attribute; + } + } + + internal static XmlSerializerOperationFormatter CreateOperationFormatter(OperationDescription operation) + { + return new XmlSerializerOperationBehavior(operation).CreateFormatter(); + } + + internal static XmlSerializerOperationFormatter CreateOperationFormatter(OperationDescription operation, XmlSerializerFormatAttribute attr) + { + return new XmlSerializerOperationBehavior(operation, attr).CreateFormatter(); + } + + internal static void AddBehaviors(ContractDescription contract) + { + AddBehaviors(contract, false); + } + + internal static void AddBuiltInBehaviors(ContractDescription contract) + { + AddBehaviors(contract, true); + } + + static void AddBehaviors(ContractDescription contract, bool builtInOperationBehavior) + { + Reflector reflector = new Reflector(contract.Namespace, contract.ContractType); + + foreach (OperationDescription operation in contract.Operations) + { + + Reflector.OperationReflector operationReflector = reflector.ReflectOperation(operation); + if (operationReflector != null) + { + bool isInherited = operation.DeclaringContract != contract; + if (!isInherited) + { + operation.Behaviors.Add(new XmlSerializerOperationBehavior(operationReflector, builtInOperationBehavior)); + //operation.Behaviors.Add(new XmlSerializerOperationGenerator(new XmlSerializerImportOptions())); + } + } + } + } + + internal XmlSerializerOperationFormatter CreateFormatter() + { + return new XmlSerializerOperationFormatter(reflector.Operation, reflector.Attribute, reflector.Request, reflector.Reply); + } + + XmlSerializerFaultFormatter CreateFaultFormatter(SynchronizedCollection faultContractInfos) + { + return new XmlSerializerFaultFormatter(faultContractInfos, reflector.XmlSerializerFaultContractInfos); + } + + void IOperationBehavior.Validate(OperationDescription description) + { + } + + void IOperationBehavior.AddBindingParameters(OperationDescription description, BindingParameterCollection parameters) + { + } + + void IOperationBehavior.ApplyDispatchBehavior(OperationDescription description, DispatchOperation dispatch) + { + if (description == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("description"); + + if (dispatch == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("dispatch"); + + if (dispatch.Formatter == null) + { + dispatch.Formatter = (IDispatchMessageFormatter)CreateFormatter(); + dispatch.DeserializeRequest = reflector.RequestRequiresSerialization; + dispatch.SerializeReply = reflector.ReplyRequiresSerialization; + } + + if (reflector.Attribute.SupportFaults) + { + if (!dispatch.IsFaultFormatterSetExplicit) + { + dispatch.FaultFormatter = (IDispatchFaultFormatter)CreateFaultFormatter(dispatch.FaultContractInfos); + } + else + { + var wrapper = dispatch.FaultFormatter as IDispatchFaultFormatterWrapper; + if (wrapper != null) + { + wrapper.InnerFaultFormatter = (IDispatchFaultFormatter)CreateFaultFormatter(dispatch.FaultContractInfos); + } + } + } + } + + void IOperationBehavior.ApplyClientBehavior(OperationDescription description, ClientOperation proxy) + { + if (description == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("description"); + + if (proxy == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("proxy"); + + if (proxy.Formatter == null) + { + proxy.Formatter = (IClientMessageFormatter)CreateFormatter(); + proxy.SerializeRequest = reflector.RequestRequiresSerialization; + proxy.DeserializeReply = reflector.ReplyRequiresSerialization; + } + + if (reflector.Attribute.SupportFaults && !proxy.IsFaultFormatterSetExplicit) + proxy.FaultFormatter = (IClientFaultFormatter)CreateFaultFormatter(proxy.FaultContractInfos); + } + + public Collection GetXmlMappings() + { + Collection mappings = new Collection(); + if (OperationReflector.Request != null && OperationReflector.Request.HeadersMapping != null) + mappings.Add(OperationReflector.Request.HeadersMapping); + if (OperationReflector.Request != null && OperationReflector.Request.BodyMapping != null) + mappings.Add(OperationReflector.Request.BodyMapping); + if (OperationReflector.Reply != null && OperationReflector.Reply.HeadersMapping != null) + mappings.Add(OperationReflector.Reply.HeadersMapping); + if (OperationReflector.Reply != null && OperationReflector.Reply.BodyMapping != null) + mappings.Add(OperationReflector.Reply.BodyMapping); + return mappings; + } + + // helper for reflecting operations + internal class Reflector + { + readonly XmlSerializerImporter importer; + readonly SerializerGenerationContext generation; + Collection operationReflectors = new Collection(); + object thisLock = new object(); + + internal Reflector(string defaultNs, Type type) + { + importer = new XmlSerializerImporter(defaultNs); + generation = new SerializerGenerationContext(type); + } + + internal void EnsureMessageInfos() + { + lock (thisLock) + { + foreach (OperationReflector operationReflector in operationReflectors) + { + operationReflector.EnsureMessageInfos(); + } + } + } + + + static XmlSerializerFormatAttribute FindAttribute(OperationDescription operation) + { + Type contractType = operation.DeclaringContract != null ? operation.DeclaringContract.ContractType : null; + XmlSerializerFormatAttribute contractFormatAttribute = contractType != null ? TypeLoader.GetFormattingAttribute(contractType.GetTypeInfo(), null) as XmlSerializerFormatAttribute : null; + return TypeLoader.GetFormattingAttribute(operation.OperationMethod, contractFormatAttribute) as XmlSerializerFormatAttribute; + } + + // auto-reflects the operation, returning null if no attribute was found or inherited + internal OperationReflector ReflectOperation(OperationDescription operation) + { + XmlSerializerFormatAttribute attr = FindAttribute(operation); + if (attr == null) + return null; + + return ReflectOperation(operation, attr); + } + + // overrides the auto-reflection with an attribute + internal OperationReflector ReflectOperation(OperationDescription operation, XmlSerializerFormatAttribute attrOverride) + { + OperationReflector operationReflector = new OperationReflector(this, operation, attrOverride, true/*reflectOnDemand*/); + operationReflectors.Add(operationReflector); + + return operationReflector; + } + + internal class OperationReflector + { + readonly Reflector parent; + + internal readonly OperationDescription Operation; + internal readonly XmlSerializerFormatAttribute Attribute; + + internal readonly bool IsEncoded; + internal readonly bool IsRpc; + internal readonly bool IsOneWay; + internal readonly bool RequestRequiresSerialization; + internal readonly bool ReplyRequiresSerialization; + + readonly string keyBase; + + MessageInfo request; + MessageInfo reply; + SynchronizedCollection xmlSerializerFaultContractInfos; + + internal OperationReflector(Reflector parent, OperationDescription operation, XmlSerializerFormatAttribute attr, bool reflectOnDemand) + { + Fx.Assert(parent != null, ""); + Fx.Assert(operation != null, ""); + Fx.Assert(attr != null, ""); + + OperationFormatter.Validate(operation, attr.Style == OperationFormatStyle.Rpc, attr.IsEncoded); + + this.parent = parent; + + Operation = operation; + Attribute = attr; + + IsEncoded = attr.IsEncoded; + IsRpc = (attr.Style == OperationFormatStyle.Rpc); + IsOneWay = operation.Messages.Count == 1; + + RequestRequiresSerialization = !operation.Messages[0].IsUntypedMessage; + ReplyRequiresSerialization = !IsOneWay && !operation.Messages[1].IsUntypedMessage; + + MethodInfo methodInfo = operation.OperationMethod; + if (methodInfo == null) + { + // keyBase needs to be unique within the scope of the parent reflector + keyBase = string.Empty; + if (operation.DeclaringContract != null) + { + keyBase = operation.DeclaringContract.Name + "," + operation.DeclaringContract.Namespace + ":"; + } + keyBase = keyBase + operation.Name; + } + else + keyBase = methodInfo.DeclaringType.FullName + ":" + methodInfo.ToString(); + + foreach (MessageDescription message in operation.Messages) + foreach (MessageHeaderDescription header in message.Headers) + SetUnknownHeaderInDescription(header); + if (!reflectOnDemand) + { + EnsureMessageInfos(); + } + } + + private void SetUnknownHeaderInDescription(MessageHeaderDescription header) + { + if (IsEncoded) //XmlAnyElementAttribute does not apply + return; + if (header.AdditionalAttributesProvider != null) + { + object[] attrs = header.AdditionalAttributesProvider.GetCustomAttributes(false).ToArray(); + bool isUnknown = false; + bool xmlIgnore = false; + + foreach (var attr in attrs) + { + if (attr is XmlAnyElementAttribute) + { + if (string.IsNullOrEmpty(((XmlAnyElementAttribute)attr).Name)) + { + isUnknown = true; + } + } + // In the original full framework code using XmlAttributes, the XmlAnyElements collection is cleared + // if the XmlIgnore attribute is present. + if (attr is XmlIgnoreAttribute) + { + xmlIgnore = true; + break; // XmlIgnore overrides all so no need to continue if found. + } + } + + header.IsUnknownHeaderCollection = isUnknown && !xmlIgnore; + } + } + + string ContractName + { + get { return Operation.DeclaringContract.Name; } + } + + string ContractNamespace + { + get { return Operation.DeclaringContract.Namespace; } + } + + internal MessageInfo Request + { + get + { + parent.EnsureMessageInfos(); + return request; + } + } + + internal MessageInfo Reply + { + get + { + parent.EnsureMessageInfos(); + return reply; + } + } + + internal SynchronizedCollection XmlSerializerFaultContractInfos + { + get + { + parent.EnsureMessageInfos(); + return xmlSerializerFaultContractInfos; + } + } + + internal void EnsureMessageInfos() + { + if (request == null) + { + foreach (Type knownType in Operation.KnownTypes) + { + if (knownType == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxKnownTypeNull, Operation.Name))); + parent.importer.IncludeType(knownType, IsEncoded); + } + request = CreateMessageInfo(Operation.Messages[0], ":Request"); + if (request != null && IsRpc && Operation.IsValidateRpcWrapperName && request.BodyMapping.XsdElementName != Operation.Name) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxRpcMessageBodyPartNameInvalid, Operation.Name, Operation.Messages[0].MessageName, request.BodyMapping.XsdElementName, Operation.Name))); + if (!IsOneWay) + { + reply = CreateMessageInfo(Operation.Messages[1], ":Response"); + XmlName responseName = TypeLoader.GetBodyWrapperResponseName(Operation.Name); + if (reply != null && IsRpc && Operation.IsValidateRpcWrapperName && reply.BodyMapping.XsdElementName != responseName.EncodedName) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxRpcMessageBodyPartNameInvalid, Operation.Name, Operation.Messages[1].MessageName, reply.BodyMapping.XsdElementName, responseName.EncodedName))); + } + if (Attribute.SupportFaults) + { + GenerateXmlSerializerFaultContractInfos(); + } + } + } + + void GenerateXmlSerializerFaultContractInfos() + { + SynchronizedCollection faultInfos = new SynchronizedCollection(); + for (int i = 0; i < Operation.Faults.Count; i++) + { + FaultDescription fault = Operation.Faults[i]; + FaultContractInfo faultContractInfo = new FaultContractInfo(fault.Action, fault.DetailType, fault.ElementName, fault.Namespace, Operation.KnownTypes); + + XmlQualifiedName elementName; + XmlMembersMapping xmlMembersMapping = ImportFaultElement(fault, out elementName); + + SerializerStub serializerStub = parent.generation.AddSerializer(xmlMembersMapping); + faultInfos.Add(new XmlSerializerFaultContractInfo(faultContractInfo, serializerStub, elementName)); + } + xmlSerializerFaultContractInfos = faultInfos; + } + + MessageInfo CreateMessageInfo(MessageDescription message, string key) + { + if (message.IsUntypedMessage) + return null; + MessageInfo info = new MessageInfo(); + if (message.IsTypedMessage) + key = message.MessageType.FullName + ":" + IsEncoded + ":" + IsRpc; + XmlMembersMapping headersMapping = LoadHeadersMapping(message, key + ":Headers"); + info.SetHeaders(parent.generation.AddSerializer(headersMapping)); + MessagePartDescriptionCollection rpcEncodedTypedMessgeBodyParts; + info.SetBody(parent.generation.AddSerializer(LoadBodyMapping(message, key, out rpcEncodedTypedMessgeBodyParts)), rpcEncodedTypedMessgeBodyParts); + CreateHeaderDescriptionTable(message, info, headersMapping); + return info; + } + + private void CreateHeaderDescriptionTable(MessageDescription message, MessageInfo info, XmlMembersMapping headersMapping) + { + int headerNameIndex = 0; + OperationFormatter.MessageHeaderDescriptionTable headerDescriptionTable = new OperationFormatter.MessageHeaderDescriptionTable(); + info.SetHeaderDescriptionTable(headerDescriptionTable); + foreach (MessageHeaderDescription header in message.Headers) + { + if (header.IsUnknownHeaderCollection) + info.SetUnknownHeaderDescription(header); + else if (headersMapping != null) + { + XmlMemberMapping memberMapping = headersMapping[headerNameIndex++]; + string headerName, headerNs; + if (IsEncoded) + { + headerName = memberMapping.TypeName; + headerNs = memberMapping.TypeNamespace; + } + else + { + headerName = memberMapping.XsdElementName; + headerNs = memberMapping.Namespace; + } + if (headerName != header.Name) + { + if (message.MessageType != null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxHeaderNameMismatchInMessageContract, message.MessageType, header.MemberInfo.Name, header.Name, headerName))); + else + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxHeaderNameMismatchInOperation, Operation.Name, Operation.DeclaringContract.Name, Operation.DeclaringContract.Namespace, header.Name, headerName))); + } + if (headerNs != header.Namespace) + { + if (message.MessageType != null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxHeaderNamespaceMismatchInMessageContract, message.MessageType, header.MemberInfo.Name, header.Namespace, headerNs))); + else + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxHeaderNamespaceMismatchInOperation, Operation.Name, Operation.DeclaringContract.Name, Operation.DeclaringContract.Namespace, header.Namespace, headerNs))); + } + + headerDescriptionTable.Add(headerName, headerNs, header); + } + } + } + + XmlMembersMapping LoadBodyMapping(MessageDescription message, string mappingKey, out MessagePartDescriptionCollection rpcEncodedTypedMessageBodyParts) + { + MessagePartDescription returnPart; + string wrapperName, wrapperNs; + MessagePartDescriptionCollection bodyParts; + if (IsEncoded && message.IsTypedMessage && message.Body.WrapperName == null) + { + MessagePartDescription wrapperPart = GetWrapperPart(message); + returnPart = null; + rpcEncodedTypedMessageBodyParts = bodyParts = GetWrappedParts(wrapperPart); + wrapperName = wrapperPart.Name; + wrapperNs = wrapperPart.Namespace; + } + else + { + rpcEncodedTypedMessageBodyParts = null; + returnPart = OperationFormatter.IsValidReturnValue(message.Body.ReturnValue) ? message.Body.ReturnValue : null; + bodyParts = message.Body.Parts; + wrapperName = message.Body.WrapperName; + wrapperNs = message.Body.WrapperNamespace; + } + bool isWrapped = (wrapperName != null); + bool hasReturnValue = returnPart != null; + int paramCount = bodyParts.Count + (hasReturnValue ? 1 : 0); + if (paramCount == 0 && !isWrapped) // no need to create serializer + { + return null; + } + + XmlReflectionMember[] members = new XmlReflectionMember[paramCount]; + int paramIndex = 0; + if (hasReturnValue) + members[paramIndex++] = XmlSerializerHelper.GetXmlReflectionMember(returnPart, IsRpc, IsEncoded, isWrapped); + + for (int i = 0; i < bodyParts.Count; i++) + members[paramIndex++] = XmlSerializerHelper.GetXmlReflectionMember(bodyParts[i], IsRpc, IsEncoded, isWrapped); + + if (!isWrapped) + wrapperNs = ContractNamespace; + return ImportMembersMapping(wrapperName, wrapperNs, members, isWrapped, IsRpc, mappingKey); + } + + private MessagePartDescription GetWrapperPart(MessageDescription message) + { + if (message.Body.Parts.Count != 1) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxRpcMessageMustHaveASingleBody, Operation.Name, message.MessageName))); + MessagePartDescription bodyPart = message.Body.Parts[0]; + Type bodyObjectType = bodyPart.Type; + if (bodyObjectType.GetTypeInfo().BaseType != null && bodyObjectType.GetTypeInfo().BaseType != typeof(object)) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxBodyObjectTypeCannotBeInherited, bodyObjectType.FullName))); + if (typeof(IEnumerable).IsAssignableFrom(bodyObjectType)) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxBodyObjectTypeCannotBeInterface, bodyObjectType.FullName, typeof(IEnumerable).FullName))); + if (typeof(IXmlSerializable).IsAssignableFrom(bodyObjectType)) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxBodyObjectTypeCannotBeInterface, bodyObjectType.FullName, typeof(IXmlSerializable).FullName))); + return bodyPart; + } + + private MessagePartDescriptionCollection GetWrappedParts(MessagePartDescription bodyPart) + { + Type bodyObjectType = bodyPart.Type; + MessagePartDescriptionCollection partList = new MessagePartDescriptionCollection(); + foreach (MemberInfo member in bodyObjectType.GetMembers(BindingFlags.Instance | BindingFlags.Public)) + { + if ((member as PropertyInfo) == null && (member as FieldInfo) == null) + continue; + // TODO: SoapIgnoreAttribute is in 1.7 + //if (member.IsDefined(typeof(SoapIgnoreAttribute), false/*inherit*/)) + // continue; + XmlName xmlName = new XmlName(member.Name); + MessagePartDescription part = new MessagePartDescription(xmlName.EncodedName, string.Empty); + part.AdditionalAttributesProvider = part.MemberInfo = member; + part.Index = part.SerializationPosition = partList.Count; + part.Type = (member is PropertyInfo) ? ((PropertyInfo)member).PropertyType : ((FieldInfo)member).FieldType; + partList.Add(part); + } + return partList; + } + + XmlMembersMapping LoadHeadersMapping(MessageDescription message, string mappingKey) + { + int headerCount = message.Headers.Count; + + if (headerCount == 0) + return null; + if (IsEncoded) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxHeadersAreNotSupportedInEncoded, message.MessageName))); + + int unknownHeaderCount = 0, headerIndex = 0; + XmlReflectionMember[] members = new XmlReflectionMember[headerCount]; + for (int i = 0; i < headerCount; i++) + { + MessageHeaderDescription header = message.Headers[i]; + if (!header.IsUnknownHeaderCollection) + { + members[headerIndex++] = XmlSerializerHelper.GetXmlReflectionMember(header, false/*isRpc*/, IsEncoded, false/*isWrapped*/); + } + else + { + unknownHeaderCount++; + } + } + + if (unknownHeaderCount == headerCount) + { + return null; + } + + if (unknownHeaderCount > 0) + { + XmlReflectionMember[] newMembers = new XmlReflectionMember[headerCount - unknownHeaderCount]; + Array.Copy(members, newMembers, newMembers.Length); + members = newMembers; + } + + return ImportMembersMapping(ContractName, ContractNamespace, members, false /*isWrapped*/, false /*isRpc*/, mappingKey); + } + + internal XmlMembersMapping ImportMembersMapping(string elementName, string ns, XmlReflectionMember[] members, bool hasWrapperElement, bool rpc, string mappingKey) + { + string key = mappingKey.StartsWith(":", StringComparison.Ordinal) ? keyBase + mappingKey : mappingKey; + return parent.importer.ImportMembersMapping(new XmlName(elementName, true /*isEncoded*/), ns, members, hasWrapperElement, rpc, IsEncoded, key); + } + + internal XmlMembersMapping ImportFaultElement(FaultDescription fault, out XmlQualifiedName elementName) + { + // the number of reflection members is always 1 because there is only one fault detail type + XmlReflectionMember[] members = new XmlReflectionMember[1]; + + XmlName faultElementName = fault.ElementName; + string faultNamespace = fault.Namespace; + if (faultElementName == null) + { + XmlTypeMapping mapping = parent.importer.ImportTypeMapping(fault.DetailType, IsEncoded); + faultElementName = new XmlName(mapping.ElementName, IsEncoded); + faultNamespace = mapping.Namespace; + if (faultElementName == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxFaultTypeAnonymous, Operation.Name, fault.DetailType.FullName))); + } + + elementName = new XmlQualifiedName(faultElementName.DecodedName, faultNamespace); + + members[0] = XmlSerializerHelper.GetXmlReflectionMember(null /*memberName*/, faultElementName, faultNamespace, fault.DetailType, + null /*additionalAttributesProvider*/, false /*isMultiple*/, IsEncoded, false /*isWrapped*/); + + string mappingKey = "fault:" + faultElementName.DecodedName + ":" + faultNamespace; + return ImportMembersMapping(faultElementName.EncodedName, faultNamespace, members, false /*hasWrapperElement*/, IsRpc, mappingKey); + } + } + + class XmlSerializerImporter + { + readonly string defaultNs; + XmlReflectionImporter xmlImporter; + // TODO: Available in 1.7 + //SoapReflectionImporter soapImporter; + Dictionary xmlMappings; + + internal XmlSerializerImporter(string defaultNs) + { + this.defaultNs = defaultNs; + xmlImporter = null; + //this.soapImporter = null; + } + + //SoapReflectionImporter SoapImporter + //{ + // get + // { + // if (this.soapImporter == null) + // { + // this.soapImporter = new SoapReflectionImporter(NamingHelper.CombineUriStrings(defaultNs, "encoded")); + // } + // return this.soapImporter; + // } + //} + + XmlReflectionImporter XmlImporter + { + get + { + if (xmlImporter == null) + { + xmlImporter = new XmlReflectionImporter(defaultNs); + } + return xmlImporter; + } + } + + Dictionary XmlMappings + { + get + { + if (xmlMappings == null) + { + xmlMappings = new Dictionary(); + } + return xmlMappings; + } + } + + internal XmlMembersMapping ImportMembersMapping(XmlName elementName, string ns, XmlReflectionMember[] members, bool hasWrapperElement, bool rpc, bool isEncoded, string mappingKey) + { + XmlMembersMapping mapping; + string mappingName = elementName.DecodedName; + if (XmlMappings.TryGetValue(mappingKey, out mapping)) + { + return mapping; + } + + if (isEncoded) + throw new PlatformNotSupportedException(); + //mapping = this.SoapImporter.ImportMembersMapping(mappingName, ns, members, hasWrapperElement, rpc); + else + mapping = XmlImporter.ImportMembersMapping(mappingName, ns, members, hasWrapperElement, rpc); + + mapping.SetKey(mappingKey); + XmlMappings.Add(mappingKey, mapping); + return mapping; + } + + internal XmlTypeMapping ImportTypeMapping(Type type, bool isEncoded) + { + if (isEncoded) + throw new PlatformNotSupportedException(); + //return this.SoapImporter.ImportTypeMapping(type); + else + return XmlImporter.ImportTypeMapping(type); + } + + internal void IncludeType(Type knownType, bool isEncoded) + { + if (isEncoded) + throw new PlatformNotSupportedException(); + //this.SoapImporter.IncludeType(knownType); + else + XmlImporter.IncludeType(knownType); + } + } + + internal class SerializerGenerationContext + { + List Mappings = new List(); + XmlSerializer[] serializers = null; + Type type; + object thisLock = new object(); + + internal SerializerGenerationContext(Type type) + { + this.type = type; + } + + // returns a stub to a serializer + internal SerializerStub AddSerializer(XmlMembersMapping mapping) + { + int handle = -1; + if (mapping != null) + { + handle = ((IList)Mappings).Add(mapping); + } + + return new SerializerStub(this, mapping, handle); + } + + internal XmlSerializer GetSerializer(int handle) + { + if (handle < 0) + { + return null; + } + + if (serializers == null) + { + lock (thisLock) + { + if (serializers == null) + { + serializers = GenerateSerializers(); + } + } + } + return serializers[handle]; + } + + XmlSerializer[] GenerateSerializers() + { + //this.Mappings may have duplicate mappings (for e.g. samed message contract is used by more than one operation) + //XmlSerializer.FromMappings require unique mappings. The following code uniquifies, calls FromMappings and deuniquifies + List uniqueMappings = new List(); + int[] uniqueIndexes = new int[Mappings.Count]; + for (int srcIndex = 0; srcIndex < Mappings.Count; srcIndex++) + { + XmlMembersMapping mapping = Mappings[srcIndex]; + int uniqueIndex = uniqueMappings.IndexOf(mapping); + if (uniqueIndex < 0) + { + uniqueMappings.Add(mapping); + uniqueIndex = uniqueMappings.Count - 1; + } + uniqueIndexes[srcIndex] = uniqueIndex; + } + XmlSerializer[] uniqueSerializers = CreateSerializersFromMappings(uniqueMappings.ToArray(), type); + if (uniqueMappings.Count == Mappings.Count) + return uniqueSerializers; + XmlSerializer[] serializers = new XmlSerializer[Mappings.Count]; + for (int i = 0; i < Mappings.Count; i++) + { + serializers[i] = uniqueSerializers[uniqueIndexes[i]]; + } + return serializers; + } + + XmlSerializer[] CreateSerializersFromMappings(XmlMapping[] mappings, Type type) + { + return XmlSerializer.FromMappings(mappings, type); + } + } + + internal struct SerializerStub + { + readonly SerializerGenerationContext context; + + internal readonly XmlMembersMapping Mapping; + internal readonly int Handle; + + internal SerializerStub(SerializerGenerationContext context, XmlMembersMapping mapping, int handle) + { + this.context = context; + Mapping = mapping; + Handle = handle; + } + + internal XmlSerializer GetSerializer() + { + return context.GetSerializer(Handle); + } + } + + internal class XmlSerializerFaultContractInfo + { + FaultContractInfo faultContractInfo; + SerializerStub serializerStub; + XmlQualifiedName faultContractElementName; + XmlSerializerObjectSerializer serializer; + + internal XmlSerializerFaultContractInfo(FaultContractInfo faultContractInfo, SerializerStub serializerStub, + XmlQualifiedName faultContractElementName) + { + if (faultContractInfo == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("faultContractInfo"); + } + if (faultContractElementName == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("faultContractElementName"); + } + this.faultContractInfo = faultContractInfo; + this.serializerStub = serializerStub; + this.faultContractElementName = faultContractElementName; + } + + internal FaultContractInfo FaultContractInfo + { + get { return faultContractInfo; } + } + + internal XmlQualifiedName FaultContractElementName + { + get { return faultContractElementName; } + } + + internal XmlSerializerObjectSerializer Serializer + { + get + { + if (serializer == null) + serializer = new XmlSerializerObjectSerializer(faultContractInfo.Detail, faultContractElementName, serializerStub.GetSerializer()); + return serializer; + } + } + } + + internal class MessageInfo : XmlSerializerOperationFormatter.MessageInfo + { + SerializerStub headers; + SerializerStub body; + OperationFormatter.MessageHeaderDescriptionTable headerDescriptionTable; + MessageHeaderDescription unknownHeaderDescription; + MessagePartDescriptionCollection rpcEncodedTypedMessageBodyParts; + + internal XmlMembersMapping BodyMapping + { + get { return body.Mapping; } + } + + internal override XmlSerializer BodySerializer + { + get { return body.GetSerializer(); } + } + + internal XmlMembersMapping HeadersMapping + { + get { return headers.Mapping; } + } + + internal override XmlSerializer HeaderSerializer + { + get { return headers.GetSerializer(); } + } + + internal override OperationFormatter.MessageHeaderDescriptionTable HeaderDescriptionTable + { + get { return headerDescriptionTable; } + } + + internal override MessageHeaderDescription UnknownHeaderDescription + { + get { return unknownHeaderDescription; } + } + + internal override MessagePartDescriptionCollection RpcEncodedTypedMessageBodyParts + { + get { return rpcEncodedTypedMessageBodyParts; } + } + + internal void SetBody(SerializerStub body, MessagePartDescriptionCollection rpcEncodedTypedMessageBodyParts) + { + this.body = body; + this.rpcEncodedTypedMessageBodyParts = rpcEncodedTypedMessageBodyParts; + } + + internal void SetHeaders(SerializerStub headers) + { + this.headers = headers; + } + + internal void SetHeaderDescriptionTable(OperationFormatter.MessageHeaderDescriptionTable headerDescriptionTable) + { + this.headerDescriptionTable = headerDescriptionTable; + } + + internal void SetUnknownHeaderDescription(MessageHeaderDescription unknownHeaderDescription) + { + this.unknownHeaderDescription = unknownHeaderDescription; + } + + } + } + } + + static class XmlSerializerHelper + { + static internal XmlReflectionMember GetXmlReflectionMember(MessagePartDescription part, bool isRpc, bool isEncoded, bool isWrapped) + { + string ns = isRpc ? null : part.Namespace; + MemberInfo additionalAttributesProvider = null; + if (part.AdditionalAttributesProvider.MemberInfo != null) + additionalAttributesProvider = part.AdditionalAttributesProvider.MemberInfo; + XmlName memberName = string.IsNullOrEmpty(part.UniquePartName) ? null : new XmlName(part.UniquePartName, true /*isEncoded*/); + XmlName elementName = part.XmlName; + return GetXmlReflectionMember(memberName, elementName, ns, part.Type, additionalAttributesProvider, part.Multiple, isEncoded, isWrapped); + } + + static internal XmlReflectionMember GetXmlReflectionMember(XmlName memberName, XmlName elementName, string ns, Type type, MemberInfo additionalAttributesProvider, bool isMultiple, bool isEncoded, bool isWrapped) + { + if (isEncoded && isMultiple) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxMultiplePartsNotAllowedInEncoded, elementName.DecodedName, ns))); + + XmlReflectionMember member = new XmlReflectionMember(); + member.MemberName = (memberName ?? elementName).DecodedName; + member.MemberType = type; + if (member.MemberType.IsByRef) + member.MemberType = member.MemberType.GetElementType(); + if (isMultiple) + member.MemberType = member.MemberType.MakeArrayType(); + if (additionalAttributesProvider != null) + { + if (isEncoded) + throw new PlatformNotSupportedException(); + //member.SoapAttributes = new SoapAttributes(additionalAttributesProvider); + else + member.XmlAttributes = new XmlAttributes(additionalAttributesProvider); + } + if (isEncoded) + { + throw new PlatformNotSupportedException(); + } + else + { + if (member.XmlAttributes == null) + member.XmlAttributes = new XmlAttributes(); + else + { + Type invalidAttributeType = null; + if (member.XmlAttributes.XmlAttribute != null) + invalidAttributeType = typeof(XmlAttributeAttribute); + else if (member.XmlAttributes.XmlAnyAttribute != null && !isWrapped) + invalidAttributeType = typeof(XmlAnyAttributeAttribute); + else if (member.XmlAttributes.XmlChoiceIdentifier != null) + invalidAttributeType = typeof(XmlChoiceIdentifierAttribute); + else if (member.XmlAttributes.XmlIgnore) + invalidAttributeType = typeof(XmlIgnoreAttribute); + else if (member.XmlAttributes.Xmlns) + invalidAttributeType = typeof(XmlNamespaceDeclarationsAttribute); + else if (member.XmlAttributes.XmlText != null) + invalidAttributeType = typeof(XmlTextAttribute); + else if (member.XmlAttributes.XmlEnum != null) + invalidAttributeType = typeof(XmlEnumAttribute); + if (invalidAttributeType != null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(isWrapped ? SR.SFxInvalidXmlAttributeInWrapped : SR.SFxInvalidXmlAttributeInBare, invalidAttributeType, elementName.DecodedName))); + if (member.XmlAttributes.XmlArray != null && isMultiple) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxXmlArrayNotAllowedForMultiple, elementName.DecodedName, ns))); + + } + + + bool isArray = member.MemberType.IsArray; + if ((isArray && !isMultiple && member.MemberType != typeof(byte[])) || + (!isArray && typeof(IEnumerable).IsAssignableFrom(member.MemberType) && member.MemberType != typeof(string) && !typeof(XmlNode).IsAssignableFrom(member.MemberType) && !typeof(IXmlSerializable).IsAssignableFrom(member.MemberType))) + { + if (member.XmlAttributes.XmlArray != null) + { + if (member.XmlAttributes.XmlArray.ElementName == string.Empty) + member.XmlAttributes.XmlArray.ElementName = elementName.DecodedName; + if (member.XmlAttributes.XmlArray.Namespace == null) + member.XmlAttributes.XmlArray.Namespace = ns; + } + else if (HasNoXmlParameterAttributes(member.XmlAttributes)) + { + member.XmlAttributes.XmlArray = new XmlArrayAttribute(); + member.XmlAttributes.XmlArray.ElementName = elementName.DecodedName; + member.XmlAttributes.XmlArray.Namespace = ns; + } + } + else + { + if (member.XmlAttributes.XmlElements == null || member.XmlAttributes.XmlElements.Count == 0) + { + if (HasNoXmlParameterAttributes(member.XmlAttributes)) + { + XmlElementAttribute elementAttribute = new XmlElementAttribute(); + elementAttribute.ElementName = elementName.DecodedName; + elementAttribute.Namespace = ns; + member.XmlAttributes.XmlElements.Add(elementAttribute); + } + } + else + { + foreach (XmlElementAttribute elementAttribute in member.XmlAttributes.XmlElements) + { + if (elementAttribute.ElementName == string.Empty) + elementAttribute.ElementName = elementName.DecodedName; + if (elementAttribute.Namespace == null) + elementAttribute.Namespace = ns; + } + } + } + } + return member; + } + + static bool HasNoXmlParameterAttributes(XmlAttributes xmlAttributes) + { + return xmlAttributes.XmlAnyAttribute == null && + (xmlAttributes.XmlAnyElements == null || xmlAttributes.XmlAnyElements.Count == 0) && + xmlAttributes.XmlArray == null && + xmlAttributes.XmlAttribute == null && + !xmlAttributes.XmlIgnore && + xmlAttributes.XmlText == null && + xmlAttributes.XmlChoiceIdentifier == null && + (xmlAttributes.XmlElements == null || xmlAttributes.XmlElements.Count == 0) && + !xmlAttributes.Xmlns; + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/DiagnosticUtility.cs b/src/CoreWCF.Primitives/src/CoreWCF/DiagnosticUtility.cs new file mode 100644 index 000000000..a87bd877d --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/DiagnosticUtility.cs @@ -0,0 +1,193 @@ +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using CoreWCF.Runtime; + +namespace CoreWCF +{ + internal class DiagnosticUtility + { + private static ExceptionUtility exceptionUtility = (ExceptionUtility)null; + private static object lockObject = new object(); + + internal static ExceptionUtility ExceptionUtility + { + get + { + return exceptionUtility ?? GetExceptionUtility(); + } + } + + private static ExceptionUtility GetExceptionUtility() + { + lock (lockObject) + { + if (exceptionUtility == null) + // TODO: Make this generic shared code used by multiple assemblies + //exceptionUtility = new ExceptionUtility("System.ServiceModel", "System.ServiceModel 4.0.0.0", (object)DiagnosticUtility.diagnosticTrace, (object)FxTrace.Exception); + exceptionUtility = new ExceptionUtility(); + } + + return exceptionUtility; + } + + internal static void TraceHandledException(Exception exception, TraceEventType traceEventType) + { + //FxTrace.Exception.TraceHandledException(exception, traceEventType); + } + + [Conditional("DEBUG")] + internal static void DebugAssert(bool condition, string message) + { + if (!condition) + { + DebugAssert(message); + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + [Conditional("DEBUG")] + internal static void DebugAssert(string message) + { + Fx.Assert(message); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + internal static Exception FailFast(string message) + { + try + { + try + { + ExceptionUtility.TraceFailFast(message); + } + finally + { + Environment.FailFast(message); + } + } + catch + { + } + Environment.FailFast(message); + return (Exception)null; + } + } + + internal class ExceptionUtility + { + internal ArgumentException ThrowHelperArgument(string message) + { + return (ArgumentException)ThrowHelperError(new ArgumentException(message)); + } + + internal ArgumentException ThrowHelperArgument(string paramName, string message) + { + return (ArgumentException)ThrowHelperError(new ArgumentException(message, paramName)); + } + + internal ArgumentNullException ThrowHelperArgumentNull(string paramName) + { + return (ArgumentNullException)ThrowHelperError(new ArgumentNullException(paramName)); + } + + internal Exception ThrowHelperFatal(string message, Exception innerException) + { + return ThrowHelperError(new FatalException(message, innerException)); + } + + internal Exception ThrowHelperError(Exception exception) + { + return ThrowHelper(exception, TraceEventType.Error); + } + + internal Exception ThrowHelperWarning(Exception exception) + { + return ThrowHelper(exception, TraceEventType.Warning); + } + + internal Exception ThrowHelper(Exception exception, TraceEventType eventType) + { + return ThrowHelper(exception, eventType, null); + } + + internal Exception ThrowHelper(Exception exception, TraceEventType eventType, TraceRecord extendedData) + { + //if ((_diagnosticTrace == null ? 0 : (_diagnosticTrace.ShouldTrace(eventType) ? 1 : 0)) != 0) + //{ + // using ( + // ExceptionUtility.useStaticActivityId + // ? Activity.CreateActivity(ExceptionUtility.activityId) + // : (Activity)null) + // _diagnosticTrace.TraceEvent(eventType, 131075, + // LegacyDiagnosticTrace.GenerateMsdnTraceCode("System.ServiceModel.Diagnostics", + // "ThrowingException"), TraceSR.Format("ThrowingException"), extendedData, exception, + // (object)null); + // IDictionary data = exception.Data; + // if (data != null && !data.IsReadOnly && !data.IsFixedSize) + // { + // object obj = + // data[(object)"System.ServiceModel.Diagnostics.ExceptionUtility.ExceptionStackAsString"]; + // string str1 = obj == null ? "" : obj as string; + // if (str1 != null) + // { + // string stackTrace = exception.StackTrace; + // if (!string.IsNullOrEmpty(stackTrace)) + // { + // string str2 = str1 + (str1.Length == 0 ? "" : Environment.NewLine) + "throw" + + // Environment.NewLine + stackTrace + Environment.NewLine + "catch" + + // Environment.NewLine; + // data[(object)"System.ServiceModel.Diagnostics.ExceptionUtility.ExceptionStackAsString"] + // = (object)str2; + // } + // } + // } + //} + //this.exceptionTrace.TraceEtwException(exception, eventType); + return exception; + } + + internal Exception ThrowHelperCallback(Exception innerException) + { + return ThrowHelperCallback(TraceSR.GenericCallbackException, innerException); + } + + internal Exception ThrowHelperCallback(string message, Exception innerException) + { + return ThrowHelperCritical(new CallbackException(message, innerException)); + } + + internal Exception ThrowHelperCritical(Exception exception) + { + return ThrowHelper(exception, TraceEventType.Critical); + } + + internal class TraceRecord + { + } + + [MethodImpl(MethodImplOptions.NoInlining)] + internal void TraceFailFast(string message) + { + //Microsoft.Runtime.Diagnostics.EventLogger logger = null; + //try + //{ + // logger = new Microsoft.Runtime.Diagnostics.EventLogger(this.eventSourceName, this.diagnosticTrace); + //} + //finally + //{ + // TraceFailFast(message, logger); + //} + } + + internal Exception ThrowHelperArgumentNull(string paramName, string message) + { + return (ArgumentNullException)ThrowHelperError(new ArgumentNullException(paramName, message)); + } + + public Exception ThrowHelperInvalidOperation(string message) + { + return ThrowHelperError(new InvalidOperationException(message)); + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Diagnostics/TraceUtility.cs b/src/CoreWCF.Primitives/src/CoreWCF/Diagnostics/TraceUtility.cs new file mode 100644 index 000000000..7579f7228 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Diagnostics/TraceUtility.cs @@ -0,0 +1,62 @@ +using System; +using System.Reflection; +using CoreWCF.Runtime; +using CoreWCF.Channels; +using CoreWCF.Dispatcher; + +namespace CoreWCF.Diagnostics +{ + // TODO: Work out how TraceUtility fits in with all the other exception and tracing classes + internal static class TraceUtility + { + internal static Exception ThrowHelperError(Exception exception, Message message) + { + // If the message is closed, we won't get an activity + //Guid activityId = TraceUtility.ExtractActivityId(message); + //if (DiagnosticUtility.ShouldTraceError) + //{ + // DiagnosticUtility.DiagnosticTrace.TraceEvent(TraceEventType.Error, TraceCode.ThrowingException, GenerateMsdnTraceCode(TraceCode.ThrowingException), + // TraceSR.Format(TraceSR.ThrowingException), null, exception, activityId, null); + //} + return exception; + } + + internal static Exception ThrowHelperError(Exception exception, Guid activityId, object source) + { + //if (DiagnosticUtility.ShouldTraceError) + //{ + // DiagnosticUtility.DiagnosticTrace.TraceEvent(TraceEventType.Error, TraceCode.ThrowingException, GenerateMsdnTraceCode(TraceCode.ThrowingException), + // TraceSR.Format(TraceSR.ThrowingException), null, exception, activityId, source); + //} + return exception; + } + + internal static void TraceUserCodeException(Exception e, MethodInfo method) + { + //if (DiagnosticUtility.ShouldTraceWarning) + //{ + // StringTraceRecord record = new StringTraceRecord("Comment", + // SR.Format(SR.SFxUserCodeThrewException, method.DeclaringType.FullName, method.Name)); + // DiagnosticUtility.DiagnosticTrace.TraceEvent(TraceEventType.Warning, + // TraceCode.UnhandledExceptionInUserOperation, GenerateMsdnTraceCode(TraceCode.UnhandledExceptionInUserOperation), + // SR.Format(SR.TraceCodeUnhandledExceptionInUserOperation, method.DeclaringType.FullName, method.Name), + // record, + // e, null); + //} + } + + internal static void TraceDroppedMessage(Message requestMessage, EndpointDispatcher endpoint) + { + //if (DiagnosticUtility.ShouldTraceInformation) + //{ + // EndpointAddress endpointAddress = null; + // if (dispatcher != null) + // { + // endpointAddress = dispatcher.EndpointAddress; + // } + // TraceUtility.TraceEvent(TraceEventType.Information, TraceCode.DroppedAMessage, + // SR.Format(SR.TraceCodeDroppedAMessage), new MessageDroppedTraceRecord(message, endpointAddress)); + //} + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ActionMessageFilter.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ActionMessageFilter.cs new file mode 100644 index 000000000..8b0ebf6ac --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ActionMessageFilter.cs @@ -0,0 +1,115 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Runtime.Serialization; +using CoreWCF.Channels; + +namespace CoreWCF.Dispatcher +{ + [DataContract] + internal class ActionMessageFilter : MessageFilter + { + Dictionary actions; + ReadOnlyCollection actionSet; + + [DataMember(IsRequired = true)] + internal string[] DCActions + { + get + { + string[] act = new string[actions.Count]; + actions.Keys.CopyTo(act, 0); + return act; + } + set + { + Init(value); + } + } + + public ActionMessageFilter(params string[] actions) + { + if (actions == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("actions"); + } + + Init(actions); + } + + void Init(string[] actions) + { + if (actions.Length == 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.ActionFilterEmptyList, nameof(actions))); + } + + this.actions = new Dictionary(); + for (int i = 0; i < actions.Length; ++i) + { + // Duplicates are removed + if (!this.actions.ContainsKey(actions[i])) + { + this.actions.Add(actions[i], 0); + } + } + } + + public ReadOnlyCollection Actions + { + get + { + if (actionSet == null) + { + actionSet = new ReadOnlyCollection(new List(actions.Keys)); + } + return actionSet; + } + } + + protected internal override IMessageFilterTable CreateFilterTable() + { + return new ActionMessageFilterTable(); + } + + bool InnerMatch(Message message) + { + string act = message.Headers.Action; + if (act == null) + { + act = string.Empty; + } + + return actions.ContainsKey(act); + } + + public override bool Match(Message message) + { + if (message == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("message"); + } + + return InnerMatch(message); + } + + public override bool Match(MessageBuffer messageBuffer) + { + if (messageBuffer == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("messageBuffer"); + } + + Message msg = messageBuffer.CreateMessage(); + try + { + return InnerMatch(msg); + } + finally + { + msg.Close(); + } + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ActionMessageFilterTable.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ActionMessageFilterTable.cs new file mode 100644 index 000000000..ca9adaa7e --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ActionMessageFilterTable.cs @@ -0,0 +1,496 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Runtime.Serialization; +using CoreWCF.Channels; +using CoreWCF.Diagnostics; + +namespace CoreWCF.Dispatcher +{ + [DataContract] + internal class ActionMessageFilterTable : IMessageFilterTable + { + Dictionary filters; + Dictionary> actions; + List always; + + public ActionMessageFilterTable() + { + Init(); + } + + void Init() + { + filters = new Dictionary(); + actions = new Dictionary>(); + always = new List(); + } + + public TFilterData this[MessageFilter filter] + { + get + { + return filters[filter]; + } + set + { + if (filters.ContainsKey(filter)) + { + filters[filter] = value; + } + else + { + Add(filter, value); + } + } + } + + public int Count + { + get + { + return filters.Count; + } + } + + [DataMember(IsRequired = true)] + Entry[] Entries + { + get + { + Entry[] entries = new Entry[Count]; + int i = 0; + foreach (KeyValuePair item in filters) + entries[i++] = new Entry(item.Key, item.Value); + + return entries; + } + set + { + Init(); + + for (int i = 0; i < value.Length; ++i) + Add(value[i].filter, value[i].data); + } + } + + public bool IsReadOnly + { + get + { + return false; + } + } + + public ICollection Keys + { + get + { + return filters.Keys; + } + } + + public ICollection Values + { + get + { + return filters.Values; + } + } + + public void Add(ActionMessageFilter filter, TFilterData data) + { + if (filter == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("filter"); + } + + this.filters.Add(filter, data); + List filters; + if (filter.Actions.Count == 0) + { + always.Add(filter); + } + else + { + for (int i = 0; i < filter.Actions.Count; ++i) + { + if (!actions.TryGetValue(filter.Actions[i], out filters)) + { + filters = new List(); + actions.Add(filter.Actions[i], filters); + } + filters.Add(filter); + } + } + } + + public void Add(MessageFilter filter, TFilterData data) + { + if (filter == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("filter"); + } + + Add((ActionMessageFilter)filter, data); + } + + public void Add(KeyValuePair item) + { + Add(item.Key, item.Value); + } + + public void Clear() + { + filters.Clear(); + actions.Clear(); + always.Clear(); + } + + public bool Contains(KeyValuePair item) + { + return ((ICollection>)filters).Contains(item); + } + + public bool ContainsKey(MessageFilter filter) + { + return filters.ContainsKey(filter); + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + ((ICollection>)filters).CopyTo(array, arrayIndex); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public IEnumerator> GetEnumerator() + { + return ((ICollection>)filters).GetEnumerator(); + } + + MessageFilter InnerMatch(Message message) + { + string act = message.Headers.Action; + if (act == null) + { + act = string.Empty; + } + + List filters; + if (actions.TryGetValue(act, out filters)) + { + if (always.Count + filters.Count > 1) + { + List tmp = new List(filters); + tmp.AddRange(always); + Collection matches = new Collection(tmp); + throw TraceUtility.ThrowHelperError(new MultipleFilterMatchesException(SR.FilterMultipleMatches, null, matches), message); + } + return filters[0]; + } + + if (always.Count > 1) + { + Collection matches = new Collection(new List(always)); + throw TraceUtility.ThrowHelperError(new MultipleFilterMatchesException(SR.FilterMultipleMatches, null, matches), message); + } + else if (always.Count == 1) + { + return always[0]; + } + + return null; + } + + void InnerMatch(Message message, ICollection results) + { + for (int i = 0; i < always.Count; ++i) + { + results.Add(always[i]); + } + + string act = message.Headers.Action; + if (act == null) + { + act = string.Empty; + } + + List filters; + if (actions.TryGetValue(act, out filters)) + { + for (int i = 0; i < filters.Count; ++i) + { + results.Add(filters[i]); + } + } + } + + void InnerMatchData(Message message, ICollection results) + { + for (int i = 0; i < always.Count; ++i) + { + results.Add(this.filters[always[i]]); + } + + string act = message.Headers.Action; + if (act == null) + { + act = string.Empty; + } + + List filters; + if (actions.TryGetValue(act, out filters)) + { + for (int i = 0; i < filters.Count; ++i) + { + results.Add(this.filters[filters[i]]); + } + } + } + + public bool GetMatchingValue(Message message, out TFilterData data) + { + if (message == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("message"); + } + + MessageFilter f = InnerMatch(message); + if (f == null) + { + data = default(TFilterData); + return false; + } + + data = filters[f]; + return true; + } + + public bool GetMatchingValue(MessageBuffer messageBuffer, out TFilterData data) + { + if (messageBuffer == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("messageBuffer"); + } + + MessageFilter f = null; + Message msg = messageBuffer.CreateMessage(); + try + { + f = InnerMatch(msg); + } + finally + { + msg.Close(); + } + + if (f == null) + { + data = default(TFilterData); + return false; + } + + data = filters[f]; + return true; + } + + public bool GetMatchingFilter(Message message, out MessageFilter filter) + { + if (message == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("message"); + } + + filter = InnerMatch(message); + return filter != null; + } + + public bool GetMatchingFilter(MessageBuffer messageBuffer, out MessageFilter filter) + { + if (messageBuffer == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("messageBuffer"); + } + + Message msg = messageBuffer.CreateMessage(); + try + { + filter = InnerMatch(msg); + return filter != null; + } + finally + { + msg.Close(); + } + } + + public bool GetMatchingFilters(Message message, ICollection results) + { + if (message == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("message"); + } + + if (results == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("results"); + } + + int count = results.Count; + InnerMatch(message, results); + return count != results.Count; + } + + public bool GetMatchingFilters(MessageBuffer messageBuffer, ICollection results) + { + if (messageBuffer == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("messageBuffer"); + } + + if (results == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("results"); + } + + Message msg = messageBuffer.CreateMessage(); + try + { + int count = results.Count; + InnerMatch(msg, results); + return count != results.Count; + } + finally + { + msg.Close(); + } + } + + public bool GetMatchingValues(Message message, ICollection results) + { + if (message == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("message"); + } + + if (results == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("results"); + } + + int count = results.Count; + InnerMatchData(message, results); + return count != results.Count; + } + + public bool GetMatchingValues(MessageBuffer messageBuffer, ICollection results) + { + if (messageBuffer == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("messageBuffer"); + } + + if (results == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("results"); + } + + Message msg = messageBuffer.CreateMessage(); + try + { + int count = results.Count; + InnerMatchData(msg, results); + return count != results.Count; + } + finally + { + msg.Close(); + } + } + + public bool Remove(ActionMessageFilter filter) + { + if (filter == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("filter"); + } + + if (this.filters.Remove(filter)) + { + if (filter.Actions.Count == 0) + { + always.Remove(filter); + } + else + { + List filters; + for (int i = 0; i < filter.Actions.Count; ++i) + { + filters = actions[filter.Actions[i]]; + if (filters.Count == 1) + { + actions.Remove(filter.Actions[i]); + } + else + { + filters.Remove(filter); + } + } + } + return true; + } + return false; + } + + public bool Remove(MessageFilter filter) + { + if (filter == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("filter"); + } + + ActionMessageFilter aFilter = filter as ActionMessageFilter; + if (aFilter != null) + { + return Remove(aFilter); + } + return false; + } + + public bool Remove(KeyValuePair item) + { + if (((ICollection>)filters).Contains(item)) + { + return Remove(item.Key); + } + return false; + } + + public bool TryGetValue(MessageFilter filter, out TFilterData data) + { + return filters.TryGetValue(filter, out data); + } + + [DataContract] + class Entry + { + [DataMember(IsRequired = true)] + internal MessageFilter filter; + + [DataMember(IsRequired = true)] + internal TFilterData data; + + internal Entry(MessageFilter f, TFilterData d) + { + filter = f; + data = d; + } + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/AndMessageFilter.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/AndMessageFilter.cs new file mode 100644 index 000000000..38d48cce7 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/AndMessageFilter.cs @@ -0,0 +1,82 @@ +using CoreWCF.Channels; + +namespace CoreWCF.Dispatcher +{ + internal class AndMessageFilter : MessageFilter + { + MessageFilter filter1; + MessageFilter filter2; + + public AndMessageFilter(MessageFilter filter1, MessageFilter filter2) + { + if (filter1 == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("filter1"); + if (filter2 == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("filter2"); + + this.filter1 = filter1; + this.filter2 = filter2; + } + + public MessageFilter Filter1 + { + get + { + return filter1; + } + } + + public MessageFilter Filter2 + { + get + { + return filter2; + } + } + + protected internal override IMessageFilterTable CreateFilterTable() + { + return new AndMessageFilterTable(); + } + + public override bool Match(Message message) + { + if (message == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("message"); + } + + return filter1.Match(message) && filter2.Match(message); + } + + internal bool Match(Message message, out bool addressMatched) + { + if (message == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("message"); + } + + if (filter1.Match(message)) + { + addressMatched = true; + return filter2.Match(message); + } + else + { + addressMatched = false; + return false; + } + } + + public override bool Match(MessageBuffer messageBuffer) + { + if (messageBuffer == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("messageBuffer"); + } + + return filter1.Match(messageBuffer) && filter2.Match(messageBuffer); + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/AndMessageFilterTable.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/AndMessageFilterTable.cs new file mode 100644 index 000000000..9b681dd76 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/AndMessageFilterTable.cs @@ -0,0 +1,481 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using CoreWCF.Channels; +using CoreWCF.Diagnostics; + +namespace CoreWCF.Dispatcher +{ + internal class AndMessageFilterTable : IMessageFilterTable + { + Dictionary filters; + Dictionary filterData; + MessageFilterTable table; + + public AndMessageFilterTable() + { + filters = new Dictionary(); + filterData = new Dictionary(); + table = new MessageFilterTable(); + } + + public FilterData this[MessageFilter filter] + { + get + { + return filters[filter]; + } + set + { + if (filters.ContainsKey(filter)) + { + filters[filter] = value; + filterData[filter].data = value; + } + else + { + Add(filter, value); + } + } + } + + public int Count + { + get + { + return filters.Count; + } + } + + public bool IsReadOnly + { + get + { + return false; + } + } + + public ICollection Keys + { + get + { + return filters.Keys; + } + } + + public ICollection Values + { + get + { + return filters.Values; + } + } + + public void Add(MessageFilter filter, FilterData data) + { + if (filter == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("filter"); + } + + Add((AndMessageFilter)filter, data); + } + + public void Add(KeyValuePair item) + { + Add(item.Key, item.Value); + + } + public void Add(AndMessageFilter filter, FilterData data) + { + if (filter == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("filter"); + } + + filters.Add(filter, data); + + FilterDataPair pair = new FilterDataPair(filter, data); + filterData.Add(filter, pair); + + table.Add(filter.Filter1, pair); + } + + public void Clear() + { + filters.Clear(); + filterData.Clear(); + table.Clear(); + } + + public bool Contains(KeyValuePair item) + { + return ((ICollection>)filters).Contains(item); + } + + public bool ContainsKey(MessageFilter filter) + { + if (filter == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("filter"); + } + return filters.ContainsKey(filter); + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + ((ICollection>)filters).CopyTo(array, arrayIndex); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public IEnumerator> GetEnumerator() + { + return filters.GetEnumerator(); + } + + FilterDataPair InnerMatch(Message message) + { + List pairs = new List(); + table.GetMatchingValues(message, pairs); + + FilterDataPair pair = null; + for (int i = 0; i < pairs.Count; ++i) + { + if (pairs[i].filter.Filter2.Match(message)) + { + if (pair != null) + { + Collection matches = new Collection(); + matches.Add(pair.filter); + matches.Add(pairs[i].filter); + throw TraceUtility.ThrowHelperError(new MultipleFilterMatchesException(SR.FilterMultipleMatches, null, matches), message); + } + pair = pairs[i]; + } + } + + return pair; + } + + FilterDataPair InnerMatch(MessageBuffer messageBuffer) + { + List pairs = new List(); + table.GetMatchingValues(messageBuffer, pairs); + + FilterDataPair pair = null; + for (int i = 0; i < pairs.Count; ++i) + { + if (pairs[i].filter.Filter2.Match(messageBuffer)) + { + if (pair != null) + { + Collection matches = new Collection(); + matches.Add(pair.filter); + matches.Add(pairs[i].filter); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MultipleFilterMatchesException(SR.FilterMultipleMatches, null, matches)); + } + pair = pairs[i]; + } + } + + return pair; + } + + void InnerMatch(Message message, ICollection results) + { + List pairs = new List(); + table.GetMatchingValues(message, pairs); + + for (int i = 0; i < pairs.Count; ++i) + { + if (pairs[i].filter.Filter2.Match(message)) + { + results.Add(pairs[i].filter); + } + } + } + + void InnerMatchData(Message message, ICollection results) + { + List pairs = new List(); + table.GetMatchingValues(message, pairs); + + for (int i = 0; i < pairs.Count; ++i) + { + if (pairs[i].filter.Filter2.Match(message)) + { + results.Add(pairs[i].data); + } + } + } + + void InnerMatch(MessageBuffer messageBuffer, ICollection results) + { + List pairs = new List(); + table.GetMatchingValues(messageBuffer, pairs); + + for (int i = 0; i < pairs.Count; ++i) + { + if (pairs[i].filter.Filter2.Match(messageBuffer)) + { + results.Add(pairs[i].filter); + } + } + } + + void InnerMatchData(MessageBuffer messageBuffer, ICollection results) + { + List pairs = new List(); + table.GetMatchingValues(messageBuffer, pairs); + + for (int i = 0; i < pairs.Count; ++i) + { + if (pairs[i].filter.Filter2.Match(messageBuffer)) + { + results.Add(pairs[i].data); + } + } + } + + internal bool GetMatchingValue(Message message, out FilterData data, out bool addressMatched) + { + if (message == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("message"); + } + + List pairs = new List(); + addressMatched = table.GetMatchingValues(message, pairs); + + FilterDataPair pair = null; + for (int i = 0; i < pairs.Count; ++i) + { + if (pairs[i].filter.Filter2.Match(message)) + { + if (pair != null) + { + Collection matches = new Collection(); + matches.Add(pair.filter); + matches.Add(pairs[i].filter); + throw TraceUtility.ThrowHelperError(new MultipleFilterMatchesException(SR.FilterMultipleMatches, null, matches), message); + } + pair = pairs[i]; + } + } + + if (pair == null) + { + data = default(FilterData); + return false; + } + + data = pair.data; + return true; + } + + public bool GetMatchingValue(Message message, out FilterData data) + { + if (message == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("message"); + } + + FilterDataPair pair = InnerMatch(message); + if (pair == null) + { + data = default(FilterData); + return false; + } + + data = pair.data; + return true; + } + + public bool GetMatchingValue(MessageBuffer messageBuffer, out FilterData data) + { + if (messageBuffer == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("messageBuffer"); + } + + FilterDataPair pair = InnerMatch(messageBuffer); + + if (pair == null) + { + data = default(FilterData); + return false; + } + + data = pair.data; + return true; + } + + public bool GetMatchingFilter(Message message, out MessageFilter filter) + { + if (message == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("message"); + } + + FilterDataPair pair = InnerMatch(message); + if (pair == null) + { + filter = null; + return false; + } + + filter = pair.filter; + return true; + } + + public bool GetMatchingFilter(MessageBuffer messageBuffer, out MessageFilter filter) + { + if (messageBuffer == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("messageBuffer"); + } + + FilterDataPair pair = InnerMatch(messageBuffer); + + if (pair == null) + { + filter = null; + return false; + } + + filter = pair.filter; + return true; + } + + public bool GetMatchingFilters(Message message, ICollection results) + { + if (message == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("message"); + } + + if (results == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("results"); + } + + int count = results.Count; + InnerMatch(message, results); + return count != results.Count; + } + + public bool GetMatchingFilters(MessageBuffer messageBuffer, ICollection results) + { + if (messageBuffer == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("messageBuffer"); + } + + if (results == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("results"); + } + + int count = results.Count; + InnerMatch(messageBuffer, results); + return count != results.Count; + } + + public bool GetMatchingValues(Message message, ICollection results) + { + if (message == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("message"); + } + + if (results == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("results"); + } + + int count = results.Count; + InnerMatchData(message, results); + return count != results.Count; + } + + public bool GetMatchingValues(MessageBuffer messageBuffer, ICollection results) + { + if (messageBuffer == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("messageBuffer"); + } + + if (results == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("results"); + } + + int count = results.Count; + InnerMatchData(messageBuffer, results); + return count != results.Count; + } + + public bool Remove(MessageFilter filter) + { + if (filter == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("filter"); + } + + AndMessageFilter sbFilter = filter as AndMessageFilter; + if (sbFilter != null) + { + return Remove(sbFilter); + } + return false; + } + + public bool Remove(KeyValuePair item) + { + if (((ICollection>)filters).Contains(item)) + { + return Remove(item.Key); + } + return false; + } + + public bool Remove(AndMessageFilter filter) + { + if (filter == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("filter"); + } + + if (filters.Remove(filter)) + { + filterData.Remove(filter); + table.Remove(filter.Filter1); + + return true; + } + return false; + } + + internal class FilterDataPair + { + internal AndMessageFilter filter; + internal FilterData data; + + internal FilterDataPair(AndMessageFilter filter, FilterData data) + { + this.filter = filter; + this.data = data; + } + } + + public bool TryGetValue(MessageFilter filter, out FilterData data) + { + return filters.TryGetValue(filter, out data); + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/AsyncMethodInvoker.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/AsyncMethodInvoker.cs new file mode 100644 index 000000000..33c8bec6d --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/AsyncMethodInvoker.cs @@ -0,0 +1,161 @@ +//using System; +//using System.Reflection; +//using CoreWCF.Description; +//using CoreWCF.Diagnostics; + +//namespace CoreWCF.Dispatcher +//{ +// public class AsyncMethodInvoker : IOperationInvoker +// { +// MethodInfo beginMethod; +// MethodInfo endMethod; +// InvokeBeginDelegate invokeBeginDelegate; +// InvokeEndDelegate invokeEndDelegate; +// int inputParameterCount; +// int outputParameterCount; + +// public bool IsSynchronous => false; + +// public object[] AllocateInputs() +// { +// return EmptyArray.Allocate(this.InputParameterCount); +// } + +// public object Invoke(object instance, object[] inputs, out object[] outputs) +// { +// throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException()); +// } + +// public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state) +// { +// if (instance == null) +// throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.SFxNoServiceObject)); +// if (inputs == null) +// { +// if (this.InputParameterCount > 0) +// throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxInputParametersToServiceNull, this.InputParameterCount))); +// } +// else if (inputs.Length != this.InputParameterCount) +// throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxInputParametersToServiceInvalid, this.InputParameterCount, inputs.Length))); + +// //StartOperationInvokePerformanceCounters(this.beginMethod.Name.Substring(ServiceReflector.BeginMethodNamePrefix.Length)); + +// IAsyncResult returnValue; +// //bool callFailed = true; +// //bool callFaulted = false; +// //ServiceModelActivity activity = null; +// try +// { +// //Activity boundActivity = null; +// //CreateActivityInfo(ref activity, ref boundActivity); + +// //StartOperationInvokeTrace(this.beginMethod.Name); + +// //using (boundActivity) +// //{ +// // if (DiagnosticUtility.ShouldUseActivity) +// // { +// // string activityName = null; + +// // if (this.endMethod == null) +// // { +// // activityName = SR.Format(SR.ActivityExecuteMethod, +// // this.beginMethod.DeclaringType.FullName, this.beginMethod.Name); +// // } +// // else +// // { +// // activityName = SR.Format(SR.ActivityExecuteAsyncMethod, +// // this.beginMethod.DeclaringType.FullName, this.beginMethod.Name, +// // this.endMethod.DeclaringType.FullName, this.endMethod.Name); +// // } + +// // ServiceModelActivity.Start(activity, activityName, ActivityType.ExecuteUserCode); +// // } + +// returnValue = this.InvokeBeginDelegate(instance, inputs, callback, state); +// //callFailed = false; +// //} +// } +// catch (System.Security.SecurityException e) +// { +// DiagnosticUtility.TraceHandledException(e, TraceEventType.Warning); +// throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(AuthorizationBehavior.CreateAccessDeniedFaultException()); +// } +// catch (Exception e) +// { +// TraceUtility.TraceUserCodeException(e, this.beginMethod); +// //if (e is FaultException) +// //{ +// // callFaulted = true; +// // callFailed = false; +// //} + +// throw; +// } +// finally +// { +// //ServiceModelActivity.Stop(activity); + +// //// An exception during the InvokeBegin will not call InvokeEnd, +// //// so we complete the trace and performance counters here. +// //if (callFailed || callFaulted) +// //{ +// // StopOperationInvokeTrace(callFailed, callFaulted, this.EndMethod.Name); +// // StopOperationInvokePerformanceCounters(callFailed, callFaulted, endMethod.Name.Substring(ServiceReflector.EndMethodNamePrefix.Length)); +// //} +// } +// return returnValue; + +// } + +// public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result) +// { +// throw new NotImplementedException(); +// } + +// InvokeBeginDelegate InvokeBeginDelegate +// { +// get +// { +// EnsureIsInitialized(); +// return invokeBeginDelegate; +// } +// } + +// InvokeEndDelegate InvokeEndDelegate +// { +// get +// { +// EnsureIsInitialized(); +// return invokeEndDelegate; +// } +// } + +// int InputParameterCount +// { +// get +// { +// EnsureIsInitialized(); +// return this.inputParameterCount; +// } +// } + +// void EnsureIsInitialized() +// { +// if (this.invokeBeginDelegate == null) +// { +// // Only pass locals byref because InvokerUtil may store temporary results in the byref. +// // If two threads both reference this.count, temporary results may interact. +// int inputParameterCount; +// InvokeBeginDelegate invokeBeginDelegate = new InvokerUtil().GenerateInvokeBeginDelegate(this.beginMethod, out inputParameterCount); +// this.inputParameterCount = inputParameterCount; + +// int outputParameterCount; +// InvokeEndDelegate invokeEndDelegate = new InvokerUtil().GenerateInvokeEndDelegate(this.endMethod, out outputParameterCount); +// this.outputParameterCount = outputParameterCount; +// this.invokeEndDelegate = invokeEndDelegate; +// this.invokeBeginDelegate = invokeBeginDelegate; // must set this last due to race +// } +// } +// } +//} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/AuthorizationBehavior.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/AuthorizationBehavior.cs new file mode 100644 index 000000000..1f345e83f --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/AuthorizationBehavior.cs @@ -0,0 +1,23 @@ +using System; +using System.Globalization; + +namespace CoreWCF.Dispatcher +{ + public class AuthorizationBehavior + { + // These values comes from SecurityJan2004Dictionary but there's a lot of boiler plate code to get this value so I've put these values + // directly here to avoid pulling in extra code for now. + internal const string FailedAuthenticationFaultCode = "FailedAuthentication"; + internal const string HeaderNamespace = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"; + + internal static Exception CreateAccessDeniedFaultException() + { + // always use default version? + //SecurityVersion wss = SecurityVersion.Default; + //FaultCode faultCode = FaultCode.CreateSenderFaultCode(wss.FailedAuthenticationFaultCode.Value, wss.HeaderNamespace.Value); + FaultCode faultCode = FaultCode.CreateSenderFaultCode(FailedAuthenticationFaultCode, HeaderNamespace); + FaultReason faultReason = new FaultReason(SR.AccessDenied, CultureInfo.CurrentCulture); + return new FaultException(faultReason, faultCode); + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/BufferedReceiveBinder.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/BufferedReceiveBinder.cs new file mode 100644 index 000000000..a191c5d0c --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/BufferedReceiveBinder.cs @@ -0,0 +1,167 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using CoreWCF.Runtime; +using CoreWCF.Channels; + +namespace CoreWCF.Dispatcher +{ + internal class BufferedReceiveBinder : IChannelBinder + { + static Action tryReceive = TryReceive; + + IChannelBinder channelBinder; + InputQueue inputQueue; + + + int pendingOperationSemaphore; + + public BufferedReceiveBinder(IChannelBinder channelBinder) + { + this.channelBinder = channelBinder; + inputQueue = new InputQueue(); + } + + public IChannel Channel + { + get { return channelBinder.Channel; } + } + + public bool HasSession + { + get { return channelBinder.HasSession; } + } + + public Uri ListenUri + { + get { return channelBinder.ListenUri; } + } + + public EndpointAddress LocalAddress + { + get { return channelBinder.LocalAddress; } + } + + public EndpointAddress RemoteAddress + { + get { return channelBinder.RemoteAddress; } + } + + public void Abort() + { + inputQueue.Close(); + channelBinder.Abort(); + } + + public void CloseAfterFault(TimeSpan timeout) + { + inputQueue.Close(); + channelBinder.CloseAfterFault(timeout); + } + + // Locking: + // Only 1 channelBinder operation call should be active at any given time. All future calls + // will wait on the inputQueue. The semaphore is always released right before the Dispatch on the inputQueue. + // This protects a new call racing with an existing operation that is just about to fully complete. + + public async Task> TryReceiveAsync(CancellationToken token) + { + if (Interlocked.CompareExchange(ref pendingOperationSemaphore, 1, 0) == 0) + { + ActionItem.Schedule(tryReceive, this); + } + + var result = await inputQueue.TryDequeueAsync(token); + bool success = result.Success; + RequestContextWrapper wrapper = result.Result; + + if (success && wrapper != null) + { + return TryAsyncResult.FromResult(wrapper.RequestContext); + } + + return TryAsyncResult.FailedResult; + + } + + public RequestContext CreateRequestContext(Message message) + { + return channelBinder.CreateRequestContext(message); + } + + public Task SendAsync(Message message, CancellationToken token) + { + return channelBinder.SendAsync(message, token); + } + + public Task RequestAsync(Message message, CancellationToken token) + { + return channelBinder.RequestAsync(message, token); + } + + public Task WaitForMessageAsync(CancellationToken token) + { + return channelBinder.WaitForMessageAsync(token); + } + + internal void InjectRequest(RequestContext requestContext) + { + // Reuse the existing requestContext + inputQueue.EnqueueAndDispatch(new RequestContextWrapper(requestContext)); + } + + // + // TryReceive threads + // + + static async void TryReceive(object state) + { + BufferedReceiveBinder binder = (BufferedReceiveBinder)state; + + bool requiresDispatch = false; + try + { + var result = await binder.channelBinder.TryReceiveAsync(CancellationToken.None); + if (result.Success) + { + requiresDispatch = binder.inputQueue.EnqueueWithoutDispatch(new RequestContextWrapper(result.Result), null); + } + } + catch (Exception exception) + { + if (Fx.IsFatal(exception)) + { + throw; + } + + requiresDispatch = binder.inputQueue.EnqueueWithoutDispatch(exception, null); + } + finally + { + Interlocked.Exchange(ref binder.pendingOperationSemaphore, 0); + if (requiresDispatch) + { + binder.inputQueue.Dispatch(); + } + } + } + + // A RequestContext may be 'null' (some pieces of ChannelHandler depend on this) but the InputQueue + // will not allow null items to be enqueued. Wrap the RequestContexts in another object to + // facilitate this semantic + class RequestContextWrapper + { + public RequestContextWrapper(RequestContext requestContext) + { + RequestContext = requestContext; + } + + public RequestContext RequestContext + { + get; + private set; + } + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ChannelDispatcher.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ChannelDispatcher.cs new file mode 100644 index 000000000..50dd9fc20 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ChannelDispatcher.cs @@ -0,0 +1,555 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Globalization; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using CoreWCF.Collections.Generic; +using CoreWCF.Runtime; +using CoreWCF.Channels; +using CoreWCF.Diagnostics; + +namespace CoreWCF.Dispatcher +{ + // This class is now only used as an OM for configuring the service. + // This class has been kept to enable using existing behaviors. + public class ChannelDispatcher : ChannelDispatcherBase + { + ThreadSafeMessageFilterTable addressTable; + CommunicationObjectManager channels; + EndpointDispatcherCollection endpointDispatchers; + EndpointDispatcherTable filterTable; + ServiceHostBase host; + //bool isTransactedReceive; + //bool asynchronousTransactedAcceptEnabled; + //readonly IChannelListener listener; + //int maxTransactedBatchSize; + MessageVersion messageVersion; + //SynchronizedChannelCollection pendingChannels; // app has not yet seen these. + bool receiveSynchronously; + bool sendAsynchronously; + int maxPendingReceives; + bool includeExceptionDetailInFaults; + //ServiceThrottle serviceThrottle; + bool session; + SharedRuntimeState shared; + IDefaultCommunicationTimeouts timeouts; + //IsolationLevel transactionIsolationLevel = ServiceBehaviorAttribute.DefaultIsolationLevel; + //bool transactionIsolationLevelSet; + TimeSpan transactionTimeout; + bool performDefaultCloseInput; + //EventTraceActivity eventTraceActivity; + ErrorBehavior errorBehavior; + + internal ChannelDispatcher(SharedRuntimeState shared) + { + Initialize(shared); + } + + internal ChannelDispatcher(Uri listenUri, Binding binding, string bindingName, IDefaultCommunicationTimeouts timeouts) + { + BindingName = bindingName; + Binding = binding; + ListenUri = listenUri; + this.timeouts = new ImmutableCommunicationTimeouts(timeouts); + Initialize(new SharedRuntimeState(true)); + } + + void Initialize(SharedRuntimeState shared) + { + this.shared = shared; + endpointDispatchers = new EndpointDispatcherCollection(this); + ChannelInitializers = NewBehaviorCollection(); + channels = new CommunicationObjectManager(ThisLock); + ErrorHandlers = new Collection(); + //this.isTransactedReceive = false; + //this.asynchronousTransactedAcceptEnabled = false; + receiveSynchronously = false; + sendAsynchronously = true; + //this.serviceThrottle = null; + //transactionTimeout = TimeSpan.Zero; + maxPendingReceives = MultipleReceiveBinder.MultipleReceiveDefaults.MaxPendingReceives; + } + + public string BindingName { get; } + + // TODO: As the channel concept is changing, does it make sense to support IChannelInitializer's? + public SynchronizedCollection ChannelInitializers { get; private set; } + + protected override TimeSpan DefaultCloseTimeout + { + get + { + if (timeouts != null) + { + return timeouts.CloseTimeout; + } + else + { + return ServiceDefaults.CloseTimeout; + } + } + } + + protected override TimeSpan DefaultOpenTimeout + { + get + { + if (timeouts != null) + { + return timeouts.OpenTimeout; + } + else + { + return ServiceDefaults.OpenTimeout; + } + } + } + + internal EndpointDispatcherTable EndpointDispatcherTable + { + get { return filterTable; } + } + + public SynchronizedCollection Endpoints + { + get { return endpointDispatchers; } + } + + public Collection ErrorHandlers { get; private set; } + + public MessageVersion MessageVersion + { + get { return messageVersion; } + set + { + messageVersion = value; + ThrowIfDisposedOrImmutable(); + } + } + + public override ServiceHostBase Host + { + get { return host; } + } + + internal bool EnableFaults + { + get { return this.shared.EnableFaults; } + set + { + this.ThrowIfDisposedOrImmutable(); + this.shared.EnableFaults = value; + } + } + + internal bool BufferedReceiveEnabled + { + get; + set; + } + + // TODO: Decide: Remove API/Make API do nothing/throw PNSE + public int MaxTransactedBatchSize + { + get + { + throw new PlatformNotSupportedException(); + } + set + { + throw new PlatformNotSupportedException(); + } + } + + //public ServiceThrottle ServiceThrottle + //{ + // get + // { + // return this.serviceThrottle; + // } + // set + // { + // this.ThrowIfDisposedOrImmutable(); + // this.serviceThrottle = value; + // } + //} + + public bool ManualAddressing + { + get { return shared.ManualAddressing; } + set + { + ThrowIfDisposedOrImmutable(); + shared.ManualAddressing = value; + } + } + + public bool ReceiveSynchronously + { + get + { + return receiveSynchronously; + } + set + { + ThrowIfDisposedOrImmutable(); + if (value != false) + { + throw new ArgumentException("Only false supported", nameof(ReceiveSynchronously)); + } + + receiveSynchronously = value; + } + } + + public bool SendAsynchronously + { + get + { + return sendAsynchronously; + } + set + { + ThrowIfDisposedOrImmutable(); + if (value != true) + { + throw new ArgumentException("Only true supported", nameof(ReceiveSynchronously)); + } + + sendAsynchronously = value; + } + + } + + // TODO: Do we ignore? Find a way to propagate? Throw? + public int MaxPendingReceives + { + get + { + return maxPendingReceives; + } + set + { + ThrowIfDisposedOrImmutable(); + maxPendingReceives = value; + } + } + + public bool IncludeExceptionDetailInFaults + { + get { return includeExceptionDetailInFaults; } + set + { + lock (ThisLock) + { + ThrowIfDisposedOrImmutable(); + includeExceptionDetailInFaults = value; + } + } + } + + internal Uri ListenUri { get; } + + internal Binding Binding { get; } + + //public IsolationLevel TransactionIsolationLevel + //{ + // get { return this.transactionIsolationLevel; } + // set + // { + // switch (value) + // { + // case IsolationLevel.Serializable: + // case IsolationLevel.RepeatableRead: + // case IsolationLevel.ReadCommitted: + // case IsolationLevel.ReadUncommitted: + // case IsolationLevel.Unspecified: + // case IsolationLevel.Chaos: + // case IsolationLevel.Snapshot: + // break; + + // default: + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value")); + // } + + // this.ThrowIfDisposedOrImmutable(); + // this.transactionIsolationLevel = value; + // this.transactionIsolationLevelSet = true; + // } + //} + + //internal bool TransactionIsolationLevelSet + //{ + // get { return this.transactionIsolationLevelSet; } + //} + + //public TimeSpan TransactionTimeout + //{ + // get + // { + // return this.transactionTimeout; + // } + // set + // { + // if (value < TimeSpan.Zero) + // { + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value, + // SR.Format(SR.SFxTimeoutOutOfRange0))); + // } + + // if (TimeoutHelper.IsTooLarge(value)) + // { + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value, + // SR.Format(SR.SFxTimeoutOutOfRangeTooBig))); + // } + + // this.ThrowIfDisposedOrImmutable(); + // this.transactionTimeout = value; + // } + //} + + + // TODO: Move this functionality somewhere else as this class is now OM only + internal bool HandleError(Exception error) + { + ErrorHandlerFaultInfo dummy = new ErrorHandlerFaultInfo(); + return HandleError(error, ref dummy); + } + + // TODO: Move this functionality somewhere else as this class is now OM only + internal bool HandleError(Exception error, ref ErrorHandlerFaultInfo faultInfo) + { + ErrorBehavior behavior; + + lock (ThisLock) + { + if (errorBehavior != null) + { + behavior = errorBehavior; + } + else + { + behavior = new ErrorBehavior(this); + } + } + + if (behavior != null) + { + return behavior.HandleError(error, ref faultInfo); + } + else + { + return false; + } + } + + internal void Init() + { + errorBehavior = new ErrorBehavior(this); + + filterTable = new EndpointDispatcherTable(ThisLock); + for (int i = 0; i < endpointDispatchers.Count; i++) + { + EndpointDispatcher endpoint = endpointDispatchers[i]; + + // Force a build of the runtime to catch any unexpected errors before we are done opening. + endpoint.DispatchRuntime.GetRuntime(); + // Lock down the DispatchRuntime. + endpoint.DispatchRuntime.LockDownProperties(); + + filterTable.AddEndpoint(endpoint); + + if ((addressTable != null) && (endpoint.OriginalAddress != null)) + { + addressTable.Add(endpoint.AddressFilter, endpoint.OriginalAddress, endpoint.FilterPriority); + } + + //if (DiagnosticUtility.ShouldTraceInformation) + //{ + // this.TraceEndpointLifetime(endpoint, TraceCode.EndpointListenerOpen, SR.Format(SR.TraceCodeEndpointListenerOpen)); + //} + } + } + + internal SynchronizedCollection NewBehaviorCollection() + { + return new ChannelDispatcherBehaviorCollection(this); + } + + internal bool HasApplicationEndpoints + { + get + { + foreach (EndpointDispatcher endpointDispatcher in this.Endpoints) + { + if (!endpointDispatcher.IsSystemEndpoint) + { + return true; + } + } + return false; + } + } + + void OnAddEndpoint(EndpointDispatcher endpoint) + { + lock (ThisLock) + { + endpoint.Attach(this); + + if (State == CommunicationState.Opened) + { + if (addressTable != null) + { + addressTable.Add(endpoint.AddressFilter, endpoint.EndpointAddress, endpoint.FilterPriority); + } + + filterTable.AddEndpoint(endpoint); + } + } + } + + void OnRemoveEndpoint(EndpointDispatcher endpoint) + { + lock (ThisLock) + { + if (State == CommunicationState.Opened) + { + filterTable.RemoveEndpoint(endpoint); + + if (addressTable != null) + { + addressTable.Remove(endpoint.AddressFilter); + } + } + + endpoint.Detach(this); + } + } + + protected override void OnAbort() + { + throw new PlatformNotSupportedException(); + } + + protected override Task OnCloseAsync(CancellationToken token) + { + throw new PlatformNotSupportedException(); + } + + protected override Task OnOpenAsync(CancellationToken token) + { + throw new PlatformNotSupportedException(); + } + + // TODO: Move this functionality somewhere else as this class is now OM only + internal void ProvideFault(Exception e, FaultConverter faultConverter, ref ErrorHandlerFaultInfo faultInfo) + { + ErrorBehavior behavior; + + lock (ThisLock) + { + if (errorBehavior != null) + { + behavior = errorBehavior; + } + else + { + behavior = new ErrorBehavior(this); + } + } + + behavior.ProvideFault(e, faultConverter, ref faultInfo); + } + + class EndpointDispatcherCollection : SynchronizedCollection + { + ChannelDispatcher owner; + + internal EndpointDispatcherCollection(ChannelDispatcher owner) + : base(owner.ThisLock) + { + this.owner = owner; + } + + protected override void ClearItems() + { + foreach (EndpointDispatcher item in Items) + { + owner.OnRemoveEndpoint(item); + } + base.ClearItems(); + } + + protected override void InsertItem(int index, EndpointDispatcher item) + { + if (item == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("item"); + + owner.OnAddEndpoint(item); + base.InsertItem(index, item); + } + + protected override void RemoveItem(int index) + { + EndpointDispatcher item = Items[index]; + base.RemoveItem(index); + owner.OnRemoveEndpoint(item); + } + + protected override void SetItem(int index, EndpointDispatcher item) + { + Exception error = new InvalidOperationException(SR.SFxCollectionDoesNotSupportSet0); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(error); + } + } + + class ChannelDispatcherBehaviorCollection : SynchronizedCollection + { + ChannelDispatcher outer; + + internal ChannelDispatcherBehaviorCollection(ChannelDispatcher outer) + : base(outer.ThisLock) + { + this.outer = outer; + } + + protected override void ClearItems() + { + outer.ThrowIfDisposedOrImmutable(); + base.ClearItems(); + } + + protected override void InsertItem(int index, T item) + { + if (item == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(item)); + } + + outer.ThrowIfDisposedOrImmutable(); + base.InsertItem(index, item); + } + + protected override void RemoveItem(int index) + { + outer.ThrowIfDisposedOrImmutable(); + base.RemoveItem(index); + } + + protected override void SetItem(int index, T item) + { + if (item == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(item)); + } + + outer.ThrowIfDisposedOrImmutable(); + base.SetItem(index, item); + } + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ChannelDispatcherBase.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ChannelDispatcherBase.cs new file mode 100644 index 000000000..09ceb1f86 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ChannelDispatcherBase.cs @@ -0,0 +1,30 @@ +using System; +using System.Threading; +using CoreWCF.Channels; +using System.Threading.Tasks; + +namespace CoreWCF.Dispatcher +{ + public abstract class ChannelDispatcherBase : CommunicationObject + { + public abstract ServiceHostBase Host { get; } + + protected virtual void Attach(ServiceHostBase host) + { + } + + protected virtual void Detach(ServiceHostBase host) + { + } + + public virtual Task CloseInputAsync() + { + return Task.CompletedTask; + } + + internal virtual Task CloseInputAsync(CancellationToken token) + { + return CloseInputAsync(); // back-compat + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ChannelDispatcherCollection.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ChannelDispatcherCollection.cs new file mode 100644 index 000000000..19157ab1b --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ChannelDispatcherCollection.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using CoreWCF.Collections.Generic; + +namespace CoreWCF.Dispatcher +{ + public class ChannelDispatcherCollection : SynchronizedCollection + { + ServiceHostBase service; + + internal ChannelDispatcherCollection(ServiceHostBase service, object syncRoot) + : base(syncRoot) + { + this.service = service ?? throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(service)); + } + + protected override void ClearItems() + { + throw new PlatformNotSupportedException(); + } + + protected override void InsertItem(int index, ChannelDispatcherBase item) + { + if (service != null) + { + if (service.State == CommunicationState.Closed) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ObjectDisposedException(service.GetType().ToString())); + } + + base.InsertItem(index, item); + } + + protected override void RemoveItem(int index) + { + ChannelDispatcherBase channelDispatcher = Items[index]; + base.RemoveItem(index); + } + + protected override void SetItem(int index, ChannelDispatcherBase item) + { + if (service != null) + { + if (service.State == CommunicationState.Closed) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ObjectDisposedException(service.GetType().ToString())); + } + + ChannelDispatcherBase old; + + lock (SyncRoot) + { + old = Items[index]; + base.SetItem(index, item); + } + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ChannelHandler.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ChannelHandler.cs new file mode 100644 index 000000000..8985d0e60 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ChannelHandler.cs @@ -0,0 +1,1511 @@ +using System; +using System.Globalization; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; +using System.Xml; +using CoreWCF.Runtime; +using CoreWCF.Channels; +using CoreWCF.Description; +using CoreWCF.Diagnostics; +using SessionIdleManager = CoreWCF.Channels.ServiceChannel.SessionIdleManager; +using System.Diagnostics; + +namespace CoreWCF.Dispatcher +{ + internal class ChannelHandler + { + public static readonly TimeSpan CloseAfterFaultTimeout = TimeSpan.FromSeconds(10); + public const string MessageBufferPropertyName = "_RequestMessageBuffer_"; + + readonly IChannelBinder binder; + readonly DuplexChannelBinder duplexBinder; + readonly ServiceHostBase host; + readonly bool incrementedActivityCountInConstructor; + readonly bool isCallback; + readonly ListenerHandler listener; + //readonly ServiceThrottle throttle; + //readonly bool wasChannelThrottled; + readonly ServiceChannel.SessionIdleManager idleManager; + readonly bool sendAsynchronously; + + //static AsyncCallback onAsyncReplyComplete = Fx.ThunkCallback(new AsyncCallback(ChannelHandler.OnAsyncReplyComplete)); + //static AsyncCallback onAsyncReceiveComplete = Fx.ThunkCallback(new AsyncCallback(ChannelHandler.OnAsyncReceiveComplete)); + //static Action onContinueAsyncReceive = OnContinueAsyncReceive; + //static Action onStartSyncMessagePump = OnStartSyncMessagePump; + //static Action onStartAsyncMessagePump = OnStartAsyncMessagePump; + //static Action onStartSingleTransactedBatch = OnStartSingleTransactedBatch; + static Action openAndEnsurePump = OpenAndEnsurePump; + + RequestInfo requestInfo; + ServiceChannel channel; + bool doneReceiving; + bool hasRegisterBeenCalled; + bool hasSession; + int isPumpAcquired; + bool isChannelTerminated; + bool isConcurrent; + bool isManualAddressing; + MessageVersion messageVersion; + ErrorHandlingReceiver receiver; + bool receiveSynchronously; + //bool receiveWithTransaction; + RequestContext replied; + //WrappedTransaction acceptTransaction; + //ServiceThrottle instanceContextThrottle; + //SharedTransactedBatchContext sharedTransactedBatchContext; + //TransactedBatchContext transactedBatchContext; + //bool isMainTransactedBatchHandler; + //EventTraceActivity eventTraceActivity; + SessionOpenNotification sessionOpenNotification; + bool needToCreateSessionOpenNotificationMessage; + bool shouldRejectMessageWithOnOpenActionHeader; + + internal ChannelHandler(MessageVersion messageVersion, IChannelBinder binder, ServiceChannel channel) + { + ClientRuntime clientRuntime = channel.ClientRuntime; + + this.messageVersion = messageVersion; + isManualAddressing = clientRuntime.ManualAddressing; + this.binder = binder; + this.channel = channel; + + isConcurrent = true; + duplexBinder = binder as DuplexChannelBinder; + hasSession = binder.HasSession; + isCallback = true; + + DispatchRuntime dispatchRuntime = clientRuntime.DispatchRuntime; + if (dispatchRuntime == null) + { + receiver = new ErrorHandlingReceiver(binder, null); + } + else + { + receiver = new ErrorHandlingReceiver(binder, dispatchRuntime.ChannelDispatcher); + } + requestInfo = new RequestInfo(this); + + } + + internal ChannelHandler(MessageVersion messageVersion, IChannelBinder binder, /*ServiceThrottle throttle,*/ + ListenerHandler listener, /*bool wasChannelThrottled,*/ /*WrappedTransaction acceptTransaction,*/ ServiceChannel.SessionIdleManager idleManager) + { + ChannelDispatcher channelDispatcher = listener.ChannelDispatcher; + + this.messageVersion = messageVersion; + isManualAddressing = channelDispatcher.ManualAddressing; + this.binder = binder; + //this.throttle = throttle; + this.listener = listener; + //this.wasChannelThrottled = wasChannelThrottled; + + host = listener.Host; + receiveSynchronously = channelDispatcher.ReceiveSynchronously; + sendAsynchronously = channelDispatcher.SendAsynchronously; + duplexBinder = binder as DuplexChannelBinder; + hasSession = binder.HasSession; + isConcurrent = ConcurrencyBehavior.IsConcurrent(channelDispatcher, hasSession); + + if (channelDispatcher.MaxPendingReceives > 1) + { + // We need to preserve order if the ChannelHandler is not concurrent. + this.binder = new MultipleReceiveBinder( + this.binder, + channelDispatcher.MaxPendingReceives, + !isConcurrent); + } + + //if (channelDispatcher.BufferedReceiveEnabled) + //{ + // this.binder = new BufferedReceiveBinder(this.binder); + //} + + receiver = new ErrorHandlingReceiver(this.binder, channelDispatcher); + this.idleManager = idleManager; + //Fx.Assert((this.idleManager != null) == (this.binder.HasSession && this.listener.ChannelDispatcher.DefaultCommunicationTimeouts.ReceiveTimeout != TimeSpan.MaxValue), "idle manager is present only when there is a session with a finite receive timeout"); + + //if (channelDispatcher.IsTransactedReceive && !channelDispatcher.ReceiveContextEnabled) + //{ + // receiveSynchronously = true; + // receiveWithTransaction = true; + + // if (channelDispatcher.MaxTransactedBatchSize > 0) + // { + // int maxConcurrentBatches = 1; + // if (null != throttle && throttle.MaxConcurrentCalls > 1) + // { + // maxConcurrentBatches = throttle.MaxConcurrentCalls; + // foreach (EndpointDispatcher endpointDispatcher in channelDispatcher.Endpoints) + // { + // if (ConcurrencyMode.Multiple != endpointDispatcher.DispatchRuntime.ConcurrencyMode) + // { + // maxConcurrentBatches = 1; + // break; + // } + // } + // } + + // this.sharedTransactedBatchContext = new SharedTransactedBatchContext(this, channelDispatcher, maxConcurrentBatches); + // this.isMainTransactedBatchHandler = true; + // this.throttle = null; + // } + //} + //else if (channelDispatcher.IsTransactedReceive && channelDispatcher.ReceiveContextEnabled && channelDispatcher.MaxTransactedBatchSize > 0) + //{ + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.IncompatibleBehaviors)); + //} + + if (this.binder.HasSession) + { + sessionOpenNotification = this.binder.Channel.GetProperty(); + needToCreateSessionOpenNotificationMessage = sessionOpenNotification != null && sessionOpenNotification.IsEnabled; + } + + //this.acceptTransaction = acceptTransaction; + requestInfo = new RequestInfo(this); + + if (this.listener.State == CommunicationState.Opened) + { + //this.listener.ChannelDispatcher.Channels.IncrementActivityCount(); + incrementedActivityCountInConstructor = true; + } + } + + + //internal ChannelHandler(ChannelHandler handler, TransactedBatchContext context) + //{ + // this.messageVersion = handler.messageVersion; + // this.isManualAddressing = handler.isManualAddressing; + // this.binder = handler.binder; + // this.listener = handler.listener; + // this.wasChannelThrottled = handler.wasChannelThrottled; + + // this.host = handler.host; + // this.receiveSynchronously = true; + // this.receiveWithTransaction = true; + // this.duplexBinder = handler.duplexBinder; + // this.hasSession = handler.hasSession; + // this.isConcurrent = handler.isConcurrent; + // this.receiver = handler.receiver; + + // this.sharedTransactedBatchContext = context.Shared; + // this.transactedBatchContext = context; + // this.requestInfo = new RequestInfo(this); + + // this.sendAsynchronously = handler.sendAsynchronously; + // this.sessionOpenNotification = handler.sessionOpenNotification; + // this.needToCreateSessionOpenNotificationMessage = handler.needToCreateSessionOpenNotificationMessage; + // this.shouldRejectMessageWithOnOpenActionHeader = handler.shouldRejectMessageWithOnOpenActionHeader; + //} + + internal IChannelBinder Binder + { + get { return binder; } + } + + internal ServiceChannel Channel + { + get { return channel; } + } + + internal bool HasRegisterBeenCalled + { + get { return hasRegisterBeenCalled; } + } + + internal InstanceContext InstanceContext + { + get { return (channel != null) ? channel.InstanceContext : null; } + } + + //internal ServiceThrottle InstanceContextServiceThrottle + //{ + // get + // { + // return this.instanceContextThrottle; + // } + // set + // { + // this.instanceContextThrottle = value; + // } + //} + + bool IsOpen + { + get { return binder.Channel.State == CommunicationState.Opened; } + } + + EndpointAddress LocalAddress + { + get + { + if (binder != null) + { + IInputChannel input = binder.Channel as IInputChannel; + if (input != null) + { + return input.LocalAddress; + } + + IReplyChannel reply = binder.Channel as IReplyChannel; + if (reply != null) + { + return reply.LocalAddress; + } + } + + return null; + } + } + + object ThisLock + { + get { return this; } + } + + //EventTraceActivity EventTraceActivity + //{ + // get + // { + // if (this.eventTraceActivity == null) + // { + // this.eventTraceActivity = new EventTraceActivity(); + // } + // return this.eventTraceActivity; + // } + //} + + internal static void Register(ChannelHandler handler) + { + handler.Register(); + } + + internal static void Register(ChannelHandler handler, RequestContext request) + { + BufferedReceiveBinder bufferedBinder = handler.Binder as BufferedReceiveBinder; + Fx.Assert(bufferedBinder != null, "ChannelHandler.Binder is not a BufferedReceiveBinder"); + + bufferedBinder.InjectRequest(request); + handler.Register(); + } + + void Register() + { + hasRegisterBeenCalled = true; + if (binder.Channel.State == CommunicationState.Created) + { + ActionItem.Schedule(openAndEnsurePump, this); + } + else + { + EnsurePump(); + } + } + + async void AsyncMessagePump() + { + for (;;) + { + requestInfo.Cleanup(); + + TryAsyncResult result; + do + { + result = await TryReceiveAsync(CancellationToken.None); + } while (!result.Success); + + var request = result.Result; + + if (!HandleRequest(request, null)) + { + break; + } + + if (!TryAcquirePump()) + { + break; + } + } + } + + //void AsyncMessagePump() + //{ + // IAsyncResult result = this.BeginTryReceive(); + + // if ((result != null) && result.CompletedSynchronously) + // { + // this.AsyncMessagePump(result); + // } + //} + + //void AsyncMessagePump(IAsyncResult result) + //{ + // //if (TD.ChannelReceiveStopIsEnabled()) + // //{ + // // TD.ChannelReceiveStop(this.EventTraceActivity, this.GetHashCode()); + // //} + + // for (;;) + // { + // RequestContext request; + + // while (!this.EndTryReceive(result, out request)) + // { + // result = this.BeginTryReceive(); + + // if ((result == null) || !result.CompletedSynchronously) + // { + // return; + // } + // } + + // if (!HandleRequest(request, null)) + // { + // break; + // } + + // if (!TryAcquirePump()) + // { + // break; + // } + + // result = this.BeginTryReceive(); + + // if (result == null || !result.CompletedSynchronously) + // { + // break; + // } + // } + //} + + bool DispatchAndReleasePump(RequestContext request, bool cleanThread, OperationContext currentOperationContext) + { + ServiceChannel channel = requestInfo.Channel; + EndpointDispatcher endpoint = requestInfo.Endpoint; + bool releasedPump = false; + + try + { + DispatchRuntime dispatchBehavior = requestInfo.DispatchRuntime; + + if (channel == null || dispatchBehavior == null) + { + Fx.Assert("CoreWCF.Dispatcher.ChannelHandler.Dispatch(): (channel == null || dispatchBehavior == null)"); + return true; + } + + MessageBuffer buffer = null; + Message message; + + //EventTraceActivity eventTraceActivity = TraceDispatchMessageStart(request.RequestMessage); + if (dispatchBehavior.PreserveMessage) + { + object previousBuffer = null; + if (request.RequestMessage.Properties.TryGetValue(MessageBufferPropertyName, out previousBuffer)) + { + buffer = (MessageBuffer)previousBuffer; + message = buffer.CreateMessage(); + } + else + { + // TODO, 34064, Need to fix this in clean way. + buffer = request.RequestMessage.CreateBufferedCopy(int.MaxValue); + message = buffer.CreateMessage(); + } + } + else + { + message = request.RequestMessage; + } + + DispatchOperationRuntime operation = dispatchBehavior.GetOperation(ref message); + if (operation == null) + { + Fx.Assert("ChannelHandler.Dispatch (operation == null)"); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "No DispatchOperationRuntime found to process message."))); + } + + if (shouldRejectMessageWithOnOpenActionHeader && message.Headers.Action == OperationDescription.SessionOpenedAction) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxNoEndpointMatchingAddressForConnectionOpeningMessage, message.Headers.Action, "Open"))); + } + + //if (MessageLogger.LoggingEnabled) + //{ + // MessageLogger.LogMessage(ref message, (operation.IsOneWay ? MessageLoggingSource.ServiceLevelReceiveDatagram : MessageLoggingSource.ServiceLevelReceiveRequest) | MessageLoggingSource.LastChance); + //} + + if (operation.IsTerminating && hasSession) + { + isChannelTerminated = true; + } + + bool hasOperationContextBeenSet; + if (currentOperationContext != null) + { + hasOperationContextBeenSet = true; + currentOperationContext.ReInit(request, message, channel); + } + else + { + hasOperationContextBeenSet = false; + currentOperationContext = new OperationContext(request, message, channel, host); + } + + if (dispatchBehavior.PreserveMessage) + { + currentOperationContext.IncomingMessageProperties.Add(MessageBufferPropertyName, buffer); + } + + if (currentOperationContext.EndpointDispatcher == null && listener != null) + { + currentOperationContext.EndpointDispatcher = endpoint; + } + + MessageRpc rpc = new MessageRpc(request, message, operation, channel, host, + /*this,*/ cleanThread, currentOperationContext, requestInfo.ExistingInstanceContext/*, eventTraceActivity*/); + + //TraceUtility.MessageFlowAtMessageReceived(message, currentOperationContext, eventTraceActivity, true); + + //rpc.TransactedBatchContext = this.transactedBatchContext; + + // passing responsibility for call throttle to MessageRpc + // (MessageRpc implicitly owns this throttle once it's created) + // explicitly passing responsibility for instance throttle to MessageRpc + + // These need to happen before Dispatch but after accessing any ChannelHandler + // state, because we go multi-threaded after this until we reacquire pump mutex. + ReleasePump(); + releasedPump = true; + + // Code here will be completely broken as I've removed the pause and resume mechanism. + operation.Parent.DispatchAsync(ref rpc, hasOperationContextBeenSet); + return true; + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + return HandleError(e, request, channel); + } + finally + { + if (!releasedPump) + { + ReleasePump(); + } + } + } + + internal void DispatchDone() + { + //if (this.throttle != null) + //{ + // this.throttle.DeactivateCall(); + //} + } + + RequestContext GetSessionOpenNotificationRequestContext() + { + Fx.Assert(sessionOpenNotification != null, "this.sessionOpenNotification should not be null."); + Message message = Message.CreateMessage(Binder.Channel.GetProperty(), OperationDescription.SessionOpenedAction); + Fx.Assert(LocalAddress != null, "this.LocalAddress should not be null."); + message.Headers.To = LocalAddress.Uri; + sessionOpenNotification.UpdateMessageProperties(message.Properties); + return Binder.CreateRequestContext(message); + } + + void EnsureChannelAndEndpoint(RequestContext request) + { + requestInfo.Channel = channel; + + if (requestInfo.Channel == null) + { + bool addressMatched; + if (hasSession) + { + requestInfo.Channel = GetSessionChannel(request.RequestMessage, out requestInfo.Endpoint, out addressMatched); + } + else + { + requestInfo.Channel = GetDatagramChannel(request.RequestMessage, out requestInfo.Endpoint, out addressMatched); + } + + if (requestInfo.Channel == null) + { + // TODO: Move the UnknownMessageReceived handler elsewhere and plumb in to here. + // Maybe define interface that user can add to DI and query for here. If it doesn't exist, do the default behavior. + //host.RaiseUnknownMessageReceived(request.RequestMessage); + if (addressMatched) + { + ReplyContractFilterDidNotMatch(request); + } + else + { + ReplyAddressFilterDidNotMatch(request); + } + } + } + else + { + requestInfo.Endpoint = requestInfo.Channel.EndpointDispatcher; + + //For sessionful contracts, the InstanceContext throttle is not copied over to the channel + //as we create the channel before acquiring the lock + //if (this.InstanceContextServiceThrottle != null && this.requestInfo.Channel.InstanceContextServiceThrottle == null) + //{ + // this.requestInfo.Channel.InstanceContextServiceThrottle = this.InstanceContextServiceThrottle; + //} + } + + requestInfo.EndpointLookupDone = true; + + if (requestInfo.Channel == null) + { + // SFx drops a message here + //TraceUtility.TraceDroppedMessage(request.RequestMessage, this.requestInfo.Endpoint); + request.CloseAsync().GetAwaiter().GetResult(); + return; + } + + if (requestInfo.Channel.HasSession || isCallback) + { + requestInfo.DispatchRuntime = requestInfo.Channel.DispatchRuntime; + } + else + { + requestInfo.DispatchRuntime = requestInfo.Endpoint.DispatchRuntime; + } + } + + void EnsurePump() + { + //if (null == this.sharedTransactedBatchContext || this.isMainTransactedBatchHandler) + //{ + if (TryAcquirePump()) + { + if (receiveSynchronously) + { + throw new PlatformNotSupportedException("No more receive sync"); + //ActionItem.Schedule(ChannelHandler.onStartSyncMessagePump, this); + } + else + { + //if (Thread.CurrentThread.IsThreadPoolThread) + //{ + using (TaskHelpers.RunTaskContinuationsOnOurThreads()) + { + AsyncMessagePump(); + } + //} + //else + //{ + // // Since this is not a threadpool thread, we don't know if this thread will exit + // // while the IO is still pending (which would cancel the IO), so we have to get + // // over to a threadpool thread which we know will not exit while there is pending IO. + // ActionItem.Schedule(ChannelHandler.onStartAsyncMessagePump, this); + //} + } + } + //} + //else + //{ + // ActionItem.Schedule(ChannelHandler.onStartSingleTransactedBatch, this); + //} + } + + ServiceChannel GetDatagramChannel(Message message, out EndpointDispatcher endpoint, out bool addressMatched) + { + addressMatched = false; + endpoint = GetEndpointDispatcher(message, out addressMatched); + + if (endpoint == null) + { + return null; + } + + if (endpoint.DatagramChannel == null) + { + lock (listener.ThisLock) + { + if (endpoint.DatagramChannel == null) + { + endpoint.DatagramChannel = null; //new ServiceChannel(binder, endpoint, listener.ChannelDispatcher, idleManager); + InitializeServiceChannel(endpoint.DatagramChannel); + } + } + } + + return endpoint.DatagramChannel; + } + + EndpointDispatcher GetEndpointDispatcher(Message message, out bool addressMatched) + { + return listener.Endpoints.Lookup(message, out addressMatched); + } + + ServiceChannel GetSessionChannel(Message message, out EndpointDispatcher endpoint, out bool addressMatched) + { + addressMatched = false; + + if (channel == null) + { + lock (ThisLock) + { + if (channel == null) + { + endpoint = GetEndpointDispatcher(message, out addressMatched); + if (endpoint != null) + { + channel = null; //new ServiceChannel(binder, endpoint, listener.ChannelDispatcher, idleManager); + InitializeServiceChannel(channel); + } + } + } + } + + if (channel == null) + { + endpoint = null; + } + else + { + endpoint = channel.EndpointDispatcher; + } + return channel; + } + + void InitializeServiceChannel(ServiceChannel channel) + { + //if (this.wasChannelThrottled) + //{ + // // TFS#500703, when the idle timeout was hit, the constructor of ServiceChannel will abort itself directly. So + // // the session throttle will not be released and thus lead to a service unavailablity. + // // Note that if the channel is already aborted, the next line "channel.ServiceThrottle = this.throttle;" will throw an exception, + // // so we are not going to do any more work inside this method. + // // Ideally we should do a thorough refactoring work for this throttling issue. However, it's too risky as a QFE. We should consider + // // this in a whole release. + // // Note that the "wasChannelThrottled" boolean will only be true if we aquired the session throttle. So we don't have to check HasSession + // // again here. + // if (channel.Aborted && this.throttle != null) + // { + // // This line will release the "session" throttle. + // this.throttle.DeactivateChannel(); + // } + + // channel.ServiceThrottle = this.throttle; + //} + + //if (this.InstanceContextServiceThrottle != null) + //{ + // channel.InstanceContextServiceThrottle = this.InstanceContextServiceThrottle; + //} + + ClientRuntime clientRuntime = channel.ClientRuntime; + if (clientRuntime != null) + { + Type contractType = clientRuntime.ContractClientType; + Type callbackType = clientRuntime.CallbackClientType; + + if (contractType != null) + { + channel.Proxy = ServiceChannelFactory.CreateProxy(contractType, callbackType, MessageDirection.Output, channel); + } + } + + if (listener != null) + { + //listener.ChannelDispatcher.InitializeChannel((IClientChannel)channel.Proxy); + } + + ((IChannel)channel).OpenAsync().GetAwaiter().GetResult(); + } + + void ProvideFault(Exception e, ref ErrorHandlerFaultInfo faultInfo) + { + if (listener != null) + { + listener.ChannelDispatcher.ProvideFault(e, requestInfo.Channel == null ? binder.Channel.GetProperty() : requestInfo.Channel.GetProperty(), ref faultInfo); + } + else if (channel != null) + { + DispatchRuntime dispatchBehavior = channel.ClientRuntime.CallbackDispatchRuntime; + dispatchBehavior.ChannelDispatcher.ProvideFault(e, channel.GetProperty(), ref faultInfo); + } + } + + internal bool HandleError(Exception e) + { + ErrorHandlerFaultInfo dummy = new ErrorHandlerFaultInfo(); + return HandleError(e, ref dummy); + } + + bool HandleError(Exception e, ref ErrorHandlerFaultInfo faultInfo) + { + if (e == null) + { + Fx.Assert(SR.SFxNonExceptionThrown); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.SFxNonExceptionThrown)); + } + if (listener != null) + { + return listener.ChannelDispatcher.HandleError(e, ref faultInfo); + } + else if (channel != null) + { + return channel.ClientRuntime.CallbackDispatchRuntime.ChannelDispatcher.HandleError(e, ref faultInfo); + } + else + { + return false; + } + } + + bool HandleError(Exception e, RequestContext request, ServiceChannel channel) + { + ErrorHandlerFaultInfo faultInfo = new ErrorHandlerFaultInfo(messageVersion.Addressing.DefaultFaultAction); + bool replied, replySentAsync; + ProvideFaultAndReplyFailure(request, e, ref faultInfo, out replied, out replySentAsync); + + if (!replySentAsync) + { + return HandleErrorContinuation(e, request, channel, ref faultInfo, replied); + } + else + { + return false; + } + } + + bool HandleErrorContinuation(Exception e, RequestContext request, ServiceChannel channel, ref ErrorHandlerFaultInfo faultInfo, bool replied) + { + if (replied) + { + try + { + request.CloseAsync().GetAwaiter().GetResult(); + } + catch (Exception e1) + { + if (Fx.IsFatal(e1)) + { + throw; + } + HandleError(e1); + } + } + else + { + request.Abort(); + } + if (!HandleError(e, ref faultInfo) && hasSession) + { + if (channel != null) + { + if (replied) + { + TimeoutHelper timeoutHelper = new TimeoutHelper(CloseAfterFaultTimeout); + try + { + channel.CloseAsync(timeoutHelper.GetCancellationToken()).GetAwaiter().GetResult(); + } + catch (Exception e2) + { + if (Fx.IsFatal(e2)) + { + throw; + } + HandleError(e2); + } + try + { + binder.CloseAfterFault(timeoutHelper.RemainingTime()); + } + catch (Exception e3) + { + if (Fx.IsFatal(e3)) + { + throw; + } + HandleError(e3); + } + } + else + { + channel.Abort(); + binder.Abort(); + } + } + else + { + if (replied) + { + try + { + binder.CloseAfterFault(CloseAfterFaultTimeout); + } + catch (Exception e4) + { + if (Fx.IsFatal(e4)) + { + throw; + } + HandleError(e4); + } + } + else + { + binder.Abort(); + } + } + } + + return true; + } + + async Task HandleReceiveCompleteAsync(RequestContext context) + { + try + { + if (channel != null) + { + channel.HandleReceiveComplete(context); + } + else + { + if (context == null && hasSession) + { + bool close; + lock (ThisLock) + { + close = !doneReceiving; + doneReceiving = true; + } + + if (close) + { + await receiver.CloseAsync(); + + if (idleManager != null) + { + idleManager.CancelTimer(); + } + + //ServiceThrottle throttle = this.throttle; + //if (throttle != null) + //{ + // throttle.DeactivateChannel(); + //} + } + } + } + } + finally + { + if ((context == null) && incrementedActivityCountInConstructor) + { + //listener.ChannelDispatcher.Channels.DecrementActivityCount(); + } + } + } + + bool HandleRequest(RequestContext request, OperationContext currentOperationContext) + { + if (request == null) + { + // channel EOF, stop receiving + return false; + } + + //ServiceModelActivity activity = DiagnosticUtility.ShouldUseActivity ? TraceUtility.ExtractActivity(request) : null; + + //using (ServiceModelActivity.BoundOperation(activity)) + //{ + if (HandleRequestAsReply(request)) + { + ReleasePump(); + return true; + } + + if (isChannelTerminated) + { + ReleasePump(); + ReplyChannelTerminated(request); + return true; + } + + if (requestInfo.RequestContext != null) + { + Fx.Assert("ChannelHandler.HandleRequest: this.requestInfo.RequestContext != null"); + } + + requestInfo.RequestContext = request; + + + if (!TryRetrievingInstanceContext(request)) + { + //Would have replied and close the request. + return true; + } + + requestInfo.Channel.CompletedIOOperation(); + + if (!DispatchAndReleasePump(request, true, currentOperationContext)) + { + // this.DispatchDone will be called to continue + return false; + } + //} + return true; + } + + bool HandleRequestAsReply(RequestContext request) + { + if (duplexBinder != null) + { + if (duplexBinder.HandleRequestAsReply(request.RequestMessage)) + { + return true; + } + } + return false; + } + + static void OnStartAsyncMessagePump(object state) + { + ((ChannelHandler)state).AsyncMessagePump(); + } + + static void OpenAndEnsurePump(object state) + { + ((ChannelHandler)state).OpenAndEnsurePump(); + } + + void OpenAndEnsurePump() + { + Exception exception = null; + try + { + binder.Channel.OpenAsync().GetAwaiter().GetResult(); + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + exception = e; + } + + if (exception != null) + { + //if (DiagnosticUtility.ShouldTraceWarning) + //{ + // TraceUtility.TraceEvent(TraceEventType.Warning, + // TraceCode.FailedToOpenIncomingChannel, + // SR.TraceCodeFailedToOpenIncomingChannel); + //} + ServiceChannel.SessionIdleManager idleManager = this.idleManager; + if (idleManager != null) + { + idleManager.CancelTimer(); + } + //if ((this.throttle != null) && this.hasSession) + //{ + // this.throttle.DeactivateChannel(); + //} + + bool errorHandled = HandleError(exception); + + if (incrementedActivityCountInConstructor) + { + //listener.ChannelDispatcher.Channels.DecrementActivityCount(); + } + + if (!errorHandled) + { + binder.Channel.Abort(); + } + } + else + { + EnsurePump(); + } + } + + private async Task> TryReceiveAsync(CancellationToken token) + { + TryAsyncResult result; + shouldRejectMessageWithOnOpenActionHeader = !needToCreateSessionOpenNotificationMessage; + + if (needToCreateSessionOpenNotificationMessage) + { + needToCreateSessionOpenNotificationMessage = false; + result = TryAsyncResult.FromResult(GetSessionOpenNotificationRequestContext()); + } + else + { + result = await receiver.TryReceiveAsync(token); + } + + if (result.Success) + { + await HandleReceiveCompleteAsync(result.Result); + } + + return result; + } + + void ReplyAddressFilterDidNotMatch(RequestContext request) + { + FaultCode code = FaultCode.CreateSenderFaultCode(AddressingStrings.DestinationUnreachable, + messageVersion.Addressing.Namespace); + string reason = SR.Format(SR.SFxNoEndpointMatchingAddress, request.RequestMessage.Headers.To); + + ReplyFailure(request, code, reason); + } + + void ReplyContractFilterDidNotMatch(RequestContext request) + { + // By default, the contract filter is just a filter over the set of initiating actions in + // the contract, so we do error messages accordingly + AddressingVersion addressingVersion = messageVersion.Addressing; + if (addressingVersion != AddressingVersion.None && request.RequestMessage.Headers.Action == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new MessageHeaderException( + SR.Format(SR.SFxMissingActionHeader, addressingVersion.Namespace), AddressingStrings.Action, addressingVersion.Namespace)); + } + else + { + // some of this code is duplicated in DispatchRuntime.UnhandledActionInvoker + // ideally both places would use FaultConverter and ActionNotSupportedException + FaultCode code = FaultCode.CreateSenderFaultCode(AddressingStrings.ActionNotSupported, + messageVersion.Addressing.Namespace); + string reason = SR.Format(SR.SFxNoEndpointMatchingContract, request.RequestMessage.Headers.Action); + ReplyFailure(request, code, reason, messageVersion.Addressing.FaultAction); + } + } + + void ReplyChannelTerminated(RequestContext request) + { + FaultCode code = FaultCode.CreateSenderFaultCode(FaultCodeConstants.Codes.SessionTerminated, + FaultCodeConstants.Namespaces.NetDispatch); + string reason = SR.SFxChannelTerminated0; + string action = FaultCodeConstants.Actions.NetDispatcher; + Message fault = Message.CreateMessage(messageVersion, code, reason, action); + ReplyFailure(request, fault, action, reason, code); + } + + void ReplyFailure(RequestContext request, FaultCode code, string reason) + { + string action = messageVersion.Addressing.DefaultFaultAction; + ReplyFailure(request, code, reason, action); + } + + void ReplyFailure(RequestContext request, FaultCode code, string reason, string action) + { + Message fault = Message.CreateMessage(messageVersion, code, reason, action); + ReplyFailure(request, fault, action, reason, code); + } + + void ReplyFailure(RequestContext request, Message fault, string action, string reason, FaultCode code) + { + FaultException exception = new FaultException(reason, code); + ErrorBehavior.ThrowAndCatch(exception); + ErrorHandlerFaultInfo faultInfo = new ErrorHandlerFaultInfo(action); + faultInfo.Fault = fault; + bool replied, replySentAsync; + ProvideFaultAndReplyFailure(request, exception, ref faultInfo, out replied, out replySentAsync); + HandleError(exception, ref faultInfo); + } + + void ProvideFaultAndReplyFailure(RequestContext request, Exception exception, ref ErrorHandlerFaultInfo faultInfo, out bool replied, out bool replySentAsync) + { + replied = false; + replySentAsync = false; + bool requestMessageIsFault = false; + try + { + requestMessageIsFault = request.RequestMessage.IsFault; + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + // swallow it + } + + bool enableFaults = false; + if (listener != null) + { + enableFaults = listener.ChannelDispatcher.EnableFaults; + } + + if ((!requestMessageIsFault) && enableFaults) + { + ProvideFault(exception, ref faultInfo); + if (faultInfo.Fault != null) + { + Message reply = faultInfo.Fault; + try + { + try + { + if (PrepareReply(request, reply)) + { + if (sendAsynchronously) + { + var state = new ContinuationState { ChannelHandler = this, Channel = channel, Exception = exception, FaultInfo = faultInfo, Request = request, Reply = reply }; + var result = request.ReplyAsync(reply); + result.ContinueWith(AsyncReplyComplete, state); + replied = result.IsCompleted; + replySentAsync = !replied; + } + else + { + request.ReplyAsync(reply).GetAwaiter().GetResult(); + replied = true; + } + } + } + finally + { + if (!replySentAsync) + { + reply.Close(); + } + } + } + + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + HandleError(e); + } + } + } + } + + /// + /// Prepares a reply that can either be sent asynchronously or synchronously depending on the value of + /// sendAsynchronously + /// + /// The request context to prepare + /// The reply to prepare + /// True if channel is open and prepared reply should be sent; otherwise false. + bool PrepareReply(RequestContext request, Message reply) + { + // Ensure we only reply once (we may hit the same error multiple times) + if (replied == request) + { + return false; + } + replied = request; + + bool canSendReply = true; + + Message requestMessage = null; + try + { + requestMessage = request.RequestMessage; + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + // swallow it + } + if (!object.ReferenceEquals(requestMessage, null)) + { + UniqueId requestID = null; + try + { + requestID = requestMessage.Headers.MessageId; + } + catch (MessageHeaderException) + { + // swallow it - we don't need to correlate the reply if the MessageId header is bad + } + if (!object.ReferenceEquals(requestID, null) && !isManualAddressing) + { + RequestReplyCorrelator.PrepareReply(reply, requestID); + } + if (!hasSession && !isManualAddressing) + { + try + { + canSendReply = RequestReplyCorrelator.AddressReply(reply, requestMessage); + } + catch (MessageHeaderException) + { + // swallow it - we don't need to address the reply if the FaultTo header is bad + } + } + } + + // ObjectDisposeException can happen + // if the channel is closed in a different + // thread. 99% this check will avoid false + // exceptions. + return IsOpen && canSendReply; + } + + static void AsyncReplyComplete(Task result, object obj) + { + ContinuationState state = (ContinuationState)obj; + try + { + result.GetAwaiter().GetResult(); + } + catch (Exception e) + { + DiagnosticUtility.TraceHandledException(e, TraceEventType.Error); + + if (Fx.IsFatal(e)) + { + throw; + } + + state.ChannelHandler.HandleError(e); + } + + try + { + state.Reply.Close(); + } + catch (Exception e) + { + DiagnosticUtility.TraceHandledException(e, TraceEventType.Error); + + if (Fx.IsFatal(e)) + { + throw; + } + + state.ChannelHandler.HandleError(e); + } + + try + { + state.ChannelHandler.HandleErrorContinuation(state.Exception, state.Request, state.Channel, ref state.FaultInfo, true); + } + catch (Exception e) + { + DiagnosticUtility.TraceHandledException(e, TraceEventType.Error); + + if (Fx.IsFatal(e)) + { + throw; + } + + state.ChannelHandler.HandleError(e); + } + + state.ChannelHandler.EnsurePump(); + } + + void ReleasePump() + { + if (isConcurrent) + { + Interlocked.Exchange(ref isPumpAcquired, 0); + } + } + + // TODO: Convert to async for close code path + bool TryRetrievingInstanceContext(RequestContext request) + { + try + { + return TryRetrievingInstanceContextCore(request); + } + catch (Exception ex) + { + if (Fx.IsFatal(ex)) + { + throw; + } + + DiagnosticUtility.TraceHandledException(ex, TraceEventType.Error); + + try + { + request.CloseAsync().GetAwaiter().GetResult(); + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + + request.Abort(); + } + + return false; + } + } + + //Return: False denotes failure, Caller should discard the request. + // : True denotes operation is sucessful. + bool TryRetrievingInstanceContextCore(RequestContext request) + { + bool releasePump = true; + try + { + if (!requestInfo.EndpointLookupDone) + { + EnsureChannelAndEndpoint(request); + } + + if (requestInfo.Channel == null) + { + return false; + } + + if (requestInfo.DispatchRuntime != null) + { + IContextChannel transparentProxy = requestInfo.Channel.Proxy as IContextChannel; + try + { + requestInfo.ExistingInstanceContext = requestInfo.DispatchRuntime.InstanceContextProvider.GetExistingInstanceContext(request.RequestMessage, transparentProxy); + releasePump = false; + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + requestInfo.Channel = null; + HandleError(e, request, channel); + return false; + } + } + else + { + // This can happen if we are pumping for an async client, + // and we receive a bogus reply. In that case, there is no + // DispatchRuntime, because we are only expecting replies. + // + // One possible fix for this would be in DuplexChannelBinder + // to drop all messages with a RelatesTo that do not match a + // pending request. + // + // However, that would not fix: + // (a) we could get a valid request message with a + // RelatesTo that we should try to process. + // (b) we could get a reply message that does not have + // a RelatesTo. + // + // So we do the null check here. + // + // SFx drops a message here + TraceUtility.TraceDroppedMessage(request.RequestMessage, requestInfo.Endpoint); + request.CloseAsync().GetAwaiter().GetResult(); + return false; + } + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + + HandleError(e, request, channel); + + return false; + } + finally + { + if (releasePump) + { + ReleasePump(); + } + } + return true; + } + + bool TryAcquirePump() + { + if (isConcurrent) + { + return Interlocked.CompareExchange(ref isPumpAcquired, 1, 0) == 0; + } + + return true; + } + + struct RequestInfo + { + public EndpointDispatcher Endpoint; + public InstanceContext ExistingInstanceContext; + public ServiceChannel Channel; + public bool EndpointLookupDone; + public DispatchRuntime DispatchRuntime; + public RequestContext RequestContext; + public ChannelHandler ChannelHandler; + + public RequestInfo(ChannelHandler channelHandler) + { + Endpoint = null; + ExistingInstanceContext = null; + Channel = null; + EndpointLookupDone = false; + DispatchRuntime = null; + RequestContext = null; + ChannelHandler = channelHandler; + } + + public void Cleanup() + { + Endpoint = null; + ExistingInstanceContext = null; + Channel = null; + EndpointLookupDone = false; + RequestContext = null; + } + } + + //EventTraceActivity TraceDispatchMessageStart(Message message) + //{ + // if (FxTrace.Trace.IsEnd2EndActivityTracingEnabled && message != null) + // { + // EventTraceActivity eventTraceActivity = EventTraceActivityHelper.TryExtractActivity(message); + // if (TD.DispatchMessageStartIsEnabled()) + // { + // TD.DispatchMessageStart(eventTraceActivity); + // } + // return eventTraceActivity; + // } + + // return null; + //} + + /// + /// Data structure used to carry state for asynchronous replies + /// + struct ContinuationState + { + public ChannelHandler ChannelHandler; + public Exception Exception; + public RequestContext Request; + public Message Reply; + public ServiceChannel Channel; + public ErrorHandlerFaultInfo FaultInfo; + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ClientOperation.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ClientOperation.cs new file mode 100644 index 000000000..f4507ab04 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ClientOperation.cs @@ -0,0 +1,283 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Reflection; +using CoreWCF.Collections.Generic; + +namespace CoreWCF.Dispatcher +{ + public sealed class ClientOperation + { + string action; + SynchronizedCollection faultContractInfos; + bool serializeRequest; + bool deserializeReply; + IClientMessageFormatter formatter; + IClientFaultFormatter faultFormatter; + bool isInitiating = true; + bool isOneWay; + bool isTerminating; + bool isSessionOpenNotificationEnabled; + string name; + + ClientRuntime parent; + string replyAction; + MethodInfo beginMethod; + MethodInfo endMethod; + MethodInfo syncMethod; + MethodInfo taskMethod; + Type taskTResult; + bool isFaultFormatterSetExplicit = false; + private SynchronizedCollection parameterInspectors; + + public ClientOperation(ClientRuntime parent, string name, string action) + : this(parent, name, action, null) + { + } + + public ClientOperation(ClientRuntime parent, string name, string action, string replyAction) + { + if (parent == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("parent"); + + if (name == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("name"); + + this.parent = parent; + this.name = name; + this.action = action; + this.replyAction = replyAction; + + faultContractInfos = parent.NewBehaviorCollection(); + parameterInspectors = parent.NewBehaviorCollection(); + } + + public string Action + { + get { return action; } + } + + internal SynchronizedCollection FaultContractInfos + { + get { return faultContractInfos; } + } + + public MethodInfo BeginMethod + { + get { return beginMethod; } + set + { + lock (parent.ThisLock) + { + parent.InvalidateRuntime(); + beginMethod = value; + } + } + } + + public MethodInfo EndMethod + { + get { return endMethod; } + set + { + lock (parent.ThisLock) + { + parent.InvalidateRuntime(); + endMethod = value; + } + } + } + + public MethodInfo SyncMethod + { + get { return syncMethod; } + set + { + lock (parent.ThisLock) + { + parent.InvalidateRuntime(); + syncMethod = value; + } + } + } + + public IClientMessageFormatter Formatter + { + get { return formatter; } + set + { + lock (parent.ThisLock) + { + parent.InvalidateRuntime(); + formatter = value; + } + } + } + + internal IClientFaultFormatter FaultFormatter + { + get + { + if (faultFormatter == null) + { + faultFormatter = new DataContractSerializerFaultFormatter(faultContractInfos); + } + return faultFormatter; + } + set + { + lock (parent.ThisLock) + { + parent.InvalidateRuntime(); + faultFormatter = value; + isFaultFormatterSetExplicit = true; + } + } + } + + internal bool IsFaultFormatterSetExplicit + { + get + { + return isFaultFormatterSetExplicit; + } + } + + internal IClientMessageFormatter InternalFormatter + { + get { return formatter; } + set { formatter = value; } + } + + public bool IsInitiating + { + get { return isInitiating; } + set + { + lock (parent.ThisLock) + { + parent.InvalidateRuntime(); + isInitiating = value; + } + } + } + + public bool IsOneWay + { + get { return isOneWay; } + set + { + lock (parent.ThisLock) + { + parent.InvalidateRuntime(); + isOneWay = value; + } + } + } + + public bool IsTerminating + { + get { return isTerminating; } + set + { + lock (parent.ThisLock) + { + parent.InvalidateRuntime(); + isTerminating = value; + } + } + } + + public string Name + { + get { return name; } + } + + public ICollection ClientParameterInspectors + { + get { return ParameterInspectors; } + } + + internal SynchronizedCollection ParameterInspectors + { + get { return parameterInspectors; } + } + + public ClientRuntime Parent + { + get { return parent; } + } + + public string ReplyAction + { + get { return replyAction; } + } + + public bool SerializeRequest + { + get { return serializeRequest; } + set + { + lock (parent.ThisLock) + { + parent.InvalidateRuntime(); + serializeRequest = value; + } + } + } + + public bool DeserializeReply + { + get { return deserializeReply; } + set + { + lock (parent.ThisLock) + { + parent.InvalidateRuntime(); + deserializeReply = value; + } + } + } + + public MethodInfo TaskMethod + { + get { return taskMethod; } + set + { + lock (parent.ThisLock) + { + parent.InvalidateRuntime(); + taskMethod = value; + } + } + } + + public Type TaskTResult + { + get { return taskTResult; } + set + { + lock (parent.ThisLock) + { + parent.InvalidateRuntime(); + taskTResult = value; + } + } + } + + internal bool IsSessionOpenNotificationEnabled + { + get { return isSessionOpenNotificationEnabled; } + set + { + lock (parent.ThisLock) + { + parent.InvalidateRuntime(); + isSessionOpenNotificationEnabled = value; + } + } + } + + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ClientRuntime.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ClientRuntime.cs new file mode 100644 index 000000000..4eb010674 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ClientRuntime.cs @@ -0,0 +1,561 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Xml; +using CoreWCF.Collections.Generic; +using CoreWCF.Runtime; +using CoreWCF.Channels; +using CoreWCF.Security; + +namespace CoreWCF.Dispatcher +{ + public sealed class ClientRuntime + { + //bool addTransactionFlowProperties = true; + internal SynchronizedCollection messageInspectors; + internal SynchronizedKeyedCollection operations; + Type callbackProxyType; + ProxyBehaviorCollection channelInitializers; + string contractName; + string contractNamespace; + Type contractProxyType; + DispatchRuntime dispatchRuntime; + IdentityVerifier identityVerifier; + + IClientOperationSelector operationSelector; + ImmutableClientRuntime runtime; + ClientOperation unhandled; + bool useSynchronizationContext = true; + Uri via; + SharedRuntimeState shared; + int maxFaultSize; + bool messageVersionNoneFaultsEnabled; + + internal ClientRuntime(DispatchRuntime dispatchRuntime, SharedRuntimeState shared) + : this(dispatchRuntime.EndpointDispatcher.ContractName, + dispatchRuntime.EndpointDispatcher.ContractNamespace, + shared) + { + if (dispatchRuntime == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("dispatchRuntime"); + + this.dispatchRuntime = dispatchRuntime; + this.shared = shared; + + Fx.Assert(shared.IsOnServer, "Server constructor called on client?"); + } + + internal ClientRuntime(string contractName, string contractNamespace) + : this(contractName, contractNamespace, new SharedRuntimeState(false)) + { + Fx.Assert(!shared.IsOnServer, "Client constructor called on server?"); + } + + ClientRuntime(string contractName, string contractNamespace, SharedRuntimeState shared) + { + this.contractName = contractName; + this.contractNamespace = contractNamespace; + this.shared = shared; + + OperationCollection operations = new OperationCollection(this); + this.operations = operations; + channelInitializers = new ProxyBehaviorCollection(this); + messageInspectors = new ProxyBehaviorCollection(this); + + unhandled = new ClientOperation(this, "*", MessageHeaders.WildcardAction, MessageHeaders.WildcardAction); + unhandled.InternalFormatter = new MessageOperationFormatter(); + maxFaultSize = TransportDefaults.MaxFaultSize; + } + + //internal bool AddTransactionFlowProperties + //{ + // get { return this.addTransactionFlowProperties; } + // set + // { + // lock (this.ThisLock) + // { + // this.InvalidateRuntime(); + // this.addTransactionFlowProperties = value; + // } + // } + //} + + public Type CallbackClientType + { + get { return callbackProxyType; } + set + { + lock (ThisLock) + { + InvalidateRuntime(); + callbackProxyType = value; + } + } + } + + internal SynchronizedCollection ChannelInitializers + { + get { return channelInitializers; } + } + + public string ContractName + { + get { return contractName; } + } + + public string ContractNamespace + { + get { return contractNamespace; } + } + + public Type ContractClientType + { + get { return contractProxyType; } + set + { + lock (ThisLock) + { + InvalidateRuntime(); + contractProxyType = value; + } + } + } + + internal IdentityVerifier IdentityVerifier + { + get + { + if (identityVerifier == null) + { + identityVerifier = IdentityVerifier.CreateDefault(); + } + + return identityVerifier; + } + set + { + if (value == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value"); + } + InvalidateRuntime(); + + identityVerifier = value; + } + } + + public Uri Via + { + get { return via; } + set + { + lock (ThisLock) + { + InvalidateRuntime(); + via = value; + } + } + } + + public bool ValidateMustUnderstand + { + get { return shared.ValidateMustUnderstand; } + set + { + lock (ThisLock) + { + InvalidateRuntime(); + shared.ValidateMustUnderstand = value; + } + } + } + + public bool MessageVersionNoneFaultsEnabled + { + get + { + return messageVersionNoneFaultsEnabled; + } + set + { + InvalidateRuntime(); + messageVersionNoneFaultsEnabled = value; + } + } + + internal DispatchRuntime DispatchRuntime + { + get { return dispatchRuntime; } + } + + public DispatchRuntime CallbackDispatchRuntime + { + get + { + if (dispatchRuntime == null) + dispatchRuntime = new DispatchRuntime(this, shared); + + return dispatchRuntime; + } + } + + internal bool EnableFaults + { + get + { + if (IsOnServer) + { + return dispatchRuntime.EnableFaults; + } + else + { + return shared.EnableFaults; + } + } + set + { + lock (ThisLock) + { + if (IsOnServer) + { + string text = SR.SFxSetEnableFaultsOnChannelDispatcher0; + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(text)); + } + else + { + InvalidateRuntime(); + shared.EnableFaults = value; + } + } + } + } + + public int MaxFaultSize + { + get + { + return maxFaultSize; + } + set + { + InvalidateRuntime(); + maxFaultSize = value; + } + } + + internal bool IsOnServer + { + get { return shared.IsOnServer; } + } + + public bool ManualAddressing + { + get + { + if (IsOnServer) + { + return dispatchRuntime.ManualAddressing; + } + else + { + return shared.ManualAddressing; + } + } + set + { + lock (ThisLock) + { + if (IsOnServer) + { + string text = SR.SFxSetManualAddresssingOnChannelDispatcher0; + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(text)); + } + else + { + InvalidateRuntime(); + shared.ManualAddressing = value; + } + } + } + } + + internal int MaxParameterInspectors + { + get + { + lock (ThisLock) + { + int max = 0; + + for (int i = 0; i < operations.Count; i++) + max = System.Math.Max(max, operations[i].ParameterInspectors.Count); + + return max; + } + } + } + + internal ICollection ClientMessageInspectors + { + get { return MessageInspectors; } + } + + internal SynchronizedCollection MessageInspectors + { + get { return messageInspectors; } + } + + public ICollection ClientOperations + { + get { return Operations; } + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public SynchronizedKeyedCollection Operations + { + get { return operations; } + } + + internal IClientOperationSelector OperationSelector + { + get { return operationSelector; } + set + { + lock (ThisLock) + { + InvalidateRuntime(); + operationSelector = value; + } + } + } + + internal object ThisLock + { + get { return shared; } + } + + public ClientOperation UnhandledClientOperation + { + get { return unhandled; } + } + + internal bool UseSynchronizationContext + { + get { return useSynchronizationContext; } + set + { + lock (ThisLock) + { + InvalidateRuntime(); + useSynchronizationContext = value; + } + } + } + + internal T[] GetArray(SynchronizedCollection collection) + { + lock (collection.SyncRoot) + { + if (collection.Count == 0) + { + return Array.Empty(); + } + else + { + T[] array = new T[collection.Count]; + collection.CopyTo(array, 0); + return array; + } + } + } + + internal ImmutableClientRuntime GetRuntime() + { + lock (ThisLock) + { + if (runtime == null) + runtime = new ImmutableClientRuntime(this); + + return runtime; + } + } + + internal void InvalidateRuntime() + { + lock (ThisLock) + { + shared.ThrowIfImmutable(); + runtime = null; + } + } + + internal void LockDownProperties() + { + shared.LockDownProperties(); + } + + internal SynchronizedCollection NewBehaviorCollection() + { + return new ProxyBehaviorCollection(this); + } + + internal bool IsFault(ref Message reply) + { + if (reply == null) + { + return false; + } + if (reply.IsFault) + { + return true; + } + if (MessageVersionNoneFaultsEnabled && IsMessageVersionNoneFault(ref reply, MaxFaultSize)) + { + return true; + } + + return false; + } + + internal static bool IsMessageVersionNoneFault(ref Message message, int maxFaultSize) + { + if (message.Version != MessageVersion.None || message.IsEmpty) + { + return false; + } + //HttpResponseMessageProperty prop = message.Properties[HttpResponseMessageProperty.Name] as HttpResponseMessageProperty; + //if (prop == null || prop.StatusCode != HttpStatusCode.InternalServerError) + //{ + // return false; + //} + using (MessageBuffer buffer = message.CreateBufferedCopy(maxFaultSize)) + { + message.Close(); + message = buffer.CreateMessage(); + using (Message copy = buffer.CreateMessage()) + { + using (XmlDictionaryReader reader = copy.GetReaderAtBodyContents()) + { + return reader.IsStartElement(XD.MessageDictionary.Fault, MessageVersion.None.Envelope.DictionaryNamespace); + } + } + } + } + + class ProxyBehaviorCollection : SynchronizedCollection + { + ClientRuntime outer; + + internal ProxyBehaviorCollection(ClientRuntime outer) + : base(outer.ThisLock) + { + this.outer = outer; + } + + protected override void ClearItems() + { + outer.InvalidateRuntime(); + base.ClearItems(); + } + + protected override void InsertItem(int index, T item) + { + if (item == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("item"); + } + + outer.InvalidateRuntime(); + base.InsertItem(index, item); + } + + protected override void RemoveItem(int index) + { + outer.InvalidateRuntime(); + base.RemoveItem(index); + } + + protected override void SetItem(int index, T item) + { + if (item == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("item"); + } + + outer.InvalidateRuntime(); + base.SetItem(index, item); + } + } + + class OperationCollection : SynchronizedKeyedCollection + { + ClientRuntime outer; + + internal OperationCollection(ClientRuntime outer) + : base(outer.ThisLock) + { + this.outer = outer; + } + + protected override void ClearItems() + { + outer.InvalidateRuntime(); + base.ClearItems(); + } + + protected override string GetKeyForItem(ClientOperation item) + { + return item.Name; + } + + protected override void InsertItem(int index, ClientOperation item) + { + if (item == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("item"); + if (item.Parent != outer) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.SFxMismatchedOperationParent); + + outer.InvalidateRuntime(); + base.InsertItem(index, item); + } + + protected override void RemoveItem(int index) + { + outer.InvalidateRuntime(); + base.RemoveItem(index); + } + + protected override void SetItem(int index, ClientOperation item) + { + if (item == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("item"); + if (item.Parent != outer) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.SFxMismatchedOperationParent); + + outer.InvalidateRuntime(); + base.SetItem(index, item); + } + + internal void InternalClearItems() { ClearItems(); } + internal string InternalGetKeyForItem(ClientOperation item) { return GetKeyForItem(item); } + internal void InternalInsertItem(int index, ClientOperation item) { InsertItem(index, item); } + internal void InternalRemoveItem(int index) { RemoveItem(index); } + internal void InternalSetItem(int index, ClientOperation item) { SetItem(index, item); } + } + + + class OperationCollectionWrapper : KeyedCollection + { + OperationCollection inner; + internal OperationCollectionWrapper(OperationCollection inner) { this.inner = inner; } + protected override void ClearItems() { inner.InternalClearItems(); } + protected override string GetKeyForItem(ClientOperation item) { return inner.InternalGetKeyForItem(item); } + protected override void InsertItem(int index, ClientOperation item) { inner.InternalInsertItem(index, item); } + protected override void RemoveItem(int index) { inner.InternalRemoveItem(index); } + protected override void SetItem(int index, ClientOperation item) { inner.InternalSetItem(index, item); } + } + + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ConcurrencyBehavior.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ConcurrencyBehavior.cs new file mode 100644 index 000000000..4b65cc6de --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ConcurrencyBehavior.cs @@ -0,0 +1,298 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using CoreWCF.Runtime; + +namespace CoreWCF.Dispatcher +{ + internal class ConcurrencyBehavior + { + ConcurrencyMode concurrencyMode; + bool enforceOrderedReceive; + + internal ConcurrencyBehavior(DispatchRuntime runtime) + { + concurrencyMode = runtime.ConcurrencyMode; + enforceOrderedReceive = runtime.EnsureOrderedDispatch; + //this.supportsTransactedBatch = ConcurrencyBehavior.SupportsTransactedBatch(runtime.ChannelDispatcher); + } + + internal bool IsConcurrent(ref MessageRpc rpc) + { + return IsConcurrent(concurrencyMode, enforceOrderedReceive, rpc.Channel.HasSession/*, this.supportsTransactedBatch*/); + } + + internal static bool IsConcurrent(ConcurrencyMode concurrencyMode, bool ensureOrderedDispatch, bool hasSession /*, bool supportsTransactedBatch*/) + { + //if (supportsTransactedBatch) + //{ + // return false; + //} + + if (concurrencyMode != ConcurrencyMode.Single) + { + return true; + } + + if (hasSession) + { + return false; + } + + if (ensureOrderedDispatch) + { + return false; + } + + return true; + } + + internal static bool IsConcurrent(ChannelDispatcher runtime, bool hasSession) + { + bool isConcurrencyModeSingle = true; + + //if (ConcurrencyBehavior.SupportsTransactedBatch(runtime)) + //{ + // return false; + //} + + foreach (EndpointDispatcher endpointDispatcher in runtime.Endpoints) + { + if (endpointDispatcher.DispatchRuntime.EnsureOrderedDispatch) + { + return false; + } + + if (endpointDispatcher.DispatchRuntime.ConcurrencyMode != ConcurrencyMode.Single) + { + isConcurrencyModeSingle = false; + } + } + + if (!isConcurrencyModeSingle) + { + return true; + } + + if (!hasSession) + { + return true; + } + + return false; + } + + internal async Task LockInstanceAsync(MessageRpc rpc) + { + if (concurrencyMode != ConcurrencyMode.Multiple) + { + ConcurrencyInstanceContextFacet resource = rpc.InstanceContext.Concurrency; + bool needToWait = false; + lock (rpc.InstanceContext.ThisLock) + { + if (!resource.Locked) + { + resource.Locked = true; + } + else + { + needToWait = true; + } + } + + if (needToWait) + { + await resource.EnqueueNewMessage(); + } + + if (concurrencyMode == ConcurrencyMode.Reentrant) + { + rpc.OperationContext.IsServiceReentrant = true; + } + } + + return rpc; + } + + internal void UnlockInstance(ref MessageRpc rpc) + { + if (concurrencyMode != ConcurrencyMode.Multiple) + { + ConcurrencyBehavior.UnlockInstance(rpc.InstanceContext); + } + } + + internal static void UnlockInstanceBeforeCallout(OperationContext operationContext) + { + if (operationContext != null && operationContext.IsServiceReentrant) + { + ConcurrencyBehavior.UnlockInstance(operationContext.InstanceContext); + } + } + + static void UnlockInstance(InstanceContext instanceContext) + { + ConcurrencyInstanceContextFacet resource = instanceContext.Concurrency; + + lock (instanceContext.ThisLock) + { + if (resource.HasWaiters) + { + resource.DequeueWaiter(); + } + else + { + //We have no pending Callouts and no new Messages to process + resource.Locked = false; + } + } + } + + // TODO: Make async to remove blocking Wait call + internal static Task LockInstanceAfterCalloutAsync(OperationContext operationContext) + { + if (operationContext != null) + { + InstanceContext instanceContext = operationContext.InstanceContext; + + if (operationContext.IsServiceReentrant) + { + ConcurrencyInstanceContextFacet resource = instanceContext.Concurrency; + bool needToWait = false; + lock (instanceContext.ThisLock) + { + if (!resource.Locked) + { + resource.Locked = true; + } + } + + if (needToWait) + { + return resource.EnqueueCalloutMessage(); + } + } + } + + return Task.CompletedTask; + } + + internal interface IWaiter + { + void Signal(); + } + + class MessageRpcWaiter : IWaiter + { + IResumeMessageRpc resume; + + internal MessageRpcWaiter(IResumeMessageRpc resume) + { + this.resume = resume; + } + + void IWaiter.Signal() + { + try + { + bool alreadyResumedNoLock; + resume.Resume(out alreadyResumedNoLock); + + if (alreadyResumedNoLock) + { + Fx.Assert("ConcurrencyBehavior resumed more than once for same call."); + } + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(e); + } + } + } + + class ThreadWaiter : IWaiter + { + ManualResetEvent wait = new ManualResetEvent(false); + + void IWaiter.Signal() + { + wait.Set(); + } + + internal void Wait() + { + wait.WaitOne(); + wait.Dispose(); + } + } + } + + internal class ConcurrencyInstanceContextFacet + { + internal bool Locked; + Queue> calloutMessageQueue; + Queue> newMessageQueue; + + internal bool HasWaiters + { + get + { + return (((calloutMessageQueue != null) && (calloutMessageQueue.Count > 0)) || + ((newMessageQueue != null) && (newMessageQueue.Count > 0))); + } + } + + TaskCompletionSource DequeueFrom(Queue> queue) + { + TaskCompletionSource waiter = queue.Dequeue(); + + if (queue.Count == 0) + { + queue.TrimExcess(); + } + + return waiter; + } + + internal void DequeueWaiter() + { + TaskCompletionSource waiter; + if ((calloutMessageQueue != null) && (calloutMessageQueue.Count > 0)) + { + waiter = DequeueFrom(calloutMessageQueue); + } + else + { + waiter = DequeueFrom(newMessageQueue); + } + + waiter.TrySetResult(null); + } + + internal Task EnqueueNewMessage() + { + if (newMessageQueue == null) + newMessageQueue = new Queue>(); + // Prevent release of waiter from running the waiter on the releasing thread by using RunContinuationsAsynchronously + var waiter = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + newMessageQueue.Enqueue(waiter); + return waiter.Task; + } + + internal Task EnqueueCalloutMessage() + { + if (calloutMessageQueue == null) + calloutMessageQueue = new Queue>(); + // Prevent release of waiter from running the waiter on the releasing thread by using RunContinuationsAsynchronously + var waiter = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + calloutMessageQueue.Enqueue(waiter); + return waiter.Task; + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/DataContractSerializerFaultFormatter.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/DataContractSerializerFaultFormatter.cs new file mode 100644 index 000000000..690c037e2 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/DataContractSerializerFaultFormatter.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using CoreWCF.Collections.Generic; + +namespace CoreWCF.Dispatcher +{ + internal class DataContractSerializerFaultFormatter : FaultFormatter + { + internal DataContractSerializerFaultFormatter(Type[] detailTypes) + : base(detailTypes) + { + } + + internal DataContractSerializerFaultFormatter(SynchronizedCollection faultContractInfoCollection) + : base(faultContractInfoCollection) + { + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/DataContractSerializerOperationFormatter.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/DataContractSerializerOperationFormatter.cs new file mode 100644 index 000000000..2d9939253 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/DataContractSerializerOperationFormatter.cs @@ -0,0 +1,587 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Runtime; +using System.Runtime.Serialization; +using CoreWCF; +using CoreWCF.Channels; +using CoreWCF.Description; +using System.Xml; +using System.Linq; +using System.Reflection; + +namespace CoreWCF.Dispatcher +{ + static class DataContractSerializerDefaults + { + internal const bool IgnoreExtensionDataObject = false; + internal const int MaxItemsInObjectGraph = int.MaxValue; + + internal static DataContractSerializer CreateSerializer(Type type, int maxItems) + { + return CreateSerializer(type, null, maxItems); + } + + internal static DataContractSerializer CreateSerializer(Type type, IList knownTypes, int maxItems) + { + return new DataContractSerializer( + type, + knownTypes); + } + + internal static DataContractSerializer CreateSerializer(Type type, string rootName, string rootNs, int maxItems) + { + return CreateSerializer(type, null, rootName, rootNs, maxItems); + } + + internal static DataContractSerializer CreateSerializer(Type type, IList knownTypes, string rootName, string rootNs, int maxItems) + { + XmlDictionary dictionary = new XmlDictionary(2); + return new DataContractSerializer( + type, + dictionary.Add(rootName), + dictionary.Add(rootNs), + knownTypes); + } + + internal static DataContractSerializer CreateSerializer(Type type, XmlDictionaryString rootName, XmlDictionaryString rootNs, int maxItems) + { + return CreateSerializer(type, null, rootName, rootNs, maxItems); + } + + internal static DataContractSerializer CreateSerializer(Type type, IList knownTypes, XmlDictionaryString rootName, XmlDictionaryString rootNs, int maxItems) + { + return new DataContractSerializer( + type, + rootName, + rootNs, + knownTypes); + } + } + + class DataContractSerializerOperationFormatter : OperationFormatter + { + static Type typeOfIQueryable = typeof(IQueryable); + static Type typeOfIQueryableGeneric = typeof(IQueryable<>); + static Type typeOfIEnumerable = typeof(IEnumerable); + static Type typeOfIEnumerableGeneric = typeof(IEnumerable<>); + + protected MessageInfo requestMessageInfo; + protected MessageInfo replyMessageInfo; + IList knownTypes; + //XsdDataContractExporter dataContractExporter; + DataContractSerializerOperationBehavior serializerFactory; + + public DataContractSerializerOperationFormatter(OperationDescription description, DataContractFormatAttribute dataContractFormatAttribute, + DataContractSerializerOperationBehavior serializerFactory) + : base(description, dataContractFormatAttribute.Style == OperationFormatStyle.Rpc, false/*isEncoded*/) + { + if (description == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("description"); + + this.serializerFactory = serializerFactory ?? new DataContractSerializerOperationBehavior(description); + foreach (Type type in description.KnownTypes) + { + if (knownTypes == null) + knownTypes = new List(); + if (type == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxKnownTypeNull, description.Name))); + ValidateDataContractType(type); + knownTypes.Add(type); + } + requestMessageInfo = CreateMessageInfo(dataContractFormatAttribute, RequestDescription, this.serializerFactory); + if (ReplyDescription != null) + replyMessageInfo = CreateMessageInfo(dataContractFormatAttribute, ReplyDescription, this.serializerFactory); + } + + MessageInfo CreateMessageInfo(DataContractFormatAttribute dataContractFormatAttribute, + MessageDescription messageDescription, DataContractSerializerOperationBehavior serializerFactory) + { + if (messageDescription.IsUntypedMessage) + return null; + MessageInfo messageInfo = new MessageInfo(); + + MessageBodyDescription body = messageDescription.Body; + if (body.WrapperName != null) + { + messageInfo.WrapperName = AddToDictionary(body.WrapperName); + messageInfo.WrapperNamespace = AddToDictionary(body.WrapperNamespace); + } + MessagePartDescriptionCollection parts = body.Parts; + messageInfo.BodyParts = new PartInfo[parts.Count]; + for (int i = 0; i < parts.Count; i++) + messageInfo.BodyParts[i] = CreatePartInfo(parts[i], dataContractFormatAttribute.Style, serializerFactory); + if (IsValidReturnValue(messageDescription.Body.ReturnValue)) + messageInfo.ReturnPart = CreatePartInfo(messageDescription.Body.ReturnValue, dataContractFormatAttribute.Style, serializerFactory); + messageInfo.HeaderDescriptionTable = new MessageHeaderDescriptionTable(); + messageInfo.HeaderParts = new PartInfo[messageDescription.Headers.Count]; + for (int i = 0; i < messageDescription.Headers.Count; i++) + { + MessageHeaderDescription headerDescription = messageDescription.Headers[i]; + if (headerDescription.IsUnknownHeaderCollection) + messageInfo.UnknownHeaderDescription = headerDescription; + else + { + ValidateDataContractType(headerDescription.Type); + messageInfo.HeaderDescriptionTable.Add(headerDescription.Name, headerDescription.Namespace, headerDescription); + } + messageInfo.HeaderParts[i] = CreatePartInfo(headerDescription, OperationFormatStyle.Document, serializerFactory); + } + messageInfo.AnyHeaders = messageInfo.UnknownHeaderDescription != null || messageInfo.HeaderDescriptionTable.Count > 0; + return messageInfo; + } + + private void ValidateDataContractType(Type type) + { + //if (dataContractExporter == null) + //{ + // dataContractExporter = new XsdDataContractExporter(); + // if (serializerFactory != null && serializerFactory.DataContractSurrogate != null) + // { + // ExportOptions options = new ExportOptions(); + // options.DataContractSurrogate = serializerFactory.DataContractSurrogate; + // dataContractExporter.Options = options; + // } + //} + //dataContractExporter.GetSchemaTypeName(type); //Throws if the type is not a valid data contract + } + + PartInfo CreatePartInfo(MessagePartDescription part, OperationFormatStyle style, DataContractSerializerOperationBehavior serializerFactory) + { + string ns = (style == OperationFormatStyle.Rpc || part.Namespace == null) ? string.Empty : part.Namespace; + PartInfo partInfo = new PartInfo(part, AddToDictionary(part.Name), AddToDictionary(ns), knownTypes, serializerFactory); + ValidateDataContractType(partInfo.ContractType); + return partInfo; + } + + protected override void AddHeadersToMessage(Message message, MessageDescription messageDescription, object[] parameters, bool isRequest) + { + MessageInfo messageInfo = isRequest ? requestMessageInfo : replyMessageInfo; + PartInfo[] headerParts = messageInfo.HeaderParts; + if (headerParts == null || headerParts.Length == 0) + return; + MessageHeaders headers = message.Headers; + for (int i = 0; i < headerParts.Length; i++) + { + PartInfo headerPart = headerParts[i]; + MessageHeaderDescription headerDescription = (MessageHeaderDescription)headerPart.Description; + object headerValue = parameters[headerDescription.Index]; + + if (headerDescription.Multiple) + { + if (headerValue != null) + { + bool isXmlElement = headerDescription.Type == typeof(XmlElement); + foreach (object headerItemValue in (IEnumerable)headerValue) + AddMessageHeaderForParameter(headers, headerPart, message.Version, headerItemValue, isXmlElement); + } + } + else + AddMessageHeaderForParameter(headers, headerPart, message.Version, headerValue, false/*isXmlElement*/); + } + } + + void AddMessageHeaderForParameter(MessageHeaders headers, PartInfo headerPart, MessageVersion messageVersion, object parameterValue, bool isXmlElement) + { + string actor; + bool mustUnderstand; + bool relay; + MessageHeaderDescription headerDescription = (MessageHeaderDescription)headerPart.Description; + object valueToSerialize = GetContentOfMessageHeaderOfT(headerDescription, parameterValue, out mustUnderstand, out relay, out actor); + + if (isXmlElement) + { + if (valueToSerialize == null) + return; + XmlElement xmlElement = (XmlElement)valueToSerialize; + headers.Add(new XmlElementMessageHeader(this, messageVersion, xmlElement.LocalName, xmlElement.NamespaceURI, mustUnderstand, actor, relay, xmlElement)); + return; + } + headers.Add(new DataContractSerializerMessageHeader(headerPart, valueToSerialize, mustUnderstand, actor, relay)); + } + + protected override void SerializeBody(XmlDictionaryWriter writer, MessageVersion version, string action, MessageDescription messageDescription, object returnValue, object[] parameters, bool isRequest) + { + if (writer == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("writer")); + if (parameters == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("parameters")); + + MessageInfo messageInfo; + if (isRequest) + messageInfo = requestMessageInfo; + else + messageInfo = replyMessageInfo; + if (messageInfo.WrapperName != null) + writer.WriteStartElement(messageInfo.WrapperName, messageInfo.WrapperNamespace); + if (messageInfo.ReturnPart != null) + SerializeParameter(writer, messageInfo.ReturnPart, returnValue); + SerializeParameters(writer, messageInfo.BodyParts, parameters); + if (messageInfo.WrapperName != null) + writer.WriteEndElement(); + } + + void SerializeParameters(XmlDictionaryWriter writer, PartInfo[] parts, object[] parameters) + { + for (int i = 0; i < parts.Length; i++) + { + PartInfo part = parts[i]; + object graph = parameters[part.Description.Index]; + SerializeParameter(writer, part, graph); + } + } + + void SerializeParameter(XmlDictionaryWriter writer, PartInfo part, object graph) + { + if (part.Description.Multiple) + { + if (graph != null) + { + foreach (object item in (IEnumerable)graph) + SerializeParameterPart(writer, part, item); + } + } + else + SerializeParameterPart(writer, part, graph); + } + + void SerializeParameterPart(XmlDictionaryWriter writer, PartInfo part, object graph) + { + try + { + part.Serializer.WriteObject(writer, graph); + } + catch (SerializationException sx) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException( + SR.Format(SR.SFxInvalidMessageBodyErrorSerializingParameter, part.Description.Namespace, part.Description.Name, sx.Message), sx)); + } + } + + protected override void GetHeadersFromMessage(Message message, MessageDescription messageDescription, object[] parameters, bool isRequest) + { + MessageInfo messageInfo = isRequest ? requestMessageInfo : replyMessageInfo; + if (!messageInfo.AnyHeaders) + return; + MessageHeaders headers = message.Headers; + KeyValuePair[] multipleHeaderValues = null; + ArrayList elementList = null; + if (messageInfo.UnknownHeaderDescription != null) + elementList = new ArrayList(); + + for (int i = 0; i < headers.Count; i++) + { + MessageHeaderInfo header = headers[i]; + MessageHeaderDescription headerDescription = messageInfo.HeaderDescriptionTable.Get(header.Name, header.Namespace); + if (headerDescription != null) + { + if (header.MustUnderstand) + headers.UnderstoodHeaders.Add(header); + + object item = null; + XmlDictionaryReader headerReader = headers.GetReaderAtHeader(i); + try + { + object dataValue = DeserializeHeaderContents(headerReader, messageDescription, headerDescription); + if (headerDescription.TypedHeader) + item = TypedHeaderManager.Create(headerDescription.Type, dataValue, headers[i].MustUnderstand, headers[i].Relay, headers[i].Actor); + else + item = dataValue; + } + finally + { + headerReader.Dispose(); + } + + if (headerDescription.Multiple) + { + if (multipleHeaderValues == null) + multipleHeaderValues = new KeyValuePair[parameters.Length]; + if (multipleHeaderValues[headerDescription.Index].Key == null) + { + multipleHeaderValues[headerDescription.Index] = new KeyValuePair(headerDescription.TypedHeader ? TypedHeaderManager.GetMessageHeaderType(headerDescription.Type) : headerDescription.Type, new ArrayList()); + } + multipleHeaderValues[headerDescription.Index].Value.Add(item); + } + else + parameters[headerDescription.Index] = item; + } + else if (messageInfo.UnknownHeaderDescription != null) + { + MessageHeaderDescription unknownHeaderDescription = messageInfo.UnknownHeaderDescription; + XmlDictionaryReader headerReader = headers.GetReaderAtHeader(i); + try + { + XmlDocument doc = new XmlDocument(); + object dataValue = doc.ReadNode(headerReader); + if (dataValue != null && unknownHeaderDescription.TypedHeader) + dataValue = TypedHeaderManager.Create(unknownHeaderDescription.Type, dataValue, headers[i].MustUnderstand, headers[i].Relay, headers[i].Actor); + elementList.Add(dataValue); + } + finally + { + headerReader.Dispose(); + } + } + } + if (multipleHeaderValues != null) + { + for (int i = 0; i < parameters.Length; i++) + { + if (multipleHeaderValues[i].Key != null) + parameters[i] = multipleHeaderValues[i].Value.ToArray(multipleHeaderValues[i].Key); + } + } + if (messageInfo.UnknownHeaderDescription != null) + parameters[messageInfo.UnknownHeaderDescription.Index] = elementList.ToArray(messageInfo.UnknownHeaderDescription.TypedHeader ? typeof(MessageHeader) : typeof(XmlElement)); + } + + object DeserializeHeaderContents(XmlDictionaryReader reader, MessageDescription messageDescription, MessageHeaderDescription headerDescription) + { + bool isQueryable; + Type dataContractType = DataContractSerializerOperationFormatter.GetSubstituteDataContractType(headerDescription.Type, out isQueryable); + XmlObjectSerializer serializerLocal = serializerFactory.CreateSerializer(dataContractType, headerDescription.Name, headerDescription.Namespace, knownTypes); + object val = serializerLocal.ReadObject(reader); + if (isQueryable && val != null) + { + return Queryable.AsQueryable((IEnumerable)val); + } + return val; + } + + protected override object DeserializeBody(XmlDictionaryReader reader, MessageVersion version, string action, MessageDescription messageDescription, object[] parameters, bool isRequest) + { + if (reader == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("reader")); + if (parameters == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("parameters")); + + MessageInfo messageInfo; + if (isRequest) + messageInfo = requestMessageInfo; + else + messageInfo = replyMessageInfo; + + if (messageInfo.WrapperName != null) + { + if (!reader.IsStartElement(messageInfo.WrapperName, messageInfo.WrapperNamespace)) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SerializationException(SR.Format(SR.SFxInvalidMessageBody, messageInfo.WrapperName, messageInfo.WrapperNamespace, reader.NodeType, reader.Name, reader.NamespaceURI))); + bool isEmptyElement = reader.IsEmptyElement; + reader.Read(); + if (isEmptyElement) + return null; + } + object returnValue = null; + if (messageInfo.ReturnPart != null) + { + while (true) + { + PartInfo part = messageInfo.ReturnPart; + if (part.Serializer.IsStartObject(reader)) + { + returnValue = DeserializeParameter(reader, part, isRequest); + break; + } + if (!reader.IsStartElement()) + break; + OperationFormatter.TraceAndSkipElement(reader); + } + } + DeserializeParameters(reader, messageInfo.BodyParts, parameters, isRequest); + if (messageInfo.WrapperName != null) + reader.ReadEndElement(); + return returnValue; + } + + void DeserializeParameters(XmlDictionaryReader reader, PartInfo[] parts, object[] parameters, bool isRequest) + { + int nextPartIndex = 0; + while (reader.IsStartElement()) + { + for (int i = nextPartIndex; i < parts.Length; i++) + { + PartInfo part = parts[i]; + if (part.Serializer.IsStartObject(reader)) + { + object parameterValue = DeserializeParameter(reader, part, isRequest); + parameters[part.Description.Index] = parameterValue; + nextPartIndex = i + 1; + } + else + parameters[part.Description.Index] = null; + } + + if (reader.IsStartElement()) + OperationFormatter.TraceAndSkipElement(reader); + } + } + + object DeserializeParameter(XmlDictionaryReader reader, PartInfo part, bool isRequest) + { + if (part.Description.Multiple) + { + ArrayList items = new ArrayList(); + while (part.Serializer.IsStartObject(reader)) + items.Add(DeserializeParameterPart(reader, part, isRequest)); + return items.ToArray(part.Description.Type); + } + return DeserializeParameterPart(reader, part, isRequest); + } + + object DeserializeParameterPart(XmlDictionaryReader reader, PartInfo part, bool isRequest) + { + object val; + try + { + val = part.ReadObject(reader); + } + catch (System.InvalidOperationException e) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException( + SR.Format(SR.SFxInvalidMessageBodyErrorDeserializingParameter, part.Description.Namespace, part.Description.Name), e)); + } + catch (System.Runtime.Serialization.InvalidDataContractException e) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException( + SR.Format(SR.SFxInvalidMessageBodyErrorDeserializingParameter, part.Description.Namespace, part.Description.Name), e)); + } + catch (System.FormatException e) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + OperationFormatter.CreateDeserializationFailedFault( + SR.Format(SR.SFxInvalidMessageBodyErrorDeserializingParameterMore,part.Description.Namespace, part.Description.Name, e.Message), + e)); + } + catch (System.Runtime.Serialization.SerializationException e) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + OperationFormatter.CreateDeserializationFailedFault( + SR.Format(SR.SFxInvalidMessageBodyErrorDeserializingParameterMore,part.Description.Namespace, part.Description.Name, e.Message), + e)); + } + + return val; + } + + internal static Type GetSubstituteDataContractType(Type type, out bool isQueryable) + { + if (type == typeOfIQueryable) + { + isQueryable = true; + return typeOfIEnumerable; + } + + if (type.GetTypeInfo().IsGenericType && + type.GetGenericTypeDefinition() == typeOfIQueryableGeneric) + { + isQueryable = true; + return typeOfIEnumerableGeneric.MakeGenericType(type.GetGenericArguments()); + } + + isQueryable = false; + return type; + } + + + class DataContractSerializerMessageHeader : XmlObjectSerializerHeader + { + PartInfo headerPart; + + public DataContractSerializerMessageHeader(PartInfo headerPart, object headerValue, bool mustUnderstand, string actor, bool relay) + : base(headerPart.DictionaryName.Value, headerPart.DictionaryNamespace.Value, headerValue, headerPart.Serializer, mustUnderstand, actor ?? string.Empty, relay) + { + this.headerPart = headerPart; + } + + protected override void OnWriteStartHeader(XmlDictionaryWriter writer, MessageVersion messageVersion) + { + //Prefix needed since there may be xsi:type attribute at toplevel with qname value where ns = "" + string prefix = (Namespace == null || Namespace.Length == 0) ? string.Empty : "h"; + writer.WriteStartElement(prefix, headerPart.DictionaryName, headerPart.DictionaryNamespace); + WriteHeaderAttributes(writer, messageVersion); + } + } + + + protected class MessageInfo + { + internal PartInfo[] HeaderParts; + internal XmlDictionaryString WrapperName; + internal XmlDictionaryString WrapperNamespace; + internal PartInfo[] BodyParts; + internal PartInfo ReturnPart; + internal MessageHeaderDescriptionTable HeaderDescriptionTable; + internal MessageHeaderDescription UnknownHeaderDescription; + internal bool AnyHeaders; + } + + protected class PartInfo + { + XmlDictionaryString dictionaryName; + XmlDictionaryString dictionaryNamespace; + MessagePartDescription description; + XmlObjectSerializer serializer; + IList knownTypes; + DataContractSerializerOperationBehavior serializerFactory; + Type contractType; + bool isQueryable; + + public PartInfo(MessagePartDescription description, XmlDictionaryString dictionaryName, XmlDictionaryString dictionaryNamespace, + IList knownTypes, DataContractSerializerOperationBehavior behavior) + { + this.dictionaryName = dictionaryName; + this.dictionaryNamespace = dictionaryNamespace; + this.description = description; + this.knownTypes = knownTypes; + serializerFactory = behavior; + + contractType = DataContractSerializerOperationFormatter.GetSubstituteDataContractType(description.Type, out isQueryable); + } + + public Type ContractType + { + get { return contractType; } + } + + public MessagePartDescription Description + { + get { return description; } + } + + public XmlDictionaryString DictionaryName + { + get { return dictionaryName; } + } + + public XmlDictionaryString DictionaryNamespace + { + get { return dictionaryNamespace; } + } + + public XmlObjectSerializer Serializer + { + get + { + if (serializer == null) + { + serializer = serializerFactory.CreateSerializer(contractType, DictionaryName, DictionaryNamespace, knownTypes); + } + return serializer; + } + } + + public object ReadObject(XmlDictionaryReader reader) + { + return ReadObject(reader, Serializer); + } + + public object ReadObject(XmlDictionaryReader reader, XmlObjectSerializer serializer) + { + object val = this.serializer.ReadObject(reader, false /* verifyObjectName */); + if (isQueryable && val != null) + { + return Queryable.AsQueryable((IEnumerable)val); + } + return val; + } + } + } + + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/DataContractSerializerServiceBehavior.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/DataContractSerializerServiceBehavior.cs new file mode 100644 index 000000000..e953218b5 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/DataContractSerializerServiceBehavior.cs @@ -0,0 +1,96 @@ +using System.Collections.ObjectModel; +using CoreWCF.Channels; +using CoreWCF.Description; + +namespace CoreWCF.Dispatcher +{ + internal class DataContractSerializerServiceBehavior : IServiceBehavior, IEndpointBehavior + { + bool _ignoreExtensionDataObject; + int _maxItemsInObjectGraph; + + internal DataContractSerializerServiceBehavior(bool ignoreExtensionDataObject, int maxItemsInObjectGraph) + { + _ignoreExtensionDataObject = ignoreExtensionDataObject; + _maxItemsInObjectGraph = maxItemsInObjectGraph; + } + + public bool IgnoreExtensionDataObject + { + get { return _ignoreExtensionDataObject; } + set { _ignoreExtensionDataObject = value; } + } + + public int MaxItemsInObjectGraph + { + get { return _maxItemsInObjectGraph; } + set { _maxItemsInObjectGraph = value; } + } + + void IServiceBehavior.Validate(ServiceDescription description, ServiceHostBase serviceHostBase) + { + } + + void IServiceBehavior.AddBindingParameters(ServiceDescription description, ServiceHostBase serviceHostBase, Collection endpoints, BindingParameterCollection parameters) + { + } + + void IServiceBehavior.ApplyDispatchBehavior(ServiceDescription description, ServiceHostBase serviceHostBase) + { + ApplySerializationSettings(description, _ignoreExtensionDataObject, _maxItemsInObjectGraph); + } + + void IEndpointBehavior.Validate(ServiceEndpoint serviceEndpoint) + { + } + + void IEndpointBehavior.AddBindingParameters(ServiceEndpoint serviceEndpoint, BindingParameterCollection parameters) + { + } + + void IEndpointBehavior.ApplyClientBehavior(ServiceEndpoint serviceEndpoint, ClientRuntime clientRuntime) + { + ApplySerializationSettings(serviceEndpoint, _ignoreExtensionDataObject, _maxItemsInObjectGraph); + } + + void IEndpointBehavior.ApplyDispatchBehavior(ServiceEndpoint serviceEndpoint, EndpointDispatcher endpointDispatcher) + { + ApplySerializationSettings(serviceEndpoint, _ignoreExtensionDataObject, _maxItemsInObjectGraph); + } + + internal static void ApplySerializationSettings(ServiceDescription description, bool ignoreExtensionDataObject, int maxItemsInObjectGraph) + { + foreach (ServiceEndpoint endpoint in description.Endpoints) + { + if (!endpoint.InternalIsSystemEndpoint(description)) + { + ApplySerializationSettings(endpoint, ignoreExtensionDataObject, maxItemsInObjectGraph); + } + } + } + + internal static void ApplySerializationSettings(ServiceEndpoint endpoint, bool ignoreExtensionDataObject, int maxItemsInObjectGraph) + { + foreach (OperationDescription operation in endpoint.Contract.Operations) + { + foreach (IOperationBehavior ob in operation.Behaviors) + { + DataContractSerializerOperationBehavior behavior = ob as DataContractSerializerOperationBehavior; + if (behavior != null) + { + if (!behavior.IgnoreExtensionDataObjectSetExplicit) + { + behavior.ignoreExtensionDataObject = ignoreExtensionDataObject; + } + if (!behavior.MaxItemsInObjectGraphSetExplicit) + { + behavior.maxItemsInObjectGraph = maxItemsInObjectGraph; + } + } + } + } + } + + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/DispatchOperation.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/DispatchOperation.cs new file mode 100644 index 000000000..03c77b414 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/DispatchOperation.cs @@ -0,0 +1,238 @@ +using System.Collections.Generic; +using CoreWCF.Collections.Generic; + +namespace CoreWCF.Dispatcher +{ + public sealed class DispatchOperation + { + string action; + SynchronizedCollection faultContractInfos; + IDispatchMessageFormatter formatter; + IDispatchFaultFormatter faultFormatter; + IOperationInvoker invoker; + bool isTerminating; + bool isSessionOpenNotificationEnabled; + string name; + bool releaseInstanceAfterCall; + bool releaseInstanceBeforeCall; + string replyAction; + bool deserializeRequest = true; + bool serializeReply = true; + bool autoDisposeParameters = true; + + public DispatchOperation(DispatchRuntime parent, string name, string action) + { + if (parent == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("parent"); + if (name == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("name"); + + Parent = parent; + this.name = name; + this.action = action; + //this.impersonation = OperationBehaviorAttribute.DefaultImpersonationOption; + // Not necessary for basic functionality + CallContextInitializers = parent.NewBehaviorCollection(); + faultContractInfos = parent.NewBehaviorCollection(); + ParameterInspectors = parent.NewBehaviorCollection(); + IsOneWay = true; + } + + internal DispatchOperation(DispatchRuntime parent, string name, string action, string replyAction) : this(parent, name, action) + { + this.replyAction = replyAction; + IsOneWay = false; + } + + public string Action + { + get { return action; } + } + + internal SynchronizedCollection CallContextInitializers { get; } + + internal SynchronizedCollection FaultContractInfos + { + get { return faultContractInfos; } + } + + internal IDispatchMessageFormatter Formatter + { + get { return formatter; } + set + { + lock (Parent.ThisLock) + { + Parent.InvalidateRuntime(); + formatter = value; + } + } + } + + internal IDispatchFaultFormatter FaultFormatter + { + get + { + if (faultFormatter == null) + { + faultFormatter = new DataContractSerializerFaultFormatter(faultContractInfos); + } + return faultFormatter; + } + set + { + lock (Parent.ThisLock) + { + Parent.InvalidateRuntime(); + faultFormatter = value; + IsFaultFormatterSetExplicit = true; + } + } + } + + internal bool IsFaultFormatterSetExplicit { get; private set; } = false; + + public bool AutoDisposeParameters + { + get { return autoDisposeParameters; } + + set + { + lock (Parent.ThisLock) + { + Parent.InvalidateRuntime(); + autoDisposeParameters = value; + } + } + } + + public bool DeserializeRequest + { + get { return deserializeRequest; } + set + { + lock (Parent.ThisLock) + { + Parent.InvalidateRuntime(); + deserializeRequest = value; + } + } + } + + public bool IsOneWay { get; } + + internal bool HasNoDisposableParameters { get; set; } + + internal IDispatchMessageFormatter InternalFormatter + { + get { return formatter; } + set { formatter = value; } + } + + internal IOperationInvoker InternalInvoker + { + get { return invoker; } + set { invoker = value; } + } + + internal IOperationInvoker Invoker + { + get { return invoker; } + set + { + lock (Parent.ThisLock) + { + Parent.InvalidateRuntime(); + invoker = value; + } + } + } + + internal bool IsTerminating + { + get { return isTerminating; } + set + { + lock (Parent.ThisLock) + { + Parent.InvalidateRuntime(); + isTerminating = value; + } + } + } + + internal bool IsSessionOpenNotificationEnabled + { + get { return isSessionOpenNotificationEnabled; } + set + { + lock (Parent.ThisLock) + { + Parent.InvalidateRuntime(); + isSessionOpenNotificationEnabled = value; + } + } + } + + public string Name + { + get { return name; } + } + + internal SynchronizedCollection ParameterInspectors { get; } + + public DispatchRuntime Parent { get; } + + internal ReceiveContextAcknowledgementMode ReceiveContextAcknowledgementMode { get; set; } + + internal bool BufferedReceiveEnabled + { + get { return Parent.ChannelDispatcher.BufferedReceiveEnabled; } + set { Parent.ChannelDispatcher.BufferedReceiveEnabled = value; } + } + + internal bool ReleaseInstanceAfterCall + { + get { return releaseInstanceAfterCall; } + set + { + lock (Parent.ThisLock) + { + Parent.InvalidateRuntime(); + releaseInstanceAfterCall = value; + } + } + } + + internal bool ReleaseInstanceBeforeCall + { + get { return releaseInstanceBeforeCall; } + set + { + lock (Parent.ThisLock) + { + Parent.InvalidateRuntime(); + releaseInstanceBeforeCall = value; + } + } + } + + public string ReplyAction + { + get { return replyAction; } + } + + public bool SerializeReply + { + get { return serializeReply; } + set + { + lock (Parent.ThisLock) + { + Parent.InvalidateRuntime(); + serializeReply = value; + } + } + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/DispatchOperationRuntime.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/DispatchOperationRuntime.cs new file mode 100644 index 000000000..59da465d1 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/DispatchOperationRuntime.cs @@ -0,0 +1,672 @@ +using System; +using System.Globalization; +using System.Reflection; +using System.Security; +using System.Threading.Tasks; +using CoreWCF.Runtime; +using CoreWCF.Channels; +using CoreWCF.Diagnostics; + +namespace CoreWCF.Dispatcher +{ + internal class DispatchOperationRuntime + { + readonly string action; + readonly ICallContextInitializer[] callContextInitializers; + readonly IDispatchFaultFormatter faultFormatter; + readonly IDispatchMessageFormatter formatter; + //readonly ImpersonationOption impersonation; + readonly IParameterInspector[] inspectors; + readonly IOperationInvoker invoker; + readonly bool isTerminating; + readonly bool isSessionOpenNotificationEnabled; + readonly bool isSynchronous; + readonly string name; + readonly ImmutableDispatchRuntime parent; + readonly bool releaseInstanceAfterCall; + readonly bool releaseInstanceBeforeCall; + readonly string replyAction; + //readonly bool transactionAutoComplete; + //readonly bool transactionRequired; + readonly bool deserializeRequest; + readonly bool serializeReply; + readonly bool isOneWay; + readonly bool disposeParameters; + readonly ReceiveContextAcknowledgementMode receiveContextAcknowledgementMode; + readonly bool bufferedReceiveEnabled; + //readonly bool isInsideTransactedReceiveScope; + + internal DispatchOperationRuntime(DispatchOperation operation, ImmutableDispatchRuntime parent) + { + if (operation == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("operation"); + } + if (parent == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("parent"); + } + if (operation.Invoker == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.RuntimeRequiresInvoker0)); + } + + disposeParameters = ((operation.AutoDisposeParameters) && (!operation.HasNoDisposableParameters)); + this.parent = parent; + callContextInitializers = EmptyArray.ToArray(operation.CallContextInitializers); + inspectors = EmptyArray.ToArray(operation.ParameterInspectors); + faultFormatter = operation.FaultFormatter; + //this.impersonation = operation.Impersonation; + deserializeRequest = operation.DeserializeRequest; + serializeReply = operation.SerializeReply; + formatter = operation.Formatter; + invoker = operation.Invoker; + + try + { + isSynchronous = operation.Invoker.IsSynchronous; + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(e); + } + isTerminating = operation.IsTerminating; + isSessionOpenNotificationEnabled = operation.IsSessionOpenNotificationEnabled; + action = operation.Action; + name = operation.Name; + releaseInstanceAfterCall = operation.ReleaseInstanceAfterCall; + releaseInstanceBeforeCall = operation.ReleaseInstanceBeforeCall; + replyAction = operation.ReplyAction; + isOneWay = operation.IsOneWay; + //this.transactionAutoComplete = operation.TransactionAutoComplete; + //this.transactionRequired = operation.TransactionRequired; + receiveContextAcknowledgementMode = operation.ReceiveContextAcknowledgementMode; + bufferedReceiveEnabled = operation.BufferedReceiveEnabled; + //this.isInsideTransactedReceiveScope = operation.IsInsideTransactedReceiveScope; + + if (formatter == null && (deserializeRequest || serializeReply)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.DispatchRuntimeRequiresFormatter0, name))); + } + + if ((operation.Parent.InstanceProvider == null) && (operation.Parent.Type != null)) + { + SyncMethodInvoker sync = invoker as SyncMethodInvoker; + if (sync != null) + { + ValidateInstanceType(operation.Parent.Type, sync.Method); + } + + //AsyncMethodInvoker async = this.invoker as AsyncMethodInvoker; + //if (async != null) + //{ + // this.ValidateInstanceType(operation.Parent.Type, async.BeginMethod); + // this.ValidateInstanceType(operation.Parent.Type, async.EndMethod); + //} + + TaskMethodInvoker task = invoker as TaskMethodInvoker; + if (task != null) + { + ValidateInstanceType(operation.Parent.Type, task.TaskMethod); + } + } + } + + internal string Action + { + get { return action; } + } + + internal ICallContextInitializer[] CallContextInitializers + { + get { return callContextInitializers; } + } + + internal bool DisposeParameters + { + get { return disposeParameters; } + } + + internal bool HasDefaultUnhandledActionInvoker + { + get { return (invoker is DispatchRuntime.UnhandledActionInvoker); } + } + + internal bool SerializeReply + { + get { return serializeReply; } + } + + internal IDispatchFaultFormatter FaultFormatter + { + get { return faultFormatter; } + } + + internal IDispatchMessageFormatter Formatter + { + get { return formatter; } + } + + //internal ImpersonationOption Impersonation + //{ + // get { return this.impersonation; } + //} + + internal IOperationInvoker Invoker + { + get { return invoker; } + } + + internal bool IsSynchronous + { + get { return isSynchronous; } + } + + internal bool IsOneWay + { + get { return isOneWay; } + } + + internal bool IsTerminating + { + get { return isTerminating; } + } + + internal string Name + { + get { return name; } + } + + internal IParameterInspector[] ParameterInspectors + { + get { return inspectors; } + } + + internal ImmutableDispatchRuntime Parent + { + get { return parent; } + } + + internal ReceiveContextAcknowledgementMode ReceiveContextAcknowledgementMode + { + get { return receiveContextAcknowledgementMode; } + } + + internal bool ReleaseInstanceAfterCall + { + get { return releaseInstanceAfterCall; } + } + + internal bool ReleaseInstanceBeforeCall + { + get { return releaseInstanceBeforeCall; } + } + + internal string ReplyAction + { + get { return replyAction; } + } + + //internal bool TransactionAutoComplete + //{ + // get { return this.transactionAutoComplete; } + //} + + //internal bool TransactionRequired + //{ + // get { return this.transactionRequired; } + //} + + //internal bool IsInsideTransactedReceiveScope + //{ + // get { return this.isInsideTransactedReceiveScope; } + //} + + void DeserializeInputs(ref MessageRpc rpc) + { + //bool success = false; + try + { + try + { + rpc.InputParameters = Invoker.AllocateInputs(); + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + if (ErrorBehavior.ShouldRethrowExceptionAsIs(e)) + { + throw; + } + throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(e); + } + try + { + // If the field is true, then this operation is to be invoked at the time the service + // channel is opened. The incoming message is created at ChannelHandler level with no + // content, so we don't need to deserialize the message. + if (!isSessionOpenNotificationEnabled) + { + if (deserializeRequest) + { + //if (TD.DispatchFormatterDeserializeRequestStartIsEnabled()) + //{ + // TD.DispatchFormatterDeserializeRequestStart(rpc.EventTraceActivity); + //} + + Formatter.DeserializeRequest(rpc.Request, rpc.InputParameters); + + //if (TD.DispatchFormatterDeserializeRequestStopIsEnabled()) + //{ + // TD.DispatchFormatterDeserializeRequestStop(rpc.EventTraceActivity); + //} + } + else + { + rpc.InputParameters[0] = rpc.Request; + } + } + + //success = true; + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + if (ErrorBehavior.ShouldRethrowExceptionAsIs(e)) + { + throw; + } + throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(e); + } + } + finally + { + rpc.DidDeserializeRequestBody = (rpc.Request.State != MessageState.Created); + + //if (!success && MessageLogger.LoggingEnabled) + //{ + // MessageLogger.LogMessage(ref rpc.Request, MessageLoggingSource.Malformed); + //} + } + } + + void InitializeCallContext(ref MessageRpc rpc) + { + if (CallContextInitializers.Length > 0) + { + InitializeCallContextCore(ref rpc); + } + } + + void InitializeCallContextCore(ref MessageRpc rpc) + { + IClientChannel channel = rpc.Channel.Proxy as IClientChannel; + int offset = Parent.CallContextCorrelationOffset; + + try + { + for (int i = 0; i < rpc.Operation.CallContextInitializers.Length; i++) + { + ICallContextInitializer initializer = CallContextInitializers[i]; + rpc.Correlation[offset + i] = initializer.BeforeInvoke(rpc.InstanceContext, channel, rpc.Request); + } + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + if (ErrorBehavior.ShouldRethrowExceptionAsIs(e)) + { + throw; + } + throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(e); + } + } + + void UninitializeCallContext(ref MessageRpc rpc) + { + if (CallContextInitializers.Length > 0) + { + UninitializeCallContextCore(ref rpc); + } + } + + void UninitializeCallContextCore(ref MessageRpc rpc) + { + IClientChannel channel = rpc.Channel.Proxy as IClientChannel; + int offset = Parent.CallContextCorrelationOffset; + + try + { + for (int i = CallContextInitializers.Length - 1; i >= 0; i--) + { + ICallContextInitializer initializer = CallContextInitializers[i]; + initializer.AfterInvoke(rpc.Correlation[offset + i]); + } + } + catch (Exception e) + { + // thread-local storage may be corrupt + DiagnosticUtility.FailFast(string.Format(CultureInfo.InvariantCulture, "ICallContextInitializer.BeforeInvoke threw an exception of type {0}: {1}", e.GetType(), e.Message)); + } + } + + void InspectInputs(ref MessageRpc rpc) + { + if (ParameterInspectors.Length > 0) + { + InspectInputsCore(ref rpc); + } + } + + void InspectInputsCore(ref MessageRpc rpc) + { + int offset = Parent.ParameterInspectorCorrelationOffset; + + for (int i = 0; i < ParameterInspectors.Length; i++) + { + IParameterInspector inspector = ParameterInspectors[i]; + rpc.Correlation[offset + i] = inspector.BeforeCall(Name, rpc.InputParameters); + //if (TD.ParameterInspectorBeforeCallInvokedIsEnabled()) + //{ + // TD.ParameterInspectorBeforeCallInvoked(rpc.EventTraceActivity, this.ParameterInspectors[i].GetType().FullName); + //} + } + } + + void InspectOutputs(ref MessageRpc rpc) + { + if (ParameterInspectors.Length > 0) + { + InspectOutputsCore(ref rpc); + } + } + + void InspectOutputsCore(ref MessageRpc rpc) + { + int offset = Parent.ParameterInspectorCorrelationOffset; + + for (int i = ParameterInspectors.Length - 1; i >= 0; i--) + { + IParameterInspector inspector = ParameterInspectors[i]; + inspector.AfterCall(Name, rpc.OutputParameters, rpc.ReturnParameter, rpc.Correlation[offset + i]); + //if (TD.ParameterInspectorAfterCallInvokedIsEnabled()) + //{ + // TD.ParameterInspectorAfterCallInvoked(rpc.EventTraceActivity, this.ParameterInspectors[i].GetType().FullName); + //} + } + } + + internal async Task InvokeAsync(MessageRpc rpc) + { + if (rpc.Error == null) + { + try + { + InitializeCallContext(ref rpc); + object target = rpc.Instance; + DeserializeInputs(ref rpc); + InspectInputs(ref rpc); + + ValidateMustUnderstand(ref rpc); + + //IAsyncResult result = null; + //IDisposable impersonationContext = null; + //IPrincipal originalPrincipal = null; + //bool isThreadPrincipalSet = false; + bool isConcurrent = Parent.IsConcurrent(ref rpc); + + //try + //{ + if (parent.RequireClaimsPrincipalOnOperationContext) + { + SetClaimsPrincipalToOperationContext(rpc); + } + + //if (this.parent.SecurityImpersonation != null) + //{ + // this.parent.SecurityImpersonation.StartImpersonation(ref rpc, out impersonationContext, out originalPrincipal, out isThreadPrincipalSet); + //} + //IManualConcurrencyOperationInvoker manualInvoker = this.Invoker as IManualConcurrencyOperationInvoker; + + if (isSynchronous) + { + //if (manualInvoker != null && isConcurrent) + //{ + // if (this.bufferedReceiveEnabled) + // { + // rpc.OperationContext.IncomingMessageProperties.Add( + // BufferedReceiveMessageProperty.Name, new BufferedReceiveMessageProperty(ref rpc)); + // } + // rpc.ReturnParameter = manualInvoker.Invoke(target, rpc.InputParameters, rpc.InvokeNotification, out rpc.OutputParameters); + //} + //else + //{ + rpc.ReturnParameter = Invoker.Invoke(target, rpc.InputParameters, out rpc.OutputParameters); + //} + } + else + { + (rpc.ReturnParameter, rpc.OutputParameters) = await Invoker.InvokeAsync(target, rpc.InputParameters); + } + // } + // finally + // { + // try + // { + // if (this.parent.SecurityImpersonation != null) + // { + // this.parent.SecurityImpersonation.StopImpersonation(ref rpc, impersonationContext, originalPrincipal, isThreadPrincipalSet); + // } + // } + // + // catch + // { + // string message = null; + // try + // { + // message = SR.SFxRevertImpersonationFailed0; + // } + // finally + // { + // DiagnosticUtility.FailFast(message); + // } + // } + // } + + InspectOutputs(ref rpc); + + SerializeOutputs(ref rpc); + } + catch { throw; } // Make sure user Exception filters are not impersonated. + finally + { + UninitializeCallContext(ref rpc); + } + } + + return rpc; + } + + void SetClaimsPrincipalToOperationContext(MessageRpc rpc) + { + // TODO: Reenable this code + + //ServiceSecurityContext securityContext = rpc.SecurityContext; + //if (!rpc.HasSecurityContext) + //{ + // SecurityMessageProperty securityContextProperty = rpc.Request.Properties.Security; + // if (securityContextProperty != null) + // { + // securityContext = securityContextProperty.ServiceSecurityContext; + // } + //} + + //if (securityContext != null) + //{ + // object principal; + // if (securityContext.AuthorizationContext.Properties.TryGetValue(AuthorizationPolicy.ClaimsPrincipalKey, out principal)) + // { + // ClaimsPrincipal claimsPrincipal = principal as ClaimsPrincipal; + // if (claimsPrincipal != null) + // { + // // + // // Always set ClaimsPrincipal to OperationContext.Current if identityModel pipeline is used. + // // + // OperationContext.Current.ClaimsPrincipal = claimsPrincipal; + // } + // else + // { + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.NoPrincipalSpecifiedInAuthorizationContext)); + // } + // } + //} + } + + void SerializeOutputs(ref MessageRpc rpc) + { + if (!IsOneWay && parent.EnableFaults) + { + Message reply; + if (serializeReply) + { + try + { + //if (TD.DispatchFormatterSerializeReplyStartIsEnabled()) + //{ + // TD.DispatchFormatterSerializeReplyStart(rpc.EventTraceActivity); + //} + + reply = Formatter.SerializeReply(rpc.RequestVersion, rpc.OutputParameters, rpc.ReturnParameter); + + //if (TD.DispatchFormatterSerializeReplyStopIsEnabled()) + //{ + // TD.DispatchFormatterSerializeReplyStop(rpc.EventTraceActivity); + //} + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + if (ErrorBehavior.ShouldRethrowExceptionAsIs(e)) + { + throw; + } + throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(e); + } + + if (reply == null) + { + string message = SR.Format(SR.SFxNullReplyFromFormatter2, Formatter.GetType().ToString(), (name ?? "")); + ErrorBehavior.ThrowAndCatch(new InvalidOperationException(message)); + } + } + else + { + if ((rpc.ReturnParameter == null) && (rpc.OperationContext.RequestContext != null)) + { + string message = SR.Format(SR.SFxDispatchRuntimeMessageCannotBeNull, name); + ErrorBehavior.ThrowAndCatch(new InvalidOperationException(message)); + } + + reply = (Message)rpc.ReturnParameter; + + if ((reply != null) && (!ProxyOperationRuntime.IsValidAction(reply, ReplyAction))) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxInvalidReplyAction, Name, reply.Headers.Action ?? "{NULL}", ReplyAction))); + } + } + + //if (DiagnosticUtility.ShouldUseActivity && rpc.Activity != null && reply != null) + //{ + // TraceUtility.SetActivity(reply, rpc.Activity); + // if (TraceUtility.ShouldPropagateActivity) + // { + // TraceUtility.AddActivityHeader(reply); + // } + //} + //else if (TraceUtility.ShouldPropagateActivity && reply != null && rpc.ResponseActivityId != Guid.Empty) + //{ + // ActivityIdHeader header = new ActivityIdHeader(rpc.ResponseActivityId); + // header.AddTo(reply); + //} + + //rely on the property set during the message receive to correlate the trace + //if (TraceUtility.MessageFlowTracingOnly) + //{ + // //Guard against MEX scenarios where the message is closed by now + // if (null != rpc.OperationContext.IncomingMessage && MessageState.Closed != rpc.OperationContext.IncomingMessage.State) + // { + // FxTrace.Trace.SetAndTraceTransfer(TraceUtility.GetReceivedActivityId(rpc.OperationContext), true); + // } + // else + // { + // if (rpc.ResponseActivityId != Guid.Empty) + // { + // FxTrace.Trace.SetAndTraceTransfer(rpc.ResponseActivityId, true); + // } + // } + //} + + // Add the ImpersonateOnSerializingReplyMessageProperty on the reply message iff + // a. reply message is not null. + // b. Impersonation is enabled on serializing Reply + + //if (reply != null && this.parent.IsImpersonationEnabledOnSerializingReply) + //{ + // bool shouldImpersonate = this.parent.SecurityImpersonation != null && this.parent.SecurityImpersonation.IsImpersonationEnabledOnCurrentOperation(ref rpc); + // if (shouldImpersonate) + // { + // reply.Properties.Add(ImpersonateOnSerializingReplyMessageProperty.Name, new ImpersonateOnSerializingReplyMessageProperty(ref rpc)); + // reply = new ImpersonatingMessage(reply); + // } + //} + + //if (MessageLogger.LoggingEnabled && null != reply) + //{ + // MessageLogger.LogMessage(ref reply, MessageLoggingSource.ServiceLevelSendReply | MessageLoggingSource.LastChance); + //} + rpc.Reply = reply; + } + } + + void ValidateInstanceType(Type type, MethodInfo method) + { + if (!method.DeclaringType.IsAssignableFrom(type)) + { + string message = SR.Format(SR.SFxMethodNotSupportedByType2,type.FullName, + method.DeclaringType.FullName); + + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(message)); + } + } + + void ValidateMustUnderstand(ref MessageRpc rpc) + { + if (parent.ValidateMustUnderstand) + { + rpc.NotUnderstoodHeaders = rpc.Request.Headers.GetHeadersNotUnderstood(); + if (rpc.NotUnderstoodHeaders != null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new MustUnderstandSoapException(rpc.NotUnderstoodHeaders, rpc.Request.Version.Envelope)); + } + } + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/DispatchRuntime.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/DispatchRuntime.cs new file mode 100644 index 000000000..c60ce3ba5 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/DispatchRuntime.cs @@ -0,0 +1,976 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Threading; +using CoreWCF.Collections.Generic; +using CoreWCF.IdentityModel.Policy; +using CoreWCF.Runtime; +using CoreWCF.Channels; +using CoreWCF.Diagnostics; + +namespace CoreWCF.Dispatcher +{ + public sealed class DispatchRuntime + { + //ServiceAuthenticationManager serviceAuthenticationManager; + //ServiceAuthorizationManager serviceAuthorizationManager; + //ReadOnlyCollection externalAuthorizationPolicies; + //AuditLogLocation securityAuditLogLocation; + ConcurrencyMode concurrencyMode; + bool ensureOrderedDispatch; + //bool suppressAuditFailure; + //AuditLevel serviceAuthorizationAuditLevel; + //AuditLevel messageAuthenticationAuditLevel; + bool automaticInputSessionShutdown; + ChannelDispatcher channelDispatcher; + SynchronizedCollection inputSessionShutdownHandlers; + EndpointDispatcher endpointDispatcher; + IInstanceProvider instanceProvider; + IInstanceContextProvider instanceContextProvider; + InstanceContext singleton; + bool ignoreTransactionMessageProperty; + SynchronizedCollection messageInspectors; + OperationCollection operations; + IDispatchOperationSelector operationSelector; + ClientRuntime proxyRuntime; + ImmutableDispatchRuntime runtime; + SynchronizedCollection instanceContextInitializers; + //bool isExternalPoliciesSet; + //bool isAuthenticationManagerSet; + //bool isAuthorizationManagerSet; + SynchronizationContext synchronizationContext; + //PrincipalPermissionMode principalPermissionMode; + //object roleProvider; + Type type; + DispatchOperation unhandled; + //bool transactionAutoCompleteOnSessionClose; + //bool impersonateCallerForAllOperations; + //bool impersonateOnSerializingReply; + //bool releaseServiceInstanceOnTransactionComplete; + SharedRuntimeState shared; + bool preserveMessage; + //bool requireClaimsPrincipalOnOperationContext; + + internal DispatchRuntime(EndpointDispatcher endpointDispatcher) + : this(new SharedRuntimeState(true)) + { + if (endpointDispatcher == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("endpointDispatcher"); + } + + this.endpointDispatcher = endpointDispatcher; + + Fx.Assert(shared.IsOnServer, "Server constructor called on client?"); + } + + internal DispatchRuntime(ClientRuntime proxyRuntime, SharedRuntimeState shared) + : this(shared) + { + if (proxyRuntime == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("proxyRuntime"); + } + + this.proxyRuntime = proxyRuntime; + instanceProvider = new CallbackInstanceProvider(); + channelDispatcher = new ChannelDispatcher(shared); + instanceContextProvider = InstanceContextProviderBase.GetProviderForMode(InstanceContextMode.PerSession, this); + + Fx.Assert(!shared.IsOnServer, "Client constructor called on server?"); + } + + DispatchRuntime(SharedRuntimeState shared) + { + this.shared = shared; + + operations = new OperationCollection(this); + + inputSessionShutdownHandlers = NewBehaviorCollection(); + messageInspectors = NewBehaviorCollection(); + instanceContextInitializers = NewBehaviorCollection(); + synchronizationContext = ThreadBehavior.GetCurrentSynchronizationContext(); + + automaticInputSessionShutdown = true; + //this.principalPermissionMode = ServiceAuthorizationBehavior.DefaultPrincipalPermissionMode; + + //this.securityAuditLogLocation = ServiceSecurityAuditBehavior.defaultAuditLogLocation; + //this.suppressAuditFailure = ServiceSecurityAuditBehavior.defaultSuppressAuditFailure; + //this.serviceAuthorizationAuditLevel = ServiceSecurityAuditBehavior.defaultServiceAuthorizationAuditLevel; + //this.messageAuthenticationAuditLevel = ServiceSecurityAuditBehavior.defaultMessageAuthenticationAuditLevel; + + unhandled = new DispatchOperation(this, "*", MessageHeaders.WildcardAction, MessageHeaders.WildcardAction); + unhandled.InternalFormatter = MessageOperationFormatter.Instance; + unhandled.InternalInvoker = new UnhandledActionInvoker(this); + } + + internal IInstanceContextProvider InstanceContextProvider + { + get + { + return instanceContextProvider; + } + + set + { + if (value == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(value)); + } + + lock (ThisLock) + { + InvalidateRuntime(); + instanceContextProvider = value; + } + } + } + + public InstanceContext SingletonInstanceContext + { + get { return singleton; } + set + { + if (value == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(value)); + } + + lock (ThisLock) + { + InvalidateRuntime(); + singleton = value; + } + } + } + + public ConcurrencyMode ConcurrencyMode + { + get + { + return concurrencyMode; + } + set + { + lock (ThisLock) + { + InvalidateRuntime(); + concurrencyMode = value; + } + } + } + + public bool EnsureOrderedDispatch + { + get + { + return ensureOrderedDispatch; + } + set + { + lock (ThisLock) + { + InvalidateRuntime(); + ensureOrderedDispatch = value; + } + } + } + + //public AuditLogLocation SecurityAuditLogLocation + //{ + // get + // { + // return this.securityAuditLogLocation; + // } + // set + // { + // if (!AuditLogLocationHelper.IsDefined(value)) + // { + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(value))); + // } + + // lock (this.ThisLock) + // { + // this.InvalidateRuntime(); + // this.securityAuditLogLocation = value; + // } + // } + //} + + //public bool SuppressAuditFailure + //{ + // get + // { + // return this.suppressAuditFailure; + // } + // set + // { + // lock (this.ThisLock) + // { + // this.InvalidateRuntime(); + // this.suppressAuditFailure = value; + // } + // } + //} + + //public AuditLevel ServiceAuthorizationAuditLevel + //{ + // get + // { + // return this.serviceAuthorizationAuditLevel; + // } + // set + // { + // if (!AuditLevelHelper.IsDefined(value)) + // { + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(value))); + // } + + // lock (this.ThisLock) + // { + // this.InvalidateRuntime(); + // this.serviceAuthorizationAuditLevel = value; + // } + // } + //} + + //public AuditLevel MessageAuthenticationAuditLevel + //{ + // get + // { + // return this.messageAuthenticationAuditLevel; + // } + // set + // { + // if (!AuditLevelHelper.IsDefined(value)) + // { + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(value))); + // } + + // lock (this.ThisLock) + // { + // this.InvalidateRuntime(); + // this.messageAuthenticationAuditLevel = value; + // } + // } + //} + + //internal ReadOnlyCollection ExternalAuthorizationPolicies + //{ + // get + // { + // return this.externalAuthorizationPolicies; + // } + // set + // { + // lock (this.ThisLock) + // { + // this.InvalidateRuntime(); + // this.externalAuthorizationPolicies = value; + // this.isExternalPoliciesSet = true; + // } + // } + //} + + //public ServiceAuthenticationManager ServiceAuthenticationManager + //{ + // get + // { + // return this.serviceAuthenticationManager; + // } + // set + // { + // lock (this.ThisLock) + // { + // this.InvalidateRuntime(); + // this.serviceAuthenticationManager = value; + // this.isAuthenticationManagerSet = true; + // } + // } + //} + + //public ServiceAuthorizationManager ServiceAuthorizationManager + //{ + // get + // { + // return this.serviceAuthorizationManager; + // } + // set + // { + // lock (this.ThisLock) + // { + // this.InvalidateRuntime(); + // this.serviceAuthorizationManager = value; + // this.isAuthorizationManagerSet = true; + // } + // } + //} + + public bool AutomaticInputSessionShutdown + { + get { return automaticInputSessionShutdown; } + set + { + lock (ThisLock) + { + InvalidateRuntime(); + automaticInputSessionShutdown = value; + } + } + } + + internal ChannelDispatcher ChannelDispatcher + { + get { return channelDispatcher ?? endpointDispatcher.ChannelDispatcher; } + } + + public ClientRuntime CallbackClientRuntime + { + get + { + if (proxyRuntime == null) + { + lock (ThisLock) + { + if (proxyRuntime == null) + { + proxyRuntime = new ClientRuntime(this, shared); + } + } + } + + return proxyRuntime; + } + } + + public EndpointDispatcher EndpointDispatcher + { + get { return endpointDispatcher; } + } + + //public bool ImpersonateCallerForAllOperations + //{ + // get + // { + // return this.impersonateCallerForAllOperations; + // } + // set + // { + // lock (this.ThisLock) + // { + // this.InvalidateRuntime(); + // this.impersonateCallerForAllOperations = value; + // } + // } + //} + + //public bool ImpersonateOnSerializingReply + //{ + // get + // { + // return this.impersonateOnSerializingReply; + // } + // set + // { + // lock (this.ThisLock) + // { + // this.InvalidateRuntime(); + // this.impersonateOnSerializingReply = value; + // } + // } + //} + + //internal bool RequireClaimsPrincipalOnOperationContext + //{ + // get + // { + // return this.requireClaimsPrincipalOnOperationContext; + // } + // set + // { + // lock (this.ThisLock) + // { + // this.InvalidateRuntime(); + // this.requireClaimsPrincipalOnOperationContext = value; + // } + // } + //} + + internal SynchronizedCollection InputSessionShutdownHandlers + { + get { return inputSessionShutdownHandlers; } + } + + public bool IgnoreTransactionMessageProperty + { + get { return ignoreTransactionMessageProperty; } + set + { + lock (ThisLock) + { + InvalidateRuntime(); + ignoreTransactionMessageProperty = value; + } + } + } + + internal IInstanceProvider InstanceProvider + { + get { return instanceProvider; } + set + { + lock (ThisLock) + { + InvalidateRuntime(); + instanceProvider = value; + } + } + } + + public SynchronizedCollection MessageInspectors + { + get { return messageInspectors; } + } + + public SynchronizedKeyedCollection Operations + { + get { return operations; } + } + + internal IDispatchOperationSelector OperationSelector + { + get { return operationSelector; } + set + { + lock (ThisLock) + { + InvalidateRuntime(); + operationSelector = value; + } + } + } + + //public bool ReleaseServiceInstanceOnTransactionComplete + //{ + // get { return this.releaseServiceInstanceOnTransactionComplete; } + // set + // { + // lock (this.ThisLock) + // { + // this.InvalidateRuntime(); + // this.releaseServiceInstanceOnTransactionComplete = value; + // } + // } + //} + + internal SynchronizedCollection InstanceContextInitializers + { + get { return instanceContextInitializers; } + } + + public SynchronizationContext SynchronizationContext + { + get { return synchronizationContext; } + set + { + lock (ThisLock) + { + InvalidateRuntime(); + synchronizationContext = value; + } + } + } + + //public PrincipalPermissionMode PrincipalPermissionMode + //{ + // get + // { + // return this.principalPermissionMode; + // } + // set + // { + // if (!PrincipalPermissionModeHelper.IsDefined(value)) + // { + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(value))); + // } + + // lock (this.ThisLock) + // { + // this.InvalidateRuntime(); + // this.principalPermissionMode = value; + // } + // } + //} + + //public RoleProvider RoleProvider + //{ + // get { return (RoleProvider)this.roleProvider; } + // set + // { + // lock (this.ThisLock) + // { + // this.InvalidateRuntime(); + // this.roleProvider = value; + // } + // } + //} + + //public bool TransactionAutoCompleteOnSessionClose + //{ + // get { return this.transactionAutoCompleteOnSessionClose; } + // set + // { + // lock (this.ThisLock) + // { + // this.InvalidateRuntime(); + // this.transactionAutoCompleteOnSessionClose = value; + // } + // } + //} + + public Type Type + { + get { return type; } + set + { + lock (ThisLock) + { + InvalidateRuntime(); + type = value; + } + } + } + + public DispatchOperation UnhandledDispatchOperation + { + get { return unhandled; } + set + { + if (value == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value"); + } + + lock (ThisLock) + { + InvalidateRuntime(); + unhandled = value; + } + } + } + + public bool ValidateMustUnderstand + { + get { return shared.ValidateMustUnderstand; } + set + { + lock (ThisLock) + { + InvalidateRuntime(); + shared.ValidateMustUnderstand = value; + } + } + } + + public bool PreserveMessage + { + get { return preserveMessage; } + set + { + throw new PlatformNotSupportedException(nameof(PreserveMessage)); + //lock (ThisLock) + //{ + // InvalidateRuntime(); + // preserveMessage = value; + //} + } + } + + //internal bool RequiresAuthentication + //{ + // get + // { + // return this.isAuthenticationManagerSet; + // } + //} + + //internal bool RequiresAuthorization + //{ + // get + // { + // return (this.isAuthorizationManagerSet || this.isExternalPoliciesSet || + // AuditLevel.Success == (this.serviceAuthorizationAuditLevel & AuditLevel.Success)); + // } + //} + + internal bool HasMatchAllOperation + { + get + { + lock (ThisLock) + { + return !(unhandled.Invoker is UnhandledActionInvoker); + } + } + } + + internal bool EnableFaults + { + get + { + if (IsOnServer) + { + ChannelDispatcher channelDispatcher = ChannelDispatcher; + return (channelDispatcher != null) && channelDispatcher.EnableFaults; + } + else + { + return shared.EnableFaults; + } + } + } + + internal bool IsOnServer + { + get { return shared.IsOnServer; } + } + + internal bool ManualAddressing + { + get + { + if (IsOnServer) + { + ChannelDispatcher channelDispatcher = ChannelDispatcher; + return (channelDispatcher != null) && channelDispatcher.ManualAddressing; + } + else + { + return shared.ManualAddressing; + } + } + } + + internal int MaxCallContextInitializers + { + get + { + lock (ThisLock) + { + int max = 0; + + for (int i = 0; i < operations.Count; i++) + { + max = System.Math.Max(max, operations[i].CallContextInitializers.Count); + } + max = System.Math.Max(max, unhandled.CallContextInitializers.Count); + return max; + } + } + } + + internal int MaxParameterInspectors + { + get + { + lock (ThisLock) + { + int max = 0; + + for (int i = 0; i < operations.Count; i++) + { + max = System.Math.Max(max, operations[i].ParameterInspectors.Count); + } + max = System.Math.Max(max, unhandled.ParameterInspectors.Count); + return max; + } + } + } + + // Internal access to CallbackClientRuntime, but this one doesn't create on demand + internal ClientRuntime ClientRuntime + { + get { return proxyRuntime; } + } + + internal object ThisLock + { + get { return shared; } + } + + //internal bool IsRoleProviderSet + //{ + // get { return this.roleProvider != null; } + //} + + internal DispatchOperationRuntime GetOperation(ref Message message) + { + ImmutableDispatchRuntime runtime = GetRuntime(); + return runtime.GetOperation(ref message); + } + + internal ImmutableDispatchRuntime GetRuntime() + { + ImmutableDispatchRuntime runtime = this.runtime; + if (runtime != null) + { + return runtime; + } + else + { + return GetRuntimeCore(); + } + } + + ImmutableDispatchRuntime GetRuntimeCore() + { + lock (ThisLock) + { + if (runtime == null) + { + runtime = new ImmutableDispatchRuntime(this); + } + + return runtime; + } + } + + internal void InvalidateRuntime() + { + lock (ThisLock) + { + shared.ThrowIfImmutable(); + runtime = null; + } + } + + internal void LockDownProperties() + { + shared.LockDownProperties(); + if (concurrencyMode != ConcurrencyMode.Single && ensureOrderedDispatch) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.SfxDispatchRuntimeNonConcurrentOrEnsureOrderedDispatch)); + } + } + + internal SynchronizedCollection NewBehaviorCollection() + { + return new DispatchBehaviorCollection(this); + } + + internal class UnhandledActionInvoker : IOperationInvoker + { + DispatchRuntime dispatchRuntime; + + public UnhandledActionInvoker(DispatchRuntime dispatchRuntime) + { + this.dispatchRuntime = dispatchRuntime; + } + + public bool IsSynchronous + { + get { return true; } + } + + public object[] AllocateInputs() + { + return new object[1]; + } + + public object Invoke(object instance, object[] inputs, out object[] outputs) + { + outputs = EmptyArray.Allocate(0); + + Message message = inputs[0] as Message; + if (message == null) + { + return null; + } + + string action = message.Headers.Action; + + //if (DiagnosticUtility.ShouldTraceInformation) + //{ + // TraceUtility.TraceEvent(TraceEventType.Information, TraceCode.UnhandledAction, + // SR.TraceCodeUnhandledAction, + // new StringTraceRecord("Action", action), + // this, null, message); + //} + + FaultCode code = FaultCode.CreateSenderFaultCode(AddressingStrings.ActionNotSupported, + message.Version.Addressing.Namespace); + string reasonText = SR.Format(SR.SFxNoEndpointMatchingContract, action); + FaultReason reason = new FaultReason(reasonText); + + FaultException exception = new FaultException(reason, code); + ErrorBehavior.ThrowAndCatch(exception); + + ServiceChannel serviceChannel = OperationContext.Current.InternalServiceChannel; + OperationContext.Current.OperationCompleted += + delegate (object sender, EventArgs e) + { + ChannelDispatcher channelDispatcher = dispatchRuntime.ChannelDispatcher; + if (!channelDispatcher.HandleError(exception) && serviceChannel.HasSession) + { + try + { + var helper = new TimeoutHelper(ChannelHandler.CloseAfterFaultTimeout); + serviceChannel.CloseAsync(helper.GetCancellationToken()).GetAwaiter().GetResult(); + } + catch (Exception ex) + { + if (Fx.IsFatal(ex)) + { + throw; + } + channelDispatcher.HandleError(ex); + } + } + }; + + if (dispatchRuntime.shared.EnableFaults) + { + MessageFault fault = MessageFault.CreateFault(code, reason, action); + return Message.CreateMessage(message.Version, fault, message.Version.Addressing.DefaultFaultAction); + } + else + { + OperationContext.Current.RequestContext.CloseAsync().GetAwaiter().GetResult(); + OperationContext.Current.RequestContext = null; + return null; + } + } + + public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException()); + } + + public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException()); + } + } + + class DispatchBehaviorCollection : SynchronizedCollection + { + DispatchRuntime outer; + + internal DispatchBehaviorCollection(DispatchRuntime outer) + : base(outer.ThisLock) + { + this.outer = outer; + } + + protected override void ClearItems() + { + outer.InvalidateRuntime(); + base.ClearItems(); + } + + protected override void InsertItem(int index, T item) + { + if (item == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("item"); + } + + outer.InvalidateRuntime(); + base.InsertItem(index, item); + } + + protected override void RemoveItem(int index) + { + outer.InvalidateRuntime(); + base.RemoveItem(index); + } + + protected override void SetItem(int index, T item) + { + if (item == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("item"); + } + + outer.InvalidateRuntime(); + base.SetItem(index, item); + } + } + + class OperationCollection : SynchronizedKeyedCollection + { + DispatchRuntime outer; + + internal OperationCollection(DispatchRuntime outer) + : base(outer.ThisLock) + { + this.outer = outer; + } + + protected override void ClearItems() + { + outer.InvalidateRuntime(); + base.ClearItems(); + } + + protected override string GetKeyForItem(DispatchOperation item) + { + return item.Name; + } + + protected override void InsertItem(int index, DispatchOperation item) + { + if (item == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("item"); + } + if (item.Parent != outer) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.SFxMismatchedOperationParent); + } + + outer.InvalidateRuntime(); + base.InsertItem(index, item); + } + + protected override void RemoveItem(int index) + { + outer.InvalidateRuntime(); + base.RemoveItem(index); + } + + protected override void SetItem(int index, DispatchOperation item) + { + if (item == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("item"); + } + if (item.Parent != outer) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.SFxMismatchedOperationParent); + } + + outer.InvalidateRuntime(); + base.SetItem(index, item); + } + } + + class CallbackInstanceProvider : IInstanceProvider + { + object IInstanceProvider.GetInstance(InstanceContext instanceContext) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.SFxCannotActivateCallbackInstace)); + } + + object IInstanceProvider.GetInstance(InstanceContext instanceContext, Message message) + { + throw TraceUtility.ThrowHelperError(new InvalidOperationException(SR.SFxCannotActivateCallbackInstace), message); + } + + void IInstanceProvider.ReleaseInstance(InstanceContext instanceContext, object instance) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.SFxCannotActivateCallbackInstace)); + } + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/DuplexChannelBinder.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/DuplexChannelBinder.cs new file mode 100644 index 000000000..7e6b5b4d8 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/DuplexChannelBinder.cs @@ -0,0 +1,960 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; +using System.Xml; +using CoreWCF.Runtime; +using CoreWCF.Channels; +using CoreWCF.Diagnostics; +using CoreWCF.Security; + +namespace CoreWCF.Dispatcher +{ + internal class DuplexChannelBinder : IChannelBinder + { + IDuplexChannel channel; + IRequestReplyCorrelator correlator; + TimeSpan defaultCloseTimeout; + TimeSpan defaultSendTimeout; + IdentityVerifier identityVerifier; + bool isSession; + Uri listenUri; + int pending; + bool syncPumpEnabled; + List requests; + List timedOutRequests; + ChannelHandler channelHandler; + volatile bool requestAborted; + bool initialized = false; + + public DuplexChannelBinder() { } + + internal void Init(IDuplexSessionChannel channel, IRequestReplyCorrelator correlator, Uri listenUri) + { + Init((IDuplexChannel)channel, correlator, listenUri); + isSession = true; + } + + internal void Init(IDuplexChannel channel, IRequestReplyCorrelator correlator, Uri listenUri) + { + if (initialized) + { + Fx.Assert(this.channel == channel, "Wrong channel when calling Init"); + Fx.Assert(this.correlator == correlator, "Wrong channel when calling Init"); + Fx.Assert(this.listenUri == listenUri, "Wrong listenUri when calling Init"); + return; + } + + Fx.Assert(channel != null, "caller must verify"); + Fx.Assert(correlator != null, "caller must verify"); + + this.channel = channel; + this.listenUri = listenUri; + this.channel.Faulted += new EventHandler(OnFaulted); + initialized = true; + } + + public IChannel Channel + { + get { return channel; } + } + + public TimeSpan DefaultCloseTimeout + { + get { return defaultCloseTimeout; } + set { defaultCloseTimeout = value; } + } + + internal ChannelHandler ChannelHandler + { + get + { + if (!(channelHandler != null)) + { + Fx.Assert("DuplexChannelBinder.ChannelHandler: (channelHandler != null)"); + } + return channelHandler; + } + set + { + if (!(channelHandler == null)) + { + Fx.Assert("DuplexChannelBinder.ChannelHandler: (channelHandler == null)"); + } + channelHandler = value; + } + } + + public TimeSpan DefaultSendTimeout + { + get { return defaultSendTimeout; } + set { defaultSendTimeout = value; } + } + + public bool HasSession + { + get { return isSession; } + } + + internal IdentityVerifier IdentityVerifier + { + get + { + if (identityVerifier == null) + { + identityVerifier = IdentityVerifier.CreateDefault(); + } + + return identityVerifier; + } + set + { + if (value == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value"); + } + + identityVerifier = value; + } + } + + public Uri ListenUri + { + get { return listenUri; } + } + + public EndpointAddress LocalAddress + { + get { return channel.LocalAddress; } + } + + bool Pumping + { + get + { + if (syncPumpEnabled) + return true; + + if (ChannelHandler != null && ChannelHandler.HasRegisterBeenCalled) + return true; + + return false; + } + } + + public EndpointAddress RemoteAddress + { + get { return channel.RemoteAddress; } + } + + List Requests + { + get + { + lock (ThisLock) + { + if (requests == null) + requests = new List(); + return requests; + } + } + } + + List TimedOutRequests + { + get + { + lock (ThisLock) + { + if (timedOutRequests == null) + { + timedOutRequests = new List(); + } + return timedOutRequests; + } + } + } + + object ThisLock + { + get { return this; } + } + + void OnFaulted(object sender, EventArgs e) + { + //Some unhandled exception happened on the channel. + //So close all pending requests so the callbacks (in case of async) + //on the requests are called. + AbortRequests(); + } + + public void Abort() + { + channel.Abort(); + AbortRequests(); + } + + public void CloseAfterFault(TimeSpan timeout) + { + var helper = new TimeoutHelper(timeout); + channel.CloseAsync(helper.GetCancellationToken()); + AbortRequests(); + } + + void AbortRequests() + { + IDuplexRequest[] array = null; + lock (ThisLock) + { + if (requests != null) + { + array = requests.ToArray(); + + foreach (IDuplexRequest request in array) + { + request.Abort(); + } + } + requests = null; + requestAborted = true; + } + + // Remove requests from the correlator since the channel might be either faulting or aborting, + // We are not going to get a reply for these requests. If they are not removed from the correlator, this will cause a leak. + // This operation does not have to be under the lock + if (array != null && array.Length > 0) + { + RequestReplyCorrelator requestReplyCorrelator = correlator as RequestReplyCorrelator; + if (requestReplyCorrelator != null) + { + foreach (IDuplexRequest request in array) + { + ICorrelatorKey keyedRequest = request as ICorrelatorKey; + if (keyedRequest != null) + { + requestReplyCorrelator.RemoveRequest(keyedRequest); + } + } + } + } + + //if there are any timed out requests, delete it from the correlator table + DeleteTimedoutRequestsFromCorrelator(); + } + + TimeoutException GetReceiveTimeoutException(TimeSpan timeout) + { + EndpointAddress address = channel.RemoteAddress ?? channel.LocalAddress; + if (address != null) + { + return new TimeoutException(SR.Format(SR.SFxRequestTimedOut2, address, timeout)); + } + else + { + return new TimeoutException(SR.Format(SR.SFxRequestTimedOut1, timeout)); + } + } + + internal bool HandleRequestAsReply(Message message) + { + UniqueId relatesTo = null; + try + { + relatesTo = message.Headers.RelatesTo; + } + catch (MessageHeaderException) + { + // ignore it + } + if (relatesTo == null) + { + return false; + } + else + { + return HandleRequestAsReplyCore(message); + } + } + + bool HandleRequestAsReplyCore(Message message) + { + IDuplexRequest request = correlator.Find(message, true); + if (request != null) + { + request.GotReply(message); + return true; + } + return false; + } + + public void EnsurePumping() + { + lock (ThisLock) + { + if (!syncPumpEnabled) + { + if (!ChannelHandler.HasRegisterBeenCalled) + { + ChannelHandler.Register(ChannelHandler); + } + } + } + } + + public async Task> TryReceiveAsync(CancellationToken token) + { + if (channel.State == CommunicationState.Faulted) + { + AbortRequests(); + return TryAsyncResult.FromResult((RequestContext)null); + } + + var result = await channel.TryReceiveAsync(token); + if (result.Success) + { + if (result.Result != null) + { + return TryAsyncResult.FromResult((RequestContext)new DuplexRequestContext(channel, result.Result, this)); + } + else + { + AbortRequests(); + return TryAsyncResult.FromResult((RequestContext)null); + } + } + else + { + return TryAsyncResult.FailedResult; + } + } + + public RequestContext CreateRequestContext(Message message) + { + return new DuplexRequestContext(channel, message, this); + } + + public Task SendAsync(Message message, CancellationToken token) + { + return channel.SendAsync(message, token); + } + + public async Task RequestAsync(Message message, CancellationToken token) + { + AsyncDuplexRequest duplexRequest = null; + bool optimized = false; + + RequestReplyCorrelator.PrepareRequest(message); + + lock (ThisLock) + { + if (!Pumping) + { + optimized = true; + syncPumpEnabled = true; + } + + if (!optimized) + duplexRequest = new AsyncDuplexRequest(this); + + RequestStarting(message, duplexRequest); + } + + if (optimized) + { + UniqueId messageId = message.Headers.MessageId; + + try + { + await channel.SendAsync(message, token); + //if (DiagnosticUtility.ShouldUseActivity && + // ServiceModelActivity.Current != null && + // ServiceModelActivity.Current.ActivityType == ActivityType.ProcessAction) + //{ + // ServiceModelActivity.Current.Suspend(); + //} + + for (;;) + { + var result = await channel.TryReceiveAsync(token); + if (!result.Success) + { + // TODO: Derive CancellationToken to attach timeout + throw TraceUtility.ThrowHelperError(GetReceiveTimeoutException(TimeSpan.Zero), message); + } + + if (result.Result == null) + { + AbortRequests(); + return null; + } + + if (result.Result.Headers.RelatesTo == messageId) + { + ThrowIfInvalidReplyIdentity(result.Result); + return result.Result; + } + else if (!HandleRequestAsReply(result.Result)) + { + // SFx drops a message here + //if (DiagnosticUtility.ShouldTraceInformation) + //{ + // EndpointDispatcher dispatcher = null; + // if (this.ChannelHandler != null && this.ChannelHandler.Channel != null) + // { + // dispatcher = this.ChannelHandler.Channel.EndpointDispatcher; + // } + // TraceUtility.TraceDroppedMessage(reply, dispatcher); + //} + result.Result.Close(); + } + } + } + finally + { + lock (ThisLock) + { + RequestCompleting(null); + syncPumpEnabled = false; + if (pending > 0) + EnsurePumping(); + } + } + } + else + { + await channel.SendAsync(message, token); + EnsurePumping(); + return await duplexRequest.WaitForReplyAsync(token); + } + } + + // ASSUMPTION: (mmaruch) caller holds lock (this.mutex) + void RequestStarting(Message message, IDuplexRequest request) + { + if (request != null) + { + Requests.Add(request); + if (!requestAborted) + { + correlator.Add(message, request); + } + } + pending++; + + } + + // ASSUMPTION: (mmaruch) caller holds lock (this.mutex) + void RequestCompleting(IDuplexRequest request) + { + pending--; + if (pending == 0) + { + requests = null; + } + else if ((request != null) && (requests != null)) + { + requests.Remove(request); + } + } + + // ASSUMPTION: caller holds ThisLock + void AddToTimedOutRequestList(ICorrelatorKey request) + { + Fx.Assert(request != null, "request cannot be null"); + TimedOutRequests.Add(request); + } + + // ASSUMPTION: caller holds ThisLock + void RemoveFromTimedOutRequestList(ICorrelatorKey request) + { + Fx.Assert(request != null, "request cannot be null"); + if (timedOutRequests != null) + { + timedOutRequests.Remove(request); + } + } + + void DeleteTimedoutRequestsFromCorrelator() + { + ICorrelatorKey[] array = null; + if (timedOutRequests != null && timedOutRequests.Count > 0) + { + lock (ThisLock) + { + if (timedOutRequests != null && timedOutRequests.Count > 0) + { + array = timedOutRequests.ToArray(); + timedOutRequests = null; + } + } + } + + // Remove requests from the correlator since the channel might be either faulting, aborting or closing + // We are not going to get a reply for these timed out requests. If they are not removed from the correlator, this will cause a leak. + // This operation does not have to be under the lock + if (array != null && array.Length > 0) + { + RequestReplyCorrelator requestReplyCorrelator = correlator as RequestReplyCorrelator; + if (requestReplyCorrelator != null) + { + foreach (ICorrelatorKey request in array) + { + requestReplyCorrelator.RemoveRequest(request); + } + } + } + + } + + //[MethodImpl(MethodImplOptions.NoInlining)] + //void EnsureIncomingIdentity(SecurityMessageProperty property, EndpointAddress address, Message reply) + //{ + // this.IdentityVerifier.EnsureIncomingIdentity(address, property.ServiceSecurityContext.AuthorizationContext); + //} + + void ThrowIfInvalidReplyIdentity(Message reply) + { + //if (!this.isSession) + //{ + // SecurityMessageProperty property = reply.Properties.Security; + // EndpointAddress address = this.channel.RemoteAddress; + + // if ((property != null) && (address != null)) + // { + // EnsureIncomingIdentity(property, address, reply); + // } + //} + } + + public Task WaitForMessageAsync(CancellationToken token) + { + return channel.WaitForMessageAsync(token); + } + + class DuplexRequestContext : RequestContextBase + { + DuplexChannelBinder binder; + IDuplexChannel channel; + + internal DuplexRequestContext(IDuplexChannel channel, Message request, DuplexChannelBinder binder) + : base(request, binder.DefaultCloseTimeout, binder.DefaultSendTimeout) + { + this.channel = channel; + this.binder = binder; + } + + protected override void OnAbort() + { + } + + protected override Task OnCloseAsync(CancellationToken token) + { + return Task.CompletedTask; + } + + protected override Task OnReplyAsync(Message message, CancellationToken token) + { + if (message != null) + { + return channel.SendAsync(message, token); + } + + return Task.CompletedTask; + } + } + + interface IDuplexRequest + { + void Abort(); + void GotReply(Message reply); + } + + class AsyncDuplexRequest : IDuplexRequest, ICorrelatorKey + { + Message reply; + DuplexChannelBinder parent; + AsyncManualResetEvent wait = new AsyncManualResetEvent(); + int waitCount = 0; + RequestReplyCorrelator.Key requestCorrelatorKey; + + internal AsyncDuplexRequest(DuplexChannelBinder parent) + { + this.parent = parent; + } + + RequestReplyCorrelator.Key ICorrelatorKey.RequestCorrelatorKey + { + get + { + return requestCorrelatorKey; + } + set + { + Fx.Assert(requestCorrelatorKey == null, "RequestCorrelatorKey is already set for this request"); + requestCorrelatorKey = value; + } + } + + public void Abort() + { + wait.Set(); + } + + internal async Task WaitForReplyAsync(CancellationToken token) + { + try + { + if(!await wait.WaitAsync(token)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(parent.GetReceiveTimeoutException(TimeSpan.Zero)); + } + } + finally + { + CloseWaitHandle(); + } + + parent.ThrowIfInvalidReplyIdentity(reply); + return reply; + } + + public void GotReply(Message reply) + { + lock (parent.ThisLock) + { + parent.RequestCompleting(this); + } + this.reply = reply; + wait.Set(); + CloseWaitHandle(); + } + + void CloseWaitHandle() + { + if (Interlocked.Increment(ref waitCount) == 2) + { + wait.Dispose(); + } + } + } + + // used to read-ahead by a single message and auto-close the session when we read null + class AutoCloseDuplexSessionChannel : IDuplexSessionChannel + { + IDuplexSessionChannel innerChannel; + InputQueue pendingMessages; + Action messageDequeuedCallback; + CloseState closeState; + + public AutoCloseDuplexSessionChannel(IDuplexSessionChannel innerChannel) + { + this.innerChannel = innerChannel; + pendingMessages = new InputQueue(); + messageDequeuedCallback = new Action(StartBackgroundReceive); // kick off a new receive when a message is picked up + closeState = new CloseState(); + } + + object ThisLock + { + get + { + return this; + } + } + + public EndpointAddress LocalAddress + { + get { return innerChannel.LocalAddress; } + } + + public EndpointAddress RemoteAddress + { + get { return innerChannel.RemoteAddress; } + } + + public Uri Via + { + get { return innerChannel.Via; } + } + + public IDuplexSession Session + { + get { return innerChannel.Session; } + } + + public CommunicationState State + { + get { return innerChannel.State; } + } + + public event EventHandler Closing + { + add { innerChannel.Closing += value; } + remove { innerChannel.Closing -= value; } + } + + public event EventHandler Closed + { + add { innerChannel.Closed += value; } + remove { innerChannel.Closed -= value; } + } + + public event EventHandler Faulted + { + add { innerChannel.Faulted += value; } + remove { innerChannel.Faulted -= value; } + } + + public event EventHandler Opened + { + add { innerChannel.Opened += value; } + remove { innerChannel.Opened -= value; } + } + + public event EventHandler Opening + { + add { innerChannel.Opening += value; } + remove { innerChannel.Opening -= value; } + } + + TimeSpan DefaultCloseTimeout + { + get + { + IDefaultCommunicationTimeouts defaultTimeouts = innerChannel as IDefaultCommunicationTimeouts; + + if (defaultTimeouts != null) + { + return defaultTimeouts.CloseTimeout; + } + else + { + return ServiceDefaults.CloseTimeout; + } + } + } + + TimeSpan DefaultReceiveTimeout + { + get + { + IDefaultCommunicationTimeouts defaultTimeouts = innerChannel as IDefaultCommunicationTimeouts; + + if (defaultTimeouts != null) + { + return defaultTimeouts.ReceiveTimeout; + } + else + { + return ServiceDefaults.ReceiveTimeout; + } + } + } + + // kick off an async receive so that we notice when the server is trying to shutdown + async void StartBackgroundReceive() + { + Exception exceptionFromBeginReceive = null; + try + { + var message = await innerChannel.ReceiveAsync(CancellationToken.None); + if (message == null) + { + // we've hit end of session, time for auto-close to kick in + pendingMessages.Shutdown(); + await CloseInnerChannelAsync(); + } + else + { + pendingMessages.EnqueueAndDispatch(message, messageDequeuedCallback, true); + } + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + exceptionFromBeginReceive = e; + + pendingMessages.EnqueueAndDispatch(e, messageDequeuedCallback, false); + } + } + + async Task CloseInnerChannelAsync() + { + lock (ThisLock) + { + if (!closeState.TryBackgroundClose() || State != CommunicationState.Opened) + { + return; + } + } + + Exception backgroundCloseException = null; + try + { + await innerChannel.CloseAsync(); + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + + innerChannel.Abort(); + + backgroundCloseException = e; + } + + if (backgroundCloseException != null) + { + // stash away exception to throw out of user's Close() + closeState.CaptureBackgroundException(backgroundCloseException); + } + } + + public Task ReceiveAsync() + { + var helper = new TimeoutHelper(DefaultReceiveTimeout); + return ReceiveAsync(helper.GetCancellationToken()); + } + + public Task ReceiveAsync(CancellationToken token) + { + return pendingMessages.DequeueAsync(token); + } + + public Task> TryReceiveAsync(CancellationToken token) + { + return pendingMessages.TryDequeueAsync(token); + } + + public Task WaitForMessageAsync(CancellationToken token) + { + return pendingMessages.WaitForItemAsync(token); + } + + public T GetProperty() where T : class + { + return innerChannel.GetProperty(); + } + + public void Abort() + { + innerChannel.Abort(); + Cleanup(); + } + + public Task CloseAsync() + { + var helper = new TimeoutHelper(DefaultCloseTimeout); + return CloseAsync(helper.GetCancellationToken()); + } + + public async Task CloseAsync(CancellationToken token) + { + bool performChannelClose; + lock (ThisLock) + { + performChannelClose = closeState.TryUserClose(); + } + if (performChannelClose) + { + await innerChannel.CloseAsync(token); + } + else + { + await closeState.WaitForBackgroundCloseAsync(token); + } + Cleanup(); + } + + // called from both Abort and Close paths + void Cleanup() + { + pendingMessages.Dispose(); + } + + public async Task OpenAsync() + { + await innerChannel.OpenAsync(); + StartBackgroundReceive(); + } + + public async Task OpenAsync(CancellationToken token) + { + await innerChannel.OpenAsync(token); + StartBackgroundReceive(); + } + + public Task SendAsync(Message message) + { + return innerChannel.SendAsync(message); + } + + public Task SendAsync(Message message, CancellationToken token) + { + return innerChannel.SendAsync(message, token); + } + + class CloseState + { + bool userClose; + InputQueue backgroundCloseData; + + public CloseState() + { + } + + public bool TryBackgroundClose() + { + Fx.Assert(backgroundCloseData == null, "can't try twice"); + if (!userClose) + { + backgroundCloseData = new InputQueue(); + return true; + } + else + { + return false; + } + } + + public void FinishBackgroundClose() + { + Fx.Assert(backgroundCloseData != null, "Only callable from background close"); + backgroundCloseData.Close(); + } + + public bool TryUserClose() + { + if (backgroundCloseData == null) + { + userClose = true; + return true; + } + else + { + return false; + } + } + + public async Task WaitForBackgroundCloseAsync(CancellationToken token) + { + Fx.Assert(backgroundCloseData != null, "Need to check background close first"); + object dummy = await backgroundCloseData.DequeueAsync(token); + Fx.Assert(dummy == null, "we should get an exception or null"); + } + + public void CaptureBackgroundException(Exception exception) + { + backgroundCloseData.EnqueueAndDispatch(exception, null, true); + } + + } + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/EndpointAddressMessageFilter.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/EndpointAddressMessageFilter.cs new file mode 100644 index 000000000..4936d208f --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/EndpointAddressMessageFilter.cs @@ -0,0 +1,322 @@ +using System; +using System.Collections.Generic; +using System.Text; +using CoreWCF.Channels; + +namespace CoreWCF.Dispatcher +{ + using QName = CoreWCF.Dispatcher.EndpointAddressProcessor.QName; + using HeaderBit = CoreWCF.Dispatcher.EndpointAddressProcessor.HeaderBit; + + internal class EndpointAddressMessageFilter : MessageFilter + { + EndpointAddress address; + bool includeHostNameInComparison; + EndpointAddressMessageFilterHelper helper; + UriComparer comparer; + + public EndpointAddressMessageFilter(EndpointAddress address) + : this(address, false) + { + } + + public EndpointAddressMessageFilter(EndpointAddress address, bool includeHostNameInComparison) + { + if (address == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("address"); + } + + this.address = address; + this.includeHostNameInComparison = includeHostNameInComparison; + helper = new EndpointAddressMessageFilterHelper(this.address); + + if (includeHostNameInComparison) + { + comparer = HostUriComparer.Value; + } + else + { + comparer = NoHostUriComparer.Value; + } + } + + public EndpointAddress Address + { + get + { + return address; + } + } + + public bool IncludeHostNameInComparison + { + get { return includeHostNameInComparison; } + } + + protected internal override IMessageFilterTable CreateFilterTable() + { + return new EndpointAddressMessageFilterTable(); + } + + public override bool Match(MessageBuffer messageBuffer) + { + if (messageBuffer == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("messageBuffer"); + } + + Message msg = messageBuffer.CreateMessage(); + try + { + return Match(msg); + } + finally + { + msg.Close(); + } + } + + public override bool Match(Message message) + { + if (message == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("message"); + } + + // To + Uri to = message.Headers.To; + Uri actingAs = address.Uri; + + if (to == null || !comparer.Equals(actingAs, to)) + { + return false; + } + + return helper.Match(message); + } + + internal Dictionary HeaderLookup + { + get { return helper.HeaderLookup; } + } + + internal bool ComparePort + { + set + { + comparer.ComparePort = value; + } + } + + internal abstract class UriComparer : EqualityComparer + { + protected UriComparer() + { + ComparePort = true; + } + + protected abstract bool CompareHost { get; } + + internal bool ComparePort + { + get; + set; + } + + public override bool Equals(Uri u1, Uri u2) + { + return EndpointAddress.UriEquals(u1, u2, true /* ignoreCase */, CompareHost, ComparePort); + } + + public override int GetHashCode(Uri uri) + { + return EndpointAddress.UriGetHashCode(uri, CompareHost, ComparePort); + } + } + + internal sealed class HostUriComparer : UriComparer + { + internal static readonly UriComparer Value = new HostUriComparer(); + + HostUriComparer() + { + } + + protected override bool CompareHost + { + get + { + return true; + } + } + } + + internal sealed class NoHostUriComparer : UriComparer + { + internal static readonly UriComparer Value = new NoHostUriComparer(); + + NoHostUriComparer() + { + } + + protected override bool CompareHost + { + get + { + return false; + } + } + } + } + + internal class EndpointAddressMessageFilterHelper + { + EndpointAddress address; + WeakReference processorPool; + + int size; + byte[] mask; + Dictionary qnameLookup; + Dictionary headerLookup; + + internal EndpointAddressMessageFilterHelper(EndpointAddress address) + { + this.address = address; + + if (this.address.Headers.Count > 0) + { + CreateMask(); + processorPool = new WeakReference(null); + } + else + { + qnameLookup = null; + headerLookup = null; + size = 0; + mask = null; + } + } + + void CreateMask() + { + int nextBit = 0; + string key; + HeaderBit[] bits; + QName qname; + qnameLookup = new Dictionary(EndpointAddressProcessor.QNameComparer); + headerLookup = new Dictionary(); + StringBuilder builder = null; + + for (int i = 0; i < address.Headers.Count; ++i) + { + if (builder == null) + builder = new StringBuilder(); + else + builder.Remove(0, builder.Length); + + + key = address.Headers[i].GetComparableForm(builder); + if (headerLookup.TryGetValue(key, out bits)) + { + Array.Resize(ref bits, bits.Length + 1); + bits[bits.Length - 1] = new HeaderBit(nextBit++); + headerLookup[key] = bits; + } + else + { + + headerLookup.Add(key, new HeaderBit[] { new HeaderBit(nextBit++) }); + AddressHeader parameter = address.Headers[i]; + + qname.name = parameter.Name; + qname.ns = parameter.Namespace; + qnameLookup[qname] = 1; + } + } + + if (nextBit == 0) + { + size = 0; + } + else + { + size = (nextBit - 1) / 8 + 1; + } + + if (size > 0) + { + mask = new byte[size]; + for (int i = 0; i < size - 1; ++i) + { + mask[i] = 0xff; + } + + if ((nextBit % 8) == 0) + { + mask[size - 1] = 0xff; + } + else + { + mask[size - 1] = (byte)((1 << (nextBit % 8)) - 1); + } + } + } + + internal Dictionary HeaderLookup + { + get + { + if (headerLookup == null) + headerLookup = new Dictionary(); + return headerLookup; + } + } + + EndpointAddressProcessor CreateProcessor(int length) + { + if (processorPool.Target != null) + { + lock (processorPool) + { + object o = processorPool.Target; + if (o != null) + { + EndpointAddressProcessor p = (EndpointAddressProcessor)o; + processorPool.Target = p.Next; + p.Next = null; + p.Clear(length); + return p; + } + } + } + + return new EndpointAddressProcessor(length); + } + + internal bool Match(Message message) + { + if (size == 0) + { + return true; + } + + EndpointAddressProcessor context = CreateProcessor(size); + context.ProcessHeaders(message, qnameLookup, headerLookup); + bool result = context.TestExact(mask); + ReleaseProcessor(context); + return result; + } + + void ReleaseProcessor(EndpointAddressProcessor context) + { + lock (processorPool) + { + context.Next = processorPool.Target as EndpointAddressProcessor; + processorPool.Target = context; + } + } + + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/EndpointAddressMessageFilterTable.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/EndpointAddressMessageFilterTable.cs new file mode 100644 index 000000000..0a684ff48 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/EndpointAddressMessageFilterTable.cs @@ -0,0 +1,813 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using CoreWCF.Channels; +using CoreWCF.Diagnostics; + +namespace CoreWCF.Dispatcher +{ + using QName = CoreWCF.Dispatcher.EndpointAddressProcessor.QName; + using HeaderBit = CoreWCF.Dispatcher.EndpointAddressProcessor.HeaderBit; + + internal class EndpointAddressMessageFilterTable : IMessageFilterTable + { + protected Dictionary filters; + protected Dictionary candidates; + WeakReference processorPool; + + int size; + int nextBit; + Dictionary headerLookup; + Dictionary toHostLookup; + Dictionary toNoHostLookup; + + internal class ProcessorPool + { + EndpointAddressProcessor processor; + + internal ProcessorPool() + { + } + + internal EndpointAddressProcessor Pop() + { + EndpointAddressProcessor p = processor; + if (null != p) + { + processor = (EndpointAddressProcessor)p.next; + p.next = null; + return p; + } + return null; + } + + internal void Push(EndpointAddressProcessor p) + { + p.next = processor; + processor = p; + } + } + + public EndpointAddressMessageFilterTable() + { + processorPool = new WeakReference(null); + + size = 0; + nextBit = 0; + + filters = new Dictionary(); + candidates = new Dictionary(); + headerLookup = new Dictionary(); + InitializeLookupTables(); + } + + protected virtual void InitializeLookupTables() + { + toHostLookup = new Dictionary(EndpointAddressMessageFilter.HostUriComparer.Value); + toNoHostLookup = new Dictionary(EndpointAddressMessageFilter.NoHostUriComparer.Value); + } + + public TFilterData this[MessageFilter filter] + { + get + { + return filters[filter]; + } + set + { + if (filters.ContainsKey(filter)) + { + filters[filter] = value; + candidates[filter].data = value; + } + else + { + Add(filter, value); + } + } + } + + public int Count + { + get + { + return filters.Count; + } + } + + public bool IsReadOnly + { + get + { + return false; + } + } + + public ICollection Keys + { + get + { + return filters.Keys; + } + } + + public ICollection Values + { + get + { + return filters.Values; + } + } + + public virtual void Add(MessageFilter filter, TFilterData data) + { + if (filter == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("filter"); + } + + Add((EndpointAddressMessageFilter)filter, data); + } + + public virtual void Add(EndpointAddressMessageFilter filter, TFilterData data) + { + if (filter == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("filter"); + } + + filters.Add(filter, data); + + // Create the candidate + byte[] mask = BuildMask(filter.HeaderLookup); + Candidate can = new Candidate(filter, data, mask, filter.HeaderLookup); + candidates.Add(filter, can); + + CandidateSet cset; + Uri soapToAddress = filter.Address.Uri; + if (filter.IncludeHostNameInComparison) + { + if (!toHostLookup.TryGetValue(soapToAddress, out cset)) + { + cset = new CandidateSet(); + toHostLookup.Add(soapToAddress, cset); + } + } + else + { + if (!toNoHostLookup.TryGetValue(soapToAddress, out cset)) + { + cset = new CandidateSet(); + toNoHostLookup.Add(soapToAddress, cset); + } + } + cset.candidates.Add(can); + + IncrementQNameCount(cset, filter.Address); + } + + protected void IncrementQNameCount(CandidateSet cset, EndpointAddress address) + { + // Update the QName ref count + QName qname; + int cnt; + for (int i = 0; i < address.Headers.Count; ++i) + { + AddressHeader parameter = address.Headers[i]; + qname.name = parameter.Name; + qname.ns = parameter.Namespace; + if (cset.qnames.TryGetValue(qname, out cnt)) + { + cset.qnames[qname] = cnt + 1; + } + else + { + cset.qnames.Add(qname, 1); + } + } + } + + public void Add(KeyValuePair item) + { + Add(item.Key, item.Value); + } + + protected byte[] BuildMask(Dictionary headerLookup) + { + HeaderBit[] bits; + byte[] mask = null; + foreach (KeyValuePair item in headerLookup) + { + if (this.headerLookup.TryGetValue(item.Key, out bits)) + { + if (bits.Length < item.Value.Length) + { + int old = bits.Length; + Array.Resize(ref bits, item.Value.Length); + for (int i = old; i < item.Value.Length; ++i) + { + bits[i] = new HeaderBit(nextBit++); + } + this.headerLookup[item.Key] = bits; + } + } + else + { + bits = new HeaderBit[item.Value.Length]; + for (int i = 0; i < item.Value.Length; ++i) + { + bits[i] = new HeaderBit(nextBit++); + } + this.headerLookup.Add(item.Key, bits); + } + + for (int i = 0; i < item.Value.Length; ++i) + { + bits[i].AddToMask(ref mask); + } + } + + if (nextBit == 0) + { + size = 0; + } + else + { + size = (nextBit - 1) / 8 + 1; + } + + return mask; + } + + public void Clear() + { + size = 0; + nextBit = 0; + filters.Clear(); + candidates.Clear(); + headerLookup.Clear(); + ClearLookupTables(); + } + + protected virtual void ClearLookupTables() + { + if (toHostLookup != null) + { + toHostLookup.Clear(); + } + if (toNoHostLookup != null) + { + toNoHostLookup.Clear(); + } + } + + public bool Contains(KeyValuePair item) + { + return ((ICollection>)filters).Contains(item); + } + + public bool ContainsKey(MessageFilter filter) + { + if (filter == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("filter"); + } + return filters.ContainsKey(filter); + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + ((ICollection>)filters).CopyTo(array, arrayIndex); + } + + EndpointAddressProcessor CreateProcessor(int length) + { + EndpointAddressProcessor p = null; + lock (processorPool) + { + ProcessorPool pool = processorPool.Target as ProcessorPool; + if (null != pool) + { + p = pool.Pop(); + } + } + + if (null != p) + { + p.Clear(length); + return p; + } + + return new EndpointAddressProcessor(length); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public IEnumerator> GetEnumerator() + { + return filters.GetEnumerator(); + } + + internal virtual bool TryMatchCandidateSet(Uri to, bool includeHostNameInComparison, out CandidateSet cset) + { + if (includeHostNameInComparison) + { + return toHostLookup.TryGetValue(to, out cset); + } + else + { + return toNoHostLookup.TryGetValue(to, out cset); + } + } + + Candidate InnerMatch(Message message) + { + Uri to = message.Headers.To; + if (to == null) + { + return null; + } + + CandidateSet cset = null; + Candidate can = null; + if (TryMatchCandidateSet(to, true/*includeHostNameInComparison*/, out cset)) + { + can = GetSingleMatch(cset, message); + } + if (TryMatchCandidateSet(to, false/*includeHostNameInComparison*/, out cset)) + { + Candidate c = GetSingleMatch(cset, message); + if (c != null) + { + if (can != null) + { + Collection matches = new Collection(); + matches.Add(can.filter); + matches.Add(c.filter); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MultipleFilterMatchesException(SR.FilterMultipleMatches, null, matches)); + } + can = c; + } + } + + return can; + } + + Candidate GetSingleMatch(CandidateSet cset, Message message) + { + int candiCount = cset.candidates.Count; + + if (cset.qnames.Count == 0) + { + if (candiCount == 0) + { + return null; + } + else if (candiCount == 1) + { + return cset.candidates[0]; + } + else + { + Collection matches = new Collection(); + for (int i = 0; i < candiCount; ++i) + { + matches.Add(cset.candidates[i].filter); + } + throw TraceUtility.ThrowHelperError(new MultipleFilterMatchesException(SR.FilterMultipleMatches, null, matches), message); + } + } + + EndpointAddressProcessor context = CreateProcessor(size); + context.ProcessHeaders(message, cset.qnames, headerLookup); + + Candidate can = null; + List candis = cset.candidates; + for (int i = 0; i < candiCount; ++i) + { + if (context.TestMask(candis[i].mask)) + { + if (can != null) + { + Collection matches = new Collection(); + matches.Add(can.filter); + matches.Add(candis[i].filter); + throw TraceUtility.ThrowHelperError(new MultipleFilterMatchesException(SR.FilterMultipleMatches, null, matches), message); + } + can = candis[i]; + } + } + + ReleaseProcessor(context); + + return can; + } + + void InnerMatchData(Message message, ICollection results) + { + Uri to = message.Headers.To; + if (to != null) + { + CandidateSet cset; + if (TryMatchCandidateSet(to, true /*includeHostNameInComparison*/, out cset)) + { + InnerMatchData(message, results, cset); + } + if (TryMatchCandidateSet(to, false /*includeHostNameInComparison*/, out cset)) + { + InnerMatchData(message, results, cset); + } + } + } + + void InnerMatchData(Message message, ICollection results, CandidateSet cset) + { + EndpointAddressProcessor context = CreateProcessor(size); + context.ProcessHeaders(message, cset.qnames, headerLookup); + + List candis = cset.candidates; + for (int i = 0; i < candis.Count; ++i) + { + if (context.TestMask(candis[i].mask)) + { + results.Add(candis[i].data); + } + } + + ReleaseProcessor(context); + } + + protected void InnerMatchFilters(Message message, ICollection results) + { + Uri to = message.Headers.To; + if (to != null) + { + CandidateSet cset; + if (TryMatchCandidateSet(to, true/*includeHostNameInComparison*/, out cset)) + { + InnerMatchFilters(message, results, cset); + } + if (TryMatchCandidateSet(to, false/*includeHostNameInComparison*/, out cset)) + { + InnerMatchFilters(message, results, cset); + } + } + } + + void InnerMatchFilters(Message message, ICollection results, CandidateSet cset) + { + EndpointAddressProcessor context = CreateProcessor(size); + context.ProcessHeaders(message, cset.qnames, headerLookup); + + List candis = cset.candidates; + for (int i = 0; i < candis.Count; ++i) + { + if (context.TestMask(candis[i].mask)) + { + results.Add(candis[i].filter); + } + } + + ReleaseProcessor(context); + } + + public bool GetMatchingValue(Message message, out TFilterData data) + { + if (message == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("message"); + } + + Candidate can = InnerMatch(message); + if (can == null) + { + data = default(TFilterData); + return false; + } + + data = can.data; + return true; + } + + public bool GetMatchingValue(MessageBuffer messageBuffer, out TFilterData data) + { + if (messageBuffer == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("messageBuffer"); + } + + Message msg = messageBuffer.CreateMessage(); + Candidate can = null; + try + { + can = InnerMatch(msg); + } + finally + { + msg.Close(); + } + + if (can == null) + { + data = default(TFilterData); + return false; + } + + data = can.data; + return true; + } + + public bool GetMatchingValues(Message message, ICollection results) + { + if (message == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("message"); + } + + if (results == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("results"); + } + + int count = results.Count; + InnerMatchData(message, results); + return count != results.Count; + } + + public bool GetMatchingValues(MessageBuffer messageBuffer, ICollection results) + { + if (messageBuffer == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("messageBuffer"); + } + + if (results == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("results"); + } + + Message msg = messageBuffer.CreateMessage(); + try + { + int count = results.Count; + InnerMatchData(msg, results); + return count != results.Count; + } + finally + { + msg.Close(); + } + } + + public bool GetMatchingFilter(Message message, out MessageFilter filter) + { + if (message == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("message"); + } + + Candidate can = InnerMatch(message); + if (can != null) + { + filter = can.filter; + return true; + } + + filter = null; + return false; + } + + public bool GetMatchingFilter(MessageBuffer messageBuffer, out MessageFilter filter) + { + if (messageBuffer == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("messageBuffer"); + } + + Message msg = messageBuffer.CreateMessage(); + Candidate can = null; + try + { + can = InnerMatch(msg); + } + finally + { + msg.Close(); + } + + if (can != null) + { + filter = can.filter; + return true; + } + + filter = null; + return false; + } + + public bool GetMatchingFilters(Message message, ICollection results) + { + if (message == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("message"); + } + + if (results == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("results"); + } + + int count = results.Count; + InnerMatchFilters(message, results); + return count != results.Count; + } + + public bool GetMatchingFilters(MessageBuffer messageBuffer, ICollection results) + { + if (messageBuffer == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("messageBuffer"); + } + + if (results == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("results"); + } + + Message msg = messageBuffer.CreateMessage(); + try + { + int count = results.Count; + InnerMatchFilters(msg, results); + return count != results.Count; + } + finally + { + msg.Close(); + } + } + + protected void RebuildMasks() + { + nextBit = 0; + size = 0; + + // Clear out all the bits. + headerLookup.Clear(); + + // Rebuild the masks + foreach (Candidate can in candidates.Values) + { + can.mask = BuildMask(can.headerLookup); + } + } + + void ReleaseProcessor(EndpointAddressProcessor processor) + { + lock (processorPool) + { + ProcessorPool pool = processorPool.Target as ProcessorPool; + if (null == pool) + { + pool = new ProcessorPool(); + processorPool.Target = pool; + } + pool.Push(processor); + } + } + + public virtual bool Remove(MessageFilter filter) + { + if (filter == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("filter"); + } + + EndpointAddressMessageFilter saFilter = filter as EndpointAddressMessageFilter; + if (saFilter != null) + { + return Remove(saFilter); + } + + return false; + } + + public virtual bool Remove(EndpointAddressMessageFilter filter) + { + if (filter == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("filter"); + } + + if (!filters.Remove(filter)) + { + return false; + } + + Candidate can = candidates[filter]; + Uri soapToAddress = filter.Address.Uri; + + CandidateSet cset = null; + if (filter.IncludeHostNameInComparison) + { + cset = toHostLookup[soapToAddress]; + } + else + { + cset = toNoHostLookup[soapToAddress]; + } + + candidates.Remove(filter); + + if (cset.candidates.Count == 1) + { + if (filter.IncludeHostNameInComparison) + { + toHostLookup.Remove(soapToAddress); + } + else + { + toNoHostLookup.Remove(soapToAddress); + } + } + else + { + DecrementQNameCount(cset, filter.Address); + + // Remove Candidate + cset.candidates.Remove(can); + } + + RebuildMasks(); + return true; + } + + protected void DecrementQNameCount(CandidateSet cset, EndpointAddress address) + { + // Adjust QName counts + QName qname; + for (int i = 0; i < address.Headers.Count; ++i) + { + AddressHeader parameter = address.Headers[i]; + qname.name = parameter.Name; + qname.ns = parameter.Namespace; + int cnt = cset.qnames[qname]; + if (cnt == 1) + { + cset.qnames.Remove(qname); + } + else + { + cset.qnames[qname] = cnt - 1; + } + } + } + + public bool Remove(KeyValuePair item) + { + if (((ICollection>)filters).Contains(item)) + { + return Remove(item.Key); + } + return false; + } + + internal class Candidate + { + internal MessageFilter filter; + internal TFilterData data; + internal byte[] mask; + internal Dictionary headerLookup; + + internal Candidate(MessageFilter filter, TFilterData data, byte[] mask, Dictionary headerLookup) + { + this.filter = filter; + this.data = data; + this.mask = mask; + this.headerLookup = headerLookup; + } + } + + internal class CandidateSet + { + internal Dictionary qnames; + internal List candidates; + + internal CandidateSet() + { + qnames = new Dictionary(EndpointAddressProcessor.QNameComparer); + candidates = new List(); + } + } + + public bool TryGetValue(MessageFilter filter, out TFilterData data) + { + return filters.TryGetValue(filter, out data); + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/EndpointAddressProcessor.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/EndpointAddressProcessor.cs new file mode 100644 index 000000000..b2be2a5ff --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/EndpointAddressProcessor.cs @@ -0,0 +1,353 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Xml; +using System.Xml.Schema; +using CoreWCF.Runtime; +using CoreWCF.Channels; + +namespace CoreWCF.Dispatcher +{ + class EndpointAddressProcessor + { + internal static readonly QNameKeyComparer QNameComparer = new QNameKeyComparer(); + + // QName Attributes + //internal static readonly string XsiNs = XmlSchema.InstanceNamespace; + internal static readonly string XsiNs = "http://www.w3.org/2001/XMLSchema-instance"; + internal const string SerNs = "http://schemas.microsoft.com/2003/10/Serialization/"; + internal const string TypeLN = "type"; + internal const string ItemTypeLN = "ItemType"; + internal const string FactoryTypeLN = "FactoryType"; + + // Pooling + internal EndpointAddressProcessor next; + + StringBuilder builder; + byte[] resultData; + + internal EndpointAddressProcessor(int length) + { + builder = new StringBuilder(); + resultData = new byte[length]; + } + + internal EndpointAddressProcessor Next + { + get + { + return next; + } + set + { + next = value; + } + } + + internal static string GetComparableForm(StringBuilder builder, XmlReader reader) + { + List attrSet = new List(); + int valueLength = -1; + while (!reader.EOF) + { + XmlNodeType type = reader.MoveToContent(); + switch (type) + { + case XmlNodeType.Element: + CompleteValue(builder, valueLength); + valueLength = -1; + + builder.Append("<"); + AppendString(builder, reader.LocalName); + builder.Append(":"); + AppendString(builder, reader.NamespaceURI); + builder.Append(" "); + + // Scan attributes + attrSet.Clear(); + if (reader.MoveToFirstAttribute()) + { + do + { + // Ignore namespaces + if (reader.Prefix == "xmlns" || reader.Name == "xmlns") + { + continue; + } + if (reader.LocalName == AddressingStrings.IsReferenceParameter && reader.NamespaceURI == Addressing10Strings.Namespace) + { + continue; // ignore IsReferenceParameter + } + + string val = reader.Value; + if ((reader.LocalName == TypeLN && reader.NamespaceURI == XsiNs) || + (reader.NamespaceURI == SerNs && (reader.LocalName == ItemTypeLN || reader.LocalName == FactoryTypeLN))) + { + string local, ns; + XmlUtil.ParseQName(reader, val, out local, out ns); + val = local + "^" + local.Length.ToString(CultureInfo.InvariantCulture) + ":" + ns + "^" + ns.Length.ToString(CultureInfo.InvariantCulture); + } + else if (reader.LocalName == XD.UtilityDictionary.IdAttribute.Value && reader.NamespaceURI == XD.UtilityDictionary.Namespace.Value) + { + // ignore wsu:Id attributes added by security to sign the header + continue; + } + attrSet.Add(new Attr(reader.LocalName, reader.NamespaceURI, val)); + } while (reader.MoveToNextAttribute()); + } + reader.MoveToElement(); + + if (attrSet.Count > 0) + { + attrSet.Sort(); + for (int i = 0; i < attrSet.Count; ++i) + { + Attr a = attrSet[i]; + + AppendString(builder, a.local); + builder.Append(":"); + AppendString(builder, a.ns); + builder.Append("=\""); + AppendString(builder, a.val); + builder.Append("\" "); + } + } + + if (reader.IsEmptyElement) + builder.Append(">"); // Should be the same as an empty tag. + else + builder.Append(">"); + break; + + case XmlNodeType.EndElement: + CompleteValue(builder, valueLength); + valueLength = -1; + builder.Append(""); + break; + + // Need to escape CDATA values + case XmlNodeType.CDATA: + CompleteValue(builder, valueLength); + valueLength = -1; + + builder.Append(""); + break; + + case XmlNodeType.SignificantWhitespace: + case XmlNodeType.Text: + if (valueLength < 0) + valueLength = builder.Length; + + builder.Append(reader.Value); + break; + + default: + // Do nothing + break; + } + reader.Read(); + } + return builder.ToString(); + } + + static void AppendString(StringBuilder builder, string s) + { + builder.Append(s); + builder.Append("^"); + builder.Append(s.Length.ToString(CultureInfo.InvariantCulture)); + } + + static void CompleteValue(StringBuilder builder, int startLength) + { + if (startLength < 0) + return; + + int len = builder.Length - startLength; + builder.Append("^"); + builder.Append(len.ToString(CultureInfo.InvariantCulture)); + } + + internal void Clear(int length) + { + if (resultData.Length == length) + { + Array.Clear(resultData, 0, resultData.Length); + } + else + { + resultData = new byte[length]; + } + } + + internal void ProcessHeaders(Message msg, Dictionary qnameLookup, Dictionary headerLookup) + { + string key; + HeaderBit[] bits; + QName qname; + MessageHeaders headers = msg.Headers; + for (int j = 0; j < headers.Count; ++j) + { + qname.name = headers[j].Name; + qname.ns = headers[j].Namespace; + if (headers.MessageVersion.Addressing == AddressingVersion.WSAddressing10 + && !headers[j].IsReferenceParameter) + { + continue; + } + if (qnameLookup.ContainsKey(qname)) + { + builder.Remove(0, builder.Length); + XmlReader reader = headers.GetReaderAtHeader(j).ReadSubtree(); + reader.Read(); // Needed after call to ReadSubtree + key = GetComparableForm(builder, reader); + + if (headerLookup.TryGetValue(key, out bits)) + { + SetBit(bits); + } + } + } + } + + internal void SetBit(HeaderBit[] bits) + { + if (bits.Length == 1) + { + resultData[bits[0].index] |= bits[0].mask; + } + else + { + byte[] results = resultData; + for (int i = 0; i < bits.Length; ++i) + { + if ((results[bits[i].index] & bits[i].mask) == 0) + { + results[bits[i].index] |= bits[i].mask; + break; + } + } + } + } + + internal bool TestExact(byte[] exact) + { + Fx.Assert(resultData.Length == exact.Length, ""); + + byte[] results = resultData; + for (int i = 0; i < exact.Length; ++i) + { + if (results[i] != exact[i]) + { + return false; + } + } + + return true; + } + + internal bool TestMask(byte[] mask) + { + if (mask == null) + { + return true; + } + + byte[] results = resultData; + for (int i = 0; i < mask.Length; ++i) + { + if ((results[i] & mask[i]) != mask[i]) + { + return false; + } + } + + return true; + } + + internal struct QName + { + internal string name; + internal string ns; + } + + internal class QNameKeyComparer : IComparer, IEqualityComparer + { + internal QNameKeyComparer() + { + } + + public int Compare(QName x, QName y) + { + int i = string.CompareOrdinal(x.name, y.name); + if (i != 0) + return i; + + return string.CompareOrdinal(x.ns, y.ns); + } + + public bool Equals(QName x, QName y) + { + int i = string.CompareOrdinal(x.name, y.name); + if (i != 0) + return false; + + return string.CompareOrdinal(x.ns, y.ns) == 0; + } + + public int GetHashCode(QName obj) + { + return obj.name.GetHashCode() ^ obj.ns.GetHashCode(); + } + } + + internal struct HeaderBit + { + internal int index; + internal byte mask; + + internal HeaderBit(int bitNum) + { + index = bitNum / 8; + mask = (byte)(1 << (bitNum % 8)); + } + + internal void AddToMask(ref byte[] mask) + { + if (mask == null) + { + mask = new byte[index + 1]; + } + else if (mask.Length <= index) + { + Array.Resize(ref mask, index + 1); + } + + mask[index] |= this.mask; + } + } + + class Attr : IComparable + { + internal string local; + internal string ns; + internal string val; + string key; + + internal Attr(string l, string ns, string v) + { + local = l; + this.ns = ns; + val = v; + key = ns + ":" + l; + } + + public int CompareTo(Attr a) + { + return string.Compare(key, a.key, StringComparison.Ordinal); + } + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/EndpointDispatcher.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/EndpointDispatcher.cs new file mode 100644 index 000000000..37b28dd02 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/EndpointDispatcher.cs @@ -0,0 +1,331 @@ +using System; +using System.Collections.Generic; +using CoreWCF.Channels; + +namespace CoreWCF.Dispatcher +{ + public class EndpointDispatcher + { + MessageFilter addressFilter; + bool addressFilterSetExplicit; + ChannelDispatcher channelDispatcher; + MessageFilter contractFilter; + string contractName; + string contractNamespace; + ServiceChannel datagramChannel; + DispatchRuntime dispatchRuntime; + MessageFilter endpointFilter; + int filterPriority; + Uri listenUri; + EndpointAddress originalAddress; + //string perfCounterId; + //string perfCounterBaseId; + string id; // for ServiceMetadataBehavior, to help get EndpointIdentity of ServiceEndpoint from EndpointDispatcher + bool isSystemEndpoint; + + internal EndpointDispatcher(EndpointAddress address, string contractName, string contractNamespace, string id, bool isSystemEndpoint) + : this(address, contractName, contractNamespace) + { + this.id = id; + this.isSystemEndpoint = isSystemEndpoint; + } + + public EndpointDispatcher(EndpointAddress address, string contractName, string contractNamespace) + : this(address, contractName, contractNamespace, false) + { + } + + public EndpointDispatcher(EndpointAddress address, string contractName, string contractNamespace, bool isSystemEndpoint) + { + originalAddress = address; + this.contractName = contractName; + this.contractNamespace = contractNamespace; + + if (address != null) + { + addressFilter = new EndpointAddressMessageFilter(address); + } + else + { + addressFilter = new MatchAllMessageFilter(); + } + + contractFilter = new MatchAllMessageFilter(); + dispatchRuntime = new DispatchRuntime(this); + filterPriority = 0; + this.isSystemEndpoint = isSystemEndpoint; + } + + EndpointDispatcher(EndpointDispatcher baseEndpoint, IEnumerable headers) + { + EndpointAddressBuilder builder = new EndpointAddressBuilder(baseEndpoint.EndpointAddress); + foreach (AddressHeader h in headers) + { + builder.Headers.Add(h); + } + EndpointAddress address = builder.ToEndpointAddress(); + + addressFilter = new EndpointAddressMessageFilter(address); + // channelDispatcher is Attached + contractFilter = baseEndpoint.ContractFilter; + contractName = baseEndpoint.ContractName; + contractNamespace = baseEndpoint.ContractNamespace; + dispatchRuntime = baseEndpoint.DispatchRuntime; + // endpointFilter is lazy + filterPriority = baseEndpoint.FilterPriority + 1; + originalAddress = address; + //if (PerformanceCounters.PerformanceCountersEnabled) + //{ + // this.perfCounterId = baseEndpoint.perfCounterId; + // this.perfCounterBaseId = baseEndpoint.perfCounterBaseId; + //} + id = baseEndpoint.id; + } + + internal MessageFilter AddressFilter + { + get { return addressFilter; } + set + { + if (value == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value"); + } + ThrowIfDisposedOrImmutable(); + addressFilter = value; + addressFilterSetExplicit = true; + } + } + + internal bool AddressFilterSetExplicit + { + get { return addressFilterSetExplicit; } + } + + internal ChannelDispatcher ChannelDispatcher + { + get { return channelDispatcher; } + } + + internal MessageFilter ContractFilter + { + get { return contractFilter; } + set + { + if (value == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value"); + } + ThrowIfDisposedOrImmutable(); + contractFilter = value; + } + } + + public string ContractName + { + get { return contractName; } + } + + public string ContractNamespace + { + get { return contractNamespace; } + } + + internal ServiceChannel DatagramChannel + { + get { return datagramChannel; } + set { datagramChannel = value; } + } + + public DispatchRuntime DispatchRuntime + { + get { return dispatchRuntime; } + } + + internal Uri ListenUri + { + get { return listenUri; } + } + + internal EndpointAddress OriginalAddress + { + get { return originalAddress; } + } + + public EndpointAddress EndpointAddress + { + get + { + if (channelDispatcher == null) + { + return originalAddress; + } + + if ((originalAddress != null) && (originalAddress.Identity != null)) + { + return originalAddress; + } + + if (originalAddress != null) + { + return originalAddress; + } + + EndpointAddressBuilder builder; + if (originalAddress != null) + { + builder = new EndpointAddressBuilder(originalAddress); + return builder.ToEndpointAddress(); + } + else + { + return null; + } + } + } + + public bool IsSystemEndpoint + { + get { return isSystemEndpoint; } + } + + internal MessageFilter EndpointFilter + { + get + { + if (endpointFilter == null) + { + MessageFilter addressFilter = this.addressFilter; + MessageFilter contractFilter = this.contractFilter; + + // Can't optimize addressFilter similarly. + // AndMessageFilter tracks when the address filter matched so the correct + // fault can be sent back. + if (contractFilter is MatchAllMessageFilter) + { + endpointFilter = addressFilter; + } + else + { + endpointFilter = new AndMessageFilter(addressFilter, contractFilter); + } + } + return endpointFilter; + } + } + + public int FilterPriority + { + get { return filterPriority; } + set { filterPriority = value; } + } + + internal string Id + { + get { return id; } + set { id = value; } + } + + //internal string PerfCounterId + //{ + // get { return this.perfCounterId; } + //} + + //internal string PerfCounterBaseId + //{ + // get { return this.perfCounterBaseId; } + //} + + internal int PerfCounterInstanceId { get; set; } + + static internal EndpointDispatcher AddEndpointDispatcher(EndpointDispatcher baseEndpoint, + IEnumerable headers) + { + EndpointDispatcher endpoint = new EndpointDispatcher(baseEndpoint, headers); + baseEndpoint.ChannelDispatcher.Endpoints.Add(endpoint); + return endpoint; + } + + internal void Attach(ChannelDispatcher channelDispatcher) + { + if (channelDispatcher == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("channelDispatcher"); + } + + if (this.channelDispatcher != null) + { + Exception error = new InvalidOperationException(SR.SFxEndpointDispatcherMultipleChannelDispatcher0); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(error); + } + + this.channelDispatcher = channelDispatcher; + // TODO: Plumb through the listening Uri + //listenUri = channelDispatcher.Listener?.Uri; + } + + internal void Detach(ChannelDispatcher channelDispatcher) + { + if (channelDispatcher == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(channelDispatcher)); + } + + if (this.channelDispatcher != channelDispatcher) + { + Exception error = new InvalidOperationException(SR.SFxEndpointDispatcherDifferentChannelDispatcher0); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(error); + } + + //this.ReleasePerformanceCounters(); + this.channelDispatcher = null; + } + + //internal void ReleasePerformanceCounters() + //{ + // if (PerformanceCounters.PerformanceCountersEnabled) + // { + // PerformanceCounters.ReleasePerformanceCountersForEndpoint(this.perfCounterId, this.perfCounterBaseId); + // } + //} + + //internal bool SetPerfCounterId() + //{ + // Uri keyUri = null; + // if (null != this.ListenUri) + // { + // keyUri = this.ListenUri; + // } + // else + // { + // EndpointAddress endpointAddress = this.EndpointAddress; + // if (null != endpointAddress) + // { + // keyUri = endpointAddress.Uri; + // } + // } + + // if (null != keyUri) + // { + // this.perfCounterBaseId = keyUri.AbsoluteUri.ToUpperInvariant(); + // this.perfCounterId = this.perfCounterBaseId + "/" + contractName.ToUpperInvariant(); + + // return true; + // } + // else + // { + // return false; + // } + //} + + void ThrowIfDisposedOrImmutable() + { + ChannelDispatcher channelDispatcher = this.channelDispatcher; + if (channelDispatcher != null) + { + channelDispatcher.ThrowIfDisposedOrImmutable(); + } + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/EndpointDispatcherTable.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/EndpointDispatcherTable.cs new file mode 100644 index 000000000..29dd70844 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/EndpointDispatcherTable.cs @@ -0,0 +1,167 @@ +using System; +using System.Collections.Generic; +using CoreWCF.Channels; +using CoreWCF.Diagnostics; + +namespace CoreWCF.Dispatcher +{ + internal class EndpointDispatcherTable + { + MessageFilterTable filters; + object thisLock; + const int optimizationThreshold = 2; + List cachedEndpoints; + + public EndpointDispatcherTable(object thisLock) + { + this.thisLock = thisLock; + } + + public int Count + { + get + { + return ((cachedEndpoints != null) ? cachedEndpoints.Count : 0) + + ((filters != null) ? filters.Count : 0); + } + } + + object ThisLock + { + get { return thisLock; } + } + + public void AddEndpoint(EndpointDispatcher endpoint) + { + lock (ThisLock) + { + MessageFilter filter = endpoint.EndpointFilter; + int priority = endpoint.FilterPriority; + + if (filters == null) + { + if (cachedEndpoints == null) + { + cachedEndpoints = new List(optimizationThreshold); + } + + if (cachedEndpoints.Count < optimizationThreshold) + { + cachedEndpoints.Add(endpoint); + } + else + { + filters = new MessageFilterTable(); + for (int i = 0; i < cachedEndpoints.Count; i++) + { + int cachedPriority = cachedEndpoints[i].FilterPriority; + MessageFilter cachedFilter = cachedEndpoints[i].EndpointFilter; + filters.Add(cachedFilter, cachedEndpoints[i], cachedPriority); + } + filters.Add(filter, endpoint, priority); + cachedEndpoints = null; + } + } + else + { + filters.Add(filter, endpoint, priority); + } + } + } + + public void RemoveEndpoint(EndpointDispatcher endpoint) + { + lock (ThisLock) + { + if (filters == null) + { + if (cachedEndpoints != null && cachedEndpoints.Contains(endpoint)) + { + cachedEndpoints.Remove(endpoint); + } + } + else + { + MessageFilter filter = endpoint.EndpointFilter; + filters.Remove(filter); + } + } + } + + EndpointDispatcher LookupInCache(Message message, out bool addressMatched) + { + EndpointDispatcher result = null; + int priority = int.MinValue; + bool duplicatePriority = false; + addressMatched = false; + + if (cachedEndpoints != null && cachedEndpoints.Count > 0) + { + for (int i = 0; i < cachedEndpoints.Count; i++) + { + EndpointDispatcher cachedEndpoint = cachedEndpoints[i]; + int cachedPriority = cachedEndpoint.FilterPriority; + MessageFilter cachedFilter = cachedEndpoint.EndpointFilter; + + bool matchResult; + AndMessageFilter andFilter = cachedFilter as AndMessageFilter; + if (andFilter != null) + { + bool addressResult; + matchResult = andFilter.Match(message, out addressResult); + addressMatched |= addressResult; + } + else + { + matchResult = cachedFilter.Match(message); + } + + if (matchResult) + { + addressMatched = true; + if (cachedPriority > priority || result == null) + { + result = cachedEndpoint; + priority = cachedPriority; + duplicatePriority = false; + } + else if (cachedPriority == priority && result != null) + { + duplicatePriority = true; + } + } + } + } + + if (duplicatePriority) + { + throw TraceUtility.ThrowHelperError(new MultipleFilterMatchesException(SR.FilterMultipleMatches), message); + } + + return result; + } + + public EndpointDispatcher Lookup(Message message, out bool addressMatched) + { + EndpointDispatcher data = null; + + data = LookupInCache(message, out addressMatched); + + if (data == null) + { + lock (ThisLock) + { + data = LookupInCache(message, out addressMatched); + + if (data == null && filters != null) + { + filters.GetMatchingValue(message, out data, out addressMatched); + } + } + } + + return data; + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/EndpointFilterProvider.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/EndpointFilterProvider.cs new file mode 100644 index 000000000..79661c929 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/EndpointFilterProvider.cs @@ -0,0 +1,49 @@ +using CoreWCF.Collections.Generic; +using CoreWCF.Channels; + +namespace CoreWCF.Dispatcher +{ + class EndpointFilterProvider + { + SynchronizedCollection initiatingActions; + object mutex; + + public EndpointFilterProvider(params string[] initiatingActions) + { + mutex = new object(); + this.initiatingActions = new SynchronizedCollection(mutex, initiatingActions); + } + + public SynchronizedCollection InitiatingActions + { + get { return initiatingActions; } + } + + public MessageFilter CreateFilter(out int priority) + { + lock (mutex) + { + priority = 1; + if (initiatingActions.Count == 0) + return new MatchNoneMessageFilter(); + + string[] actions = new string[initiatingActions.Count]; + int index = 0; + for (int i = 0; i < initiatingActions.Count; i++) + { + string currentAction = initiatingActions[i]; + if (currentAction == MessageHeaders.WildcardAction) + { + priority = 0; + return new MatchAllMessageFilter(); + } + actions[index] = currentAction; + ++index; + } + + return new ActionMessageFilter(actions); + } + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ErrorBehavior.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ErrorBehavior.cs new file mode 100644 index 000000000..e1d246d70 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ErrorBehavior.cs @@ -0,0 +1,289 @@ +using System; +using CoreWCF.Runtime; +using CoreWCF.Channels; +using CoreWCF.Diagnostics; + +namespace CoreWCF.Dispatcher +{ + class ErrorBehavior + { + IErrorHandler[] handlers; + bool debug; + bool isOnServer; + MessageVersion messageVersion; + + internal ErrorBehavior(ChannelDispatcher channelDispatcher) + { + if (channelDispatcher?.ErrorHandlers == null) + { + handlers = EmptyArray.Allocate(0); + } + else + { + handlers = EmptyArray.ToArray(channelDispatcher.ErrorHandlers); + } + //debug = channelDispatcher.IncludeExceptionDetailInFaults; + debug = true; + //isOnServer = channelDispatcher.IsOnServer; + isOnServer = true; + //messageVersion = channelDispatcher.MessageVersion; + messageVersion = MessageVersion.Soap11; + } + + void InitializeFault(ref MessageRpc rpc) + { + Exception error = rpc.Error; + FaultException fault = error as FaultException; + if (fault != null) + { + string action; + MessageFault messageFault = rpc.Operation.FaultFormatter.Serialize(fault, out action); + if (action == null) + action = rpc.RequestVersion.Addressing.DefaultFaultAction; + if (messageFault != null) + rpc.FaultInfo.Fault = Message.CreateMessage(rpc.RequestVersion, messageFault, action); + } + } + + internal IErrorHandler[] Handlers + { + get + { + return handlers; + } + } + + internal void ProvideMessageFault(ref MessageRpc rpc) + { + if (rpc.Error != null) + { + ProvideMessageFaultCore(ref rpc); + } + } + + void ProvideMessageFaultCore(ref MessageRpc rpc) + { + if (messageVersion != rpc.RequestVersion) + { + Fx.Assert("CoreWCF.Dispatcher.ErrorBehavior.ProvideMessageFaultCore(): (this.messageVersion != rpc.RequestVersion)"); + } + + InitializeFault(ref rpc); + + ProvideFault(rpc.Error, rpc.Channel.GetProperty(), ref rpc.FaultInfo); + + ProvideMessageFaultCoreCoda(ref rpc); + } + + void ProvideFaultOfLastResort(Exception error, ref ErrorHandlerFaultInfo faultInfo) + { + if (faultInfo.Fault == null) + { + FaultCode code = new FaultCode(FaultCodeConstants.Codes.InternalServiceFault, FaultCodeConstants.Namespaces.NetDispatch); + code = FaultCode.CreateReceiverFaultCode(code); + string action = FaultCodeConstants.Actions.NetDispatcher; + MessageFault fault; + if (debug) + { + faultInfo.DefaultFaultAction = action; + fault = MessageFault.CreateFault(code, new FaultReason(error.Message), new ExceptionDetail(error)); + } + else + { + string reason = isOnServer ? SR.SFxInternalServerError : SR.SFxInternalCallbackError; + fault = MessageFault.CreateFault(code, new FaultReason(reason)); + } + faultInfo.IsConsideredUnhandled = true; + faultInfo.Fault = Message.CreateMessage(messageVersion, fault, action); + } + //if this is an InternalServiceFault coming from another service dispatcher we should treat it as unhandled so that the channels are cleaned up + else if (error != null) + { + FaultException e = error as FaultException; + if (e != null && e.Fault != null && e.Fault.Code != null && e.Fault.Code.SubCode != null && + string.Compare(e.Fault.Code.SubCode.Namespace, FaultCodeConstants.Namespaces.NetDispatch, StringComparison.Ordinal) == 0 && + string.Compare(e.Fault.Code.SubCode.Name, FaultCodeConstants.Codes.InternalServiceFault, StringComparison.Ordinal) == 0) + { + faultInfo.IsConsideredUnhandled = true; + } + } + } + + void ProvideMessageFaultCoreCoda(ref MessageRpc rpc) + { + if (rpc.FaultInfo.Fault.Headers.Action == null) + { + rpc.FaultInfo.Fault.Headers.Action = rpc.RequestVersion.Addressing.DefaultFaultAction; + } + + rpc.Reply = rpc.FaultInfo.Fault; + } + + internal void ProvideOnlyFaultOfLastResort(ref MessageRpc rpc) + { + ProvideFaultOfLastResort(rpc.Error, ref rpc.FaultInfo); + ProvideMessageFaultCoreCoda(ref rpc); + } + + internal void ProvideFault(Exception e, FaultConverter faultConverter, ref ErrorHandlerFaultInfo faultInfo) + { + ProvideWellKnownFault(e, faultConverter, ref faultInfo); + for (int i = 0; i < handlers.Length; i++) + { + Message m = faultInfo.Fault; + handlers[i].ProvideFault(e, messageVersion, ref m); + faultInfo.Fault = m; + //if (TD.FaultProviderInvokedIsEnabled()) + //{ + // TD.FaultProviderInvoked(handlers[i].GetType().FullName, e.Message); + //} + } + ProvideFaultOfLastResort(e, ref faultInfo); + } + + void ProvideWellKnownFault(Exception e, FaultConverter faultConverter, ref ErrorHandlerFaultInfo faultInfo) + { + Message faultMessage; + if (faultConverter != null && faultConverter.TryCreateFaultMessage(e, out faultMessage)) + { + faultInfo.Fault = faultMessage; + return; + } + else if (e is NetDispatcherFaultException) + { + NetDispatcherFaultException ndfe = e as NetDispatcherFaultException; + if (debug) + { + ExceptionDetail detail = new ExceptionDetail(ndfe); + faultInfo.Fault = Message.CreateMessage(messageVersion, MessageFault.CreateFault(ndfe.Code, ndfe.Reason, detail), ndfe.Action); + } + else + { + faultInfo.Fault = Message.CreateMessage(messageVersion, ndfe.CreateMessageFault(), ndfe.Action); + } + } + } + + internal void HandleError(ref MessageRpc rpc) + { + if (rpc.Error != null) + { + HandleErrorCore(ref rpc); + } + } + + void HandleErrorCore(ref MessageRpc rpc) + { + bool handled = HandleErrorCommon(rpc.Error, ref rpc.FaultInfo); + if (handled) + { + rpc.Error = null; + } + } + + bool HandleErrorCommon(Exception error, ref ErrorHandlerFaultInfo faultInfo) + { + bool handled; + if (faultInfo.Fault != null // there is a message + && !faultInfo.IsConsideredUnhandled) // and it's not the internal-server-error one + { + handled = true; + } + else + { + handled = false; + } + + try + { + //if (TD.ServiceExceptionIsEnabled()) + //{ + // TD.ServiceException(null, error.ToString(), error.GetType().FullName); + //} + for (int i = 0; i < handlers.Length; i++) + { + bool handledByThis = handlers[i].HandleError(error); + handled = handledByThis || handled; + //if (TD.ErrorHandlerInvokedIsEnabled()) + //{ + // TD.ErrorHandlerInvoked(handlers[i].GetType().FullName, handledByThis, error.GetType().FullName); + //} + } + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(e); + } + return handled; + } + + internal bool HandleError(Exception error) + { + ErrorHandlerFaultInfo faultInfo = new ErrorHandlerFaultInfo(messageVersion.Addressing.DefaultFaultAction); + return HandleError(error, ref faultInfo); + } + + internal bool HandleError(Exception error, ref ErrorHandlerFaultInfo faultInfo) + { + return HandleErrorCommon(error, ref faultInfo); + } + + internal static bool ShouldRethrowExceptionAsIs(Exception e) + { + return true; + } + + internal static bool ShouldRethrowClientSideExceptionAsIs(Exception e) + { + return true; + } + + // This ensures that people debugging first-chance Exceptions see this Exception, + // and that the Exception shows up in the trace logs. + internal static void ThrowAndCatch(Exception e, Message message) + { + try + { + if (System.Diagnostics.Debugger.IsAttached) + { + if (message == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(e); + } + else + { + throw TraceUtility.ThrowHelperError(e, message); + } + } + else + { + if (message == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(e); + } + else + { + TraceUtility.ThrowHelperError(e, message); + } + } + } + catch (Exception e2) + { + if (!object.ReferenceEquals(e, e2)) + { + throw; + } + } + } + + internal static void ThrowAndCatch(Exception e) + { + ThrowAndCatch(e, null); + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ErrorHandlerFaultInfo.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ErrorHandlerFaultInfo.cs new file mode 100644 index 000000000..381f8c5cd --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ErrorHandlerFaultInfo.cs @@ -0,0 +1,36 @@ +using CoreWCF.Channels; + +namespace CoreWCF.Dispatcher +{ + internal struct ErrorHandlerFaultInfo + { + Message fault; // if this is null, then we aren't interested in sending back a fault + bool isConsideredUnhandled; // if this is true, it means Fault is the 'internal server error' fault + string defaultFaultAction; + + public ErrorHandlerFaultInfo(string defaultFaultAction) + { + this.defaultFaultAction = defaultFaultAction; + fault = null; + isConsideredUnhandled = false; + } + + public Message Fault + { + get { return fault; } + set { fault = value; } + } + + public string DefaultFaultAction + { + get { return defaultFaultAction; } + set { defaultFaultAction = value; } + } + + public bool IsConsideredUnhandled + { + get { return isConsideredUnhandled; } + set { isConsideredUnhandled = value; } + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ErrorHandlingAcceptor.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ErrorHandlingAcceptor.cs new file mode 100644 index 000000000..6c686fdea --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ErrorHandlingAcceptor.cs @@ -0,0 +1,126 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using CoreWCF.Runtime; + +namespace CoreWCF.Dispatcher +{ + internal class ErrorHandlingAcceptor + { + readonly ChannelDispatcher dispatcher; + readonly IListenerBinder binder; + + internal ErrorHandlingAcceptor(IListenerBinder binder, ChannelDispatcher dispatcher) + { + if (binder == null) + { + Fx.Assert("binder is null"); + } + if (dispatcher == null) + { + Fx.Assert("dispatcher is null"); + } + + this.binder = binder; + this.dispatcher = dispatcher; + } + + internal async Task CloseAsync() + { + try + { + await binder.Listener.CloseAsync(); + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + HandleError(e); + } + } + + void HandleError(Exception e) + { + if (dispatcher != null) + { + dispatcher.HandleError(e); + } + } + + void HandleErrorOrAbort(Exception e) + { + if ((dispatcher == null) || !dispatcher.HandleError(e)) + { + // We only stop if the listener faults. It is a bug + // if the listener is in an invalid state and does not + // fault. So there are no cases today where this aborts. + } + } + + internal async Task> TryAcceptAsync(CancellationToken token) + { + IChannelBinder channelBinder; + try + { + channelBinder = await binder.AcceptAsync(token); + if (channelBinder != null) + { + //dispatcher.PendingChannels.Add(channelBinder.Channel); + } + return TryAsyncResult.FromResult(channelBinder); + } + catch (CommunicationObjectAbortedException) + { + channelBinder = null; + return TryAsyncResult.FromResult(channelBinder); + } + catch (CommunicationObjectFaultedException) + { + channelBinder = null; + return TryAsyncResult.FromResult(channelBinder); + } + catch (OperationCanceledException) + { + return TryAsyncResult.FailedResult; + } + catch (CommunicationException e) + { + HandleError(e); + return TryAsyncResult.FailedResult; + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + HandleErrorOrAbort(e); + return TryAsyncResult.FailedResult; + } + } + + //internal async Task WaitForChannelAsync() + //{ + // try + // { + // await this.binder.Listener.WaitForChannelAsync(CancellationToken.None); + // } + // catch (CommunicationObjectAbortedException) { } + // catch (CommunicationObjectFaultedException) { } + // catch (CommunicationException e) + // { + // this.HandleError(e); + // } + // catch (Exception e) + // { + // if (Fx.IsFatal(e)) + // { + // throw; + // } + // this.HandleErrorOrAbort(e); + // } + //} + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ErrorHandlingReceiver.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ErrorHandlingReceiver.cs new file mode 100644 index 000000000..b84426472 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ErrorHandlingReceiver.cs @@ -0,0 +1,112 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using CoreWCF.Runtime; +using CoreWCF.Channels; + +namespace CoreWCF.Dispatcher +{ + internal class ErrorHandlingReceiver + { + ChannelDispatcher dispatcher; + IChannelBinder binder; + + internal ErrorHandlingReceiver(IChannelBinder binder, ChannelDispatcher dispatcher) + { + this.binder = binder; + this.dispatcher = dispatcher; + } + + internal async Task CloseAsync() + { + try + { + await binder.Channel.CloseAsync(); + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + HandleError(e); + } + } + + void HandleError(Exception e) + { + if (dispatcher != null) + { + dispatcher.HandleError(e); + } + } + + void HandleErrorOrAbort(Exception e) + { + if ((dispatcher == null) || !dispatcher.HandleError(e)) + { + if (binder.HasSession) + { + binder.Abort(); + } + } + } + + internal async Task> TryReceiveAsync(CancellationToken token) + { + try + { + return await binder.TryReceiveAsync(token); + } + catch (CommunicationObjectAbortedException) + { + return TryAsyncResult.FromResult((RequestContext)null); + } + catch (CommunicationObjectFaultedException) + { + return TryAsyncResult.FromResult((RequestContext)null); + } + catch (CommunicationException e) + { + HandleError(e); + return TryAsyncResult.FailedResult; + } + catch (TimeoutException e) + { + HandleError(e); + return TryAsyncResult.FailedResult; + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + HandleErrorOrAbort(e); + return TryAsyncResult.FailedResult; + } + } + + internal async Task WaitForMessageAsync() + { + try + { + await binder.WaitForMessageAsync(CancellationToken.None); + } + catch (CommunicationObjectAbortedException) { } + catch (CommunicationObjectFaultedException) { } + catch (CommunicationException e) + { + HandleError(e); + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + HandleErrorOrAbort(e); + } + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ExceptionHandler.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ExceptionHandler.cs new file mode 100644 index 000000000..6d06df9d4 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ExceptionHandler.cs @@ -0,0 +1,85 @@ +using System; +using System.Diagnostics; +using CoreWCF.Runtime; + +namespace CoreWCF.Dispatcher +{ + public abstract class ExceptionHandler + { + static readonly ExceptionHandler alwaysHandle = new AlwaysHandleExceptionHandler(); + + static ExceptionHandler transportExceptionHandler = alwaysHandle; + + public static ExceptionHandler AlwaysHandle + { + get + { + return alwaysHandle; + } + } + + public static ExceptionHandler AsynchronousThreadExceptionHandler + { + get + { + HandlerWrapper wrapper = (HandlerWrapper)Fx.AsynchronousThreadExceptionHandler; + return wrapper == null ? null : wrapper.Handler; + } + + set + { + Fx.AsynchronousThreadExceptionHandler = value == null ? null : new HandlerWrapper(value); + } + } + + public static ExceptionHandler TransportExceptionHandler + { + get + { + return transportExceptionHandler; + } + + set + { + transportExceptionHandler = value; + } + } + + // Returns true if the exception has been handled. If it returns false or + // throws a different exception, the original exception will be rethrown. + public abstract bool HandleException(Exception exception); + + + class AlwaysHandleExceptionHandler : ExceptionHandler + { + public override bool HandleException(Exception exception) + { + return true; + } + } + + class HandlerWrapper : Fx.ExceptionHandler + { + readonly ExceptionHandler handler; + + public HandlerWrapper(ExceptionHandler handler) + { + Fx.Assert(handler != null, "Cannot wrap a null handler."); + this.handler = handler; + } + + public ExceptionHandler Handler + { + get + { + return handler; + } + } + + public override bool HandleException(Exception exception) + { + return handler.HandleException(exception); + } + } + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/FaultContractInfo.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/FaultContractInfo.cs new file mode 100644 index 000000000..fa5533bca --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/FaultContractInfo.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; +using CoreWCF.Description; + +namespace CoreWCF.Dispatcher +{ + internal class FaultContractInfo + { + readonly string _action; + readonly Type _detail; + readonly string _elementName; + readonly string _ns; + readonly IList _knownTypes; + DataContractSerializer _serializer; + + public FaultContractInfo(string action, Type detail) + : this(action, detail, null, null, null) + { + } + internal FaultContractInfo(string action, Type detail, XmlName elementName, string ns, IList knownTypes) + { + if (action == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("action"); + } + if (detail == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("detail"); + } + + _action = action; + _detail = detail; + if (elementName != null) + _elementName = elementName.EncodedName; + _ns = ns; + _knownTypes = knownTypes; + } + + public string Action => _action; + + public Type Detail => _detail; + + internal string ElementName => _elementName; + + internal string ElementNamespace => _ns; + + internal IList KnownTypes => _knownTypes; + + internal DataContractSerializer Serializer + { + get + { + if (_serializer == null) + { + if (_elementName == null) + { + _serializer = DataContractSerializerDefaults.CreateSerializer(_detail, _knownTypes, int.MaxValue /* maxItemsInObjectGraph */); + } + else + { + _serializer = DataContractSerializerDefaults.CreateSerializer(_detail, _knownTypes, _elementName, _ns ?? string.Empty, int.MaxValue /* maxItemsInObjectGraph */); + } + } + return _serializer; + } + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/FaultFormatter.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/FaultFormatter.cs new file mode 100644 index 000000000..41bca6874 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/FaultFormatter.cs @@ -0,0 +1,207 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Runtime.Serialization; +using System.Xml; +using CoreWCF.Collections.Generic; +using CoreWCF.Runtime; +using CoreWCF.Channels; + +namespace CoreWCF.Dispatcher +{ + internal class FaultFormatter : IClientFaultFormatter, IDispatchFaultFormatter + { + FaultContractInfo[] faultContractInfos; + + internal FaultFormatter(Type[] detailTypes) + { + List faultContractInfoList = new List(); + for (int i = 0; i < detailTypes.Length; i++) + faultContractInfoList.Add(new FaultContractInfo(MessageHeaders.WildcardAction, detailTypes[i])); + AddInfrastructureFaults(faultContractInfoList); + faultContractInfos = GetSortedArray(faultContractInfoList); + } + + internal FaultFormatter(SynchronizedCollection faultContractInfoCollection) + { + List faultContractInfoList; + lock (faultContractInfoCollection.SyncRoot) + { + faultContractInfoList = new List(faultContractInfoCollection); + } + AddInfrastructureFaults(faultContractInfoList); + faultContractInfos = GetSortedArray(faultContractInfoList); + } + + public MessageFault Serialize(FaultException faultException, out string action) + { + XmlObjectSerializer serializer = null; + Type detailType = null; + string faultExceptionAction = action = faultException.Action; + + Type faultExceptionOfT = null; + for (Type faultType = faultException.GetType(); faultType != typeof(FaultException); faultType = faultType.GetTypeInfo().BaseType) + { + if (faultType.GetTypeInfo().IsGenericType && (faultType.GetGenericTypeDefinition() == typeof(FaultException<>))) + { + faultExceptionOfT = faultType; + break; + } + } + if (faultExceptionOfT != null) + { + detailType = faultExceptionOfT.GetGenericArguments()[0]; + serializer = GetSerializer(detailType, faultExceptionAction, out action); + } + return CreateMessageFault(serializer, faultException, detailType); + } + + public FaultException Deserialize(MessageFault messageFault, string action) + { + if (!messageFault.HasDetail) + return new FaultException(messageFault, action); + + return CreateFaultException(messageFault, action); + } + + protected virtual XmlObjectSerializer GetSerializer(Type detailType, string faultExceptionAction, out string action) + { + action = faultExceptionAction; + FaultContractInfo faultInfo = null; + for (int i = 0; i < faultContractInfos.Length; i++) + { + if (faultContractInfos[i].Detail == detailType) + { + faultInfo = faultContractInfos[i]; + break; + } + } + if (faultInfo != null) + { + if (action == null) + action = faultInfo.Action; + + return faultInfo.Serializer; + } + else + return DataContractSerializerDefaults.CreateSerializer(detailType, int.MaxValue /* maxItemsInObjectGraph */ ); + } + + protected virtual FaultException CreateFaultException(MessageFault messageFault, string action) + { + IList faultInfos; + if (action != null) + { + faultInfos = new List(); + for (int i = 0; i < faultContractInfos.Length; i++) + { + if (faultContractInfos[i].Action == action || faultContractInfos[i].Action == MessageHeaders.WildcardAction) + { + faultInfos.Add(faultContractInfos[i]); + } + } + } + else + { + faultInfos = faultContractInfos; + } + + Type detailType = null; + object detailObj = null; + for (int i = 0; i < faultInfos.Count; i++) + { + FaultContractInfo faultInfo = faultInfos[i]; + XmlDictionaryReader detailReader = messageFault.GetReaderAtDetailContents(); + XmlObjectSerializer serializer = faultInfo.Serializer; + if (serializer.IsStartObject(detailReader)) + { + detailType = faultInfo.Detail; + try + { + detailObj = serializer.ReadObject(detailReader); + FaultException faultException = CreateFaultException(messageFault, action, + detailObj, detailType, detailReader); + if (faultException != null) + return faultException; + } + catch (SerializationException) + { + } + } + } + return new FaultException(messageFault, action); + } + + protected FaultException CreateFaultException(MessageFault messageFault, string action, + object detailObj, Type detailType, XmlDictionaryReader detailReader) + { + if (!detailReader.EOF) + { + detailReader.MoveToContent(); + if (detailReader.NodeType != XmlNodeType.EndElement && !detailReader.EOF) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.ExtraContentIsPresentInFaultDetail)); + } + bool isDetailObjectValid = true; + if (detailObj == null) + { + isDetailObjectValid = !detailType.GetTypeInfo().IsValueType; + } + else + { + isDetailObjectValid = detailType.IsAssignableFrom(detailObj.GetType()); + } + if (isDetailObjectValid) + { + Type knownFaultType = typeof(FaultException<>).MakeGenericType(detailType); + return (FaultException)Activator.CreateInstance(knownFaultType, + detailObj, + messageFault.Reason, + messageFault.Code, + action); + } + return null; + } + + static FaultContractInfo[] GetSortedArray(List faultContractInfoList) + { + FaultContractInfo[] temp = faultContractInfoList.ToArray(); + Array.Sort(temp, + delegate (FaultContractInfo x, FaultContractInfo y) + { return string.CompareOrdinal(x.Action, y.Action); } + ); + return temp; + } + + static void AddInfrastructureFaults(List faultContractInfos) + { + faultContractInfos.Add(new FaultContractInfo(FaultCodeConstants.Actions.NetDispatcher, typeof(ExceptionDetail))); + } + + static MessageFault CreateMessageFault(XmlObjectSerializer serializer, FaultException faultException, Type detailType) + { + if (detailType == null) + { + if (faultException.Fault != null) + return faultException.Fault; + return MessageFault.CreateFault(faultException.Code, faultException.Reason); + } + Fx.Assert(serializer != null, ""); + + Type operationFaultType = typeof(OperationFault<>).MakeGenericType(detailType); + return (MessageFault)Activator.CreateInstance(operationFaultType, serializer, faultException); + } + + internal class OperationFault : XmlObjectSerializerFault + { + public OperationFault(XmlObjectSerializer serializer, FaultException faultException) : + base(faultException.Code, faultException.Reason, + faultException.Detail, + serializer, + string.Empty/*actor*/, + string.Empty/*node*/) + { + } + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ICallContextInitializer.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ICallContextInitializer.cs new file mode 100644 index 000000000..14fae7c9f --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ICallContextInitializer.cs @@ -0,0 +1,10 @@ +using CoreWCF.Channels; + +namespace CoreWCF.Dispatcher +{ + internal interface ICallContextInitializer + { + object BeforeInvoke(InstanceContext instanceContext, IClientChannel channel, Message message); + void AfterInvoke(object correlationState); + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IChannelBinder.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IChannelBinder.cs new file mode 100644 index 000000000..ffdf5c9a2 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IChannelBinder.cs @@ -0,0 +1,23 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using CoreWCF.Channels; + +namespace CoreWCF.Dispatcher +{ + internal interface IChannelBinder + { + IChannel Channel { get; } + bool HasSession { get; } + Uri ListenUri { get; } + EndpointAddress LocalAddress { get; } + EndpointAddress RemoteAddress { get; } + void Abort(); + void CloseAfterFault(TimeSpan timeout); + Task> TryReceiveAsync(CancellationToken token); + Task SendAsync(Message message, CancellationToken token); + Task RequestAsync(Message message, CancellationToken token); + Task WaitForMessageAsync(CancellationToken token); + RequestContext CreateRequestContext(Message message); + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IChannelInitializer.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IChannelInitializer.cs new file mode 100644 index 000000000..9fef991cd --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IChannelInitializer.cs @@ -0,0 +1,7 @@ +namespace CoreWCF.Dispatcher +{ + public interface IChannelInitializer + { + void Initialize(IClientChannel channel); + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IClientFaultFormatter.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IClientFaultFormatter.cs new file mode 100644 index 000000000..775096d8e --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IClientFaultFormatter.cs @@ -0,0 +1,9 @@ +using CoreWCF.Channels; + +namespace CoreWCF.Dispatcher +{ + internal interface IClientFaultFormatter + { + FaultException Deserialize(MessageFault messageFault, string action); + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IClientMessageFormatter.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IClientMessageFormatter.cs new file mode 100644 index 000000000..6eb1b725d --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IClientMessageFormatter.cs @@ -0,0 +1,10 @@ +using CoreWCF.Channels; + +namespace CoreWCF.Dispatcher +{ + public interface IClientMessageFormatter + { + Message SerializeRequest(MessageVersion messageVersion, object[] parameters); + object DeserializeReply(Message message, object[] parameters); + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IClientMessageInspector.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IClientMessageInspector.cs new file mode 100644 index 000000000..ca501780c --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IClientMessageInspector.cs @@ -0,0 +1,10 @@ +using CoreWCF.Channels; + +namespace CoreWCF.Dispatcher +{ + public interface IClientMessageInspector + { + void AfterReceiveReply(ref Message reply, object correlationState); + object BeforeSendRequest(ref Message request, IClientChannel channel); + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IClientOperationSelector.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IClientOperationSelector.cs new file mode 100644 index 000000000..b7e55bb87 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IClientOperationSelector.cs @@ -0,0 +1,10 @@ +using System.Reflection; + +namespace CoreWCF.Dispatcher +{ + public interface IClientOperationSelector + { + bool AreParametersRequiredForSelection { get; } + string SelectOperation(MethodBase method, object[] parameters); + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IDispatchFaultFormatter.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IDispatchFaultFormatter.cs new file mode 100644 index 000000000..b801dc6cd --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IDispatchFaultFormatter.cs @@ -0,0 +1,19 @@ +using CoreWCF.Channels; + +namespace CoreWCF.Dispatcher +{ + internal interface IDispatchFaultFormatter + { + MessageFault Serialize(FaultException faultException, out string action); + } + + // Only used on full framework by WebHttpBehavior + internal interface IDispatchFaultFormatterWrapper + { + IDispatchFaultFormatter InnerFaultFormatter + { + get; + set; + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IDispatchMessageFormatter.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IDispatchMessageFormatter.cs new file mode 100644 index 000000000..acd193031 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IDispatchMessageFormatter.cs @@ -0,0 +1,10 @@ +using CoreWCF.Channels; + +namespace CoreWCF.Dispatcher +{ + internal interface IDispatchMessageFormatter + { + void DeserializeRequest(Message message, object[] parameters); + Message SerializeReply(MessageVersion messageVersion, object[] parameters, object result); + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IDispatchMessageInspector.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IDispatchMessageInspector.cs new file mode 100644 index 000000000..4fbe27ba7 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IDispatchMessageInspector.cs @@ -0,0 +1,10 @@ +using CoreWCF.Channels; + +namespace CoreWCF.Dispatcher +{ + public interface IDispatchMessageInspector + { + object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext); + void BeforeSendReply(ref Message reply, object correlationState); + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IDispatchOperationSelector.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IDispatchOperationSelector.cs new file mode 100644 index 000000000..15892032e --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IDispatchOperationSelector.cs @@ -0,0 +1,9 @@ +using CoreWCF.Channels; + +namespace CoreWCF.Dispatcher +{ + internal interface IDispatchOperationSelector + { + string SelectOperation(ref Message message); + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IErrorHandler.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IErrorHandler.cs new file mode 100644 index 000000000..e512a6454 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IErrorHandler.cs @@ -0,0 +1,11 @@ +using System; +using CoreWCF.Channels; + +namespace CoreWCF.Dispatcher +{ + public interface IErrorHandler + { + void ProvideFault(Exception error, MessageVersion version, ref Message fault); + bool HandleError(Exception error); + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IInputSessionShutdown.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IInputSessionShutdown.cs new file mode 100644 index 000000000..7f3912524 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IInputSessionShutdown.cs @@ -0,0 +1,8 @@ +namespace CoreWCF.Dispatcher +{ + internal interface IInputSessionShutdown + { + void ChannelFaulted(IDuplexContextChannel channel); + void DoneReceiving(IDuplexContextChannel channel); + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IInstanceContextInitializer.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IInstanceContextInitializer.cs new file mode 100644 index 000000000..89904e219 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IInstanceContextInitializer.cs @@ -0,0 +1,10 @@ +using CoreWCF.Channels; + +namespace CoreWCF.Dispatcher +{ + internal interface IInstanceContextInitializer + { + // message=null for singleton + void Initialize(InstanceContext instanceContext, Message message); + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IInstanceContextProvider.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IInstanceContextProvider.cs new file mode 100644 index 000000000..1f5e2783d --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IInstanceContextProvider.cs @@ -0,0 +1,98 @@ +using System; +using CoreWCF.Channels; + +namespace CoreWCF.Dispatcher +{ + internal interface IInstanceContextProvider + { + InstanceContext GetExistingInstanceContext(Message message, IContextChannel channel); + void InitializeInstanceContext(InstanceContext instanceContext, Message message, IContextChannel channel); + bool IsIdle(InstanceContext instanceContext); + void NotifyIdle(Action callback, InstanceContext instanceContext); + } + + + internal abstract class InstanceContextProviderBase : IInstanceContextProvider + { + DispatchRuntime dispatchRuntime; + + public DispatchRuntime DispatchRuntime + { + get + { + return dispatchRuntime; + } + } + + internal InstanceContextProviderBase(DispatchRuntime dispatchRuntime) + { + this.dispatchRuntime = dispatchRuntime; + } + + internal static bool IsProviderSingleton(IInstanceContextProvider provider) + { + return (provider is SingletonInstanceContextProvider); + } + + internal static bool IsProviderSessionful(IInstanceContextProvider provider) + { + return (provider is PerSessionInstanceContextProvider); + } + + internal static IInstanceContextProvider GetProviderForMode(InstanceContextMode instanceMode, DispatchRuntime runtime) + { + switch (instanceMode) + { + case InstanceContextMode.PerCall: + return new PerCallInstanceContextProvider(runtime); + case InstanceContextMode.PerSession: + return new PerSessionInstanceContextProvider(runtime); + case InstanceContextMode.Single: + return new SingletonInstanceContextProvider(runtime); + default: + DiagnosticUtility.FailFast("InstanceContextProviderBase.GetProviderForMode: default"); + return null; + } + } + + internal static bool IsProviderPerCall(IInstanceContextProvider provider) + { + return (provider is PerCallInstanceContextProvider); + } + + internal ServiceChannel GetServiceChannelFromProxy(IContextChannel channel) + { + ServiceChannel serviceChannel = channel as ServiceChannel; + if (serviceChannel == null) + { + serviceChannel = ServiceChannelFactory.GetServiceChannel(channel); + } + return serviceChannel; + } + + #region IInstanceContextProvider Members + + public virtual InstanceContext GetExistingInstanceContext(Message message, IContextChannel channel) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException()); + } + + public virtual void InitializeInstanceContext(InstanceContext instanceContext, Message message, IContextChannel channel) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException()); + } + + public virtual bool IsIdle(InstanceContext instanceContext) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException()); + } + + public virtual void NotifyIdle(Action callback, InstanceContext instanceContext) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException()); + } + + #endregion + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IInstanceProvider.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IInstanceProvider.cs new file mode 100644 index 000000000..56ed799dc --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IInstanceProvider.cs @@ -0,0 +1,11 @@ +using CoreWCF.Channels; + +namespace CoreWCF.Dispatcher +{ + internal interface IInstanceProvider + { + object GetInstance(InstanceContext instanceContext); + object GetInstance(InstanceContext instanceContext, Message message); + void ReleaseInstance(InstanceContext instanceContext, object instance); + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IInvokeReceivedNotification.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IInvokeReceivedNotification.cs new file mode 100644 index 000000000..fab45e1cb --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IInvokeReceivedNotification.cs @@ -0,0 +1,10 @@ +using CoreWCF.Channels; + +namespace CoreWCF.Dispatcher +{ + interface IInvokeReceivedNotification + { + void NotifyInvokeReceived(); + void NotifyInvokeReceived(RequestContext request); + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IListenerBinder.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IListenerBinder.cs new file mode 100644 index 000000000..273346d66 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IListenerBinder.cs @@ -0,0 +1,14 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using CoreWCF.Channels; + +namespace CoreWCF.Dispatcher +{ + internal interface IListenerBinder + { + IChannelListener Listener { get; } + MessageVersion MessageVersion { get; } + Task AcceptAsync(CancellationToken token); + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IMessageFilterTable.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IMessageFilterTable.cs new file mode 100644 index 000000000..56fb89aaa --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IMessageFilterTable.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using CoreWCF.Channels; + +namespace CoreWCF.Dispatcher +{ + internal interface IMessageFilterTable : IDictionary + { + // return a single match + bool GetMatchingValue(Message message, out TFilterData value); + bool GetMatchingValue(MessageBuffer messageBuffer, out TFilterData value); + + // return multiple matches + bool GetMatchingValues(Message message, ICollection results); + bool GetMatchingValues(MessageBuffer messageBuffer, ICollection results); + + // If you need both the filter and the data, use these functions then look up + // the data using the filter tables IDictionary methods. + bool GetMatchingFilter(Message message, out MessageFilter filter); + bool GetMatchingFilter(MessageBuffer messageBuffer, out MessageFilter filter); + bool GetMatchingFilters(Message message, ICollection results); + bool GetMatchingFilters(MessageBuffer messageBuffer, ICollection results); + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IOperationInvoker.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IOperationInvoker.cs new file mode 100644 index 000000000..15559fa11 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IOperationInvoker.cs @@ -0,0 +1,18 @@ +using System; + +namespace CoreWCF.Dispatcher +{ + public interface IOperationInvoker + { + bool IsSynchronous { get; } + + object[] AllocateInputs(); + + object Invoke(object instance, object[] inputs, out object[] outputs); + + // TODO: Switch to Task based invoker + IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state); + + object InvokeEnd(object instance, out object[] outputs, IAsyncResult result); + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IParameterInspector.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IParameterInspector.cs new file mode 100644 index 000000000..02827df8a --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IParameterInspector.cs @@ -0,0 +1,8 @@ +namespace CoreWCF.Dispatcher +{ + public interface IParameterInspector + { + object BeforeCall(string operationName, object[] inputs); + void AfterCall(string operationName, object[] outputs, object returnValue, object correlationState); + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IResumeMessageRpc.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IResumeMessageRpc.cs new file mode 100644 index 000000000..4f8f0442f --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/IResumeMessageRpc.cs @@ -0,0 +1,16 @@ +using System; + +namespace CoreWCF.Dispatcher +{ + internal interface IResumeMessageRpc + { + InstanceContext GetMessageInstanceContext(); + + void Resume(); + void Resume(out bool alreadyResumedNoLock); + // TODO: Convert to task based continuations + void Resume(IAsyncResult result); + void Resume(object instance); + void SignalConditionalResume(IAsyncResult result); + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ImmutableClientRuntime.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ImmutableClientRuntime.cs new file mode 100644 index 000000000..50f69cdc9 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ImmutableClientRuntime.cs @@ -0,0 +1,232 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using CoreWCF.Runtime; +using CoreWCF.Channels; + +namespace CoreWCF.Dispatcher +{ + internal class ImmutableClientRuntime + { + int correlationCount; + //bool addTransactionFlowProperties; + //IInteractiveChannelInitializer[] interactiveChannelInitializers; + IClientOperationSelector operationSelector; + IChannelInitializer[] channelInitializers; + IClientMessageInspector[] messageInspectors; + Dictionary operations; + ProxyOperationRuntime unhandled; + bool useSynchronizationContext; + bool validateMustUnderstand; + + internal ImmutableClientRuntime(ClientRuntime behavior) + { + channelInitializers = EmptyArray.ToArray(behavior.ChannelInitializers); + //this.interactiveChannelInitializers = EmptyArray.ToArray(behavior.InteractiveChannelInitializers); + messageInspectors = EmptyArray.ToArray(behavior.MessageInspectors); + + operationSelector = behavior.OperationSelector; + useSynchronizationContext = behavior.UseSynchronizationContext; + validateMustUnderstand = behavior.ValidateMustUnderstand; + + unhandled = new ProxyOperationRuntime(behavior.UnhandledClientOperation, this); + + //this.addTransactionFlowProperties = behavior.AddTransactionFlowProperties; + + operations = new Dictionary(); + + for (int i = 0; i < behavior.Operations.Count; i++) + { + ClientOperation operation = behavior.Operations[i]; + ProxyOperationRuntime operationRuntime = new ProxyOperationRuntime(operation, this); + operations.Add(operation.Name, operationRuntime); + } + + correlationCount = messageInspectors.Length + behavior.MaxParameterInspectors; + } + + internal int MessageInspectorCorrelationOffset + { + get { return 0; } + } + + internal int ParameterInspectorCorrelationOffset + { + get { return messageInspectors.Length; } + } + + internal int CorrelationCount + { + get { return correlationCount; } + } + + internal IClientOperationSelector OperationSelector + { + get { return operationSelector; } + } + + internal ProxyOperationRuntime UnhandledProxyOperation + { + get { return unhandled; } + } + + internal bool UseSynchronizationContext + { + get { return useSynchronizationContext; } + } + + internal bool ValidateMustUnderstand + { + get { return validateMustUnderstand; } + set { validateMustUnderstand = value; } + } + + internal void AfterReceiveReply(ref ProxyRpc rpc) + { + int offset = MessageInspectorCorrelationOffset; + try + { + for (int i = 0; i < messageInspectors.Length; i++) + { + messageInspectors[i].AfterReceiveReply(ref rpc.Reply, rpc.Correlation[offset + i]); + //if (TD.ClientMessageInspectorAfterReceiveInvokedIsEnabled()) + //{ + // TD.ClientMessageInspectorAfterReceiveInvoked(rpc.EventTraceActivity, this.messageInspectors[i].GetType().FullName); + //} + } + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + if (ErrorBehavior.ShouldRethrowClientSideExceptionAsIs(e)) + { + throw; + } + throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(e); + } + } + + internal void BeforeSendRequest(ref ProxyRpc rpc) + { + int offset = MessageInspectorCorrelationOffset; + try + { + for (int i = 0; i < messageInspectors.Length; i++) + { + rpc.Correlation[offset + i] = messageInspectors[i].BeforeSendRequest(ref rpc.Request, (IClientChannel)rpc.Channel.Proxy); + //if (TD.ClientMessageInspectorBeforeSendInvokedIsEnabled()) + //{ + // TD.ClientMessageInspectorBeforeSendInvoked(rpc.EventTraceActivity, this.messageInspectors[i].GetType().FullName); + //} + } + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + if (ErrorBehavior.ShouldRethrowClientSideExceptionAsIs(e)) + { + throw; + } + throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(e); + } + + //if (this.addTransactionFlowProperties) + //{ + // SendTransaction(ref rpc); + //} + } + + // this should not be inlined, since we want to JIT the reference to System.Transactions + // only if transactions are being flowed. + //[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] + //static void SendTransaction(ref ProxyRpc rpc) + //{ + // System.ServiceModel.Channels.TransactionFlowProperty.Set(Transaction.Current, rpc.Request); + //} + + internal void InitializeChannel(IClientChannel channel) + { + try + { + for (int i = 0; i < channelInitializers.Length; ++i) + { + channelInitializers[i].Initialize(channel); + } + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + if (ErrorBehavior.ShouldRethrowClientSideExceptionAsIs(e)) + { + throw; + } + throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(e); + } + } + + internal ProxyOperationRuntime GetOperation(MethodBase methodBase, object[] args, out bool canCacheResult) + { + if (operationSelector == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException + (SR.Format(SR.SFxNeedProxyBehaviorOperationSelector2, methodBase.Name, + methodBase.DeclaringType.Name))); + } + + try + { + if (operationSelector.AreParametersRequiredForSelection) + { + canCacheResult = false; + } + else + { + args = null; + canCacheResult = true; + } + string operationName = operationSelector.SelectOperation(methodBase, args); + ProxyOperationRuntime operation; + if ((operationName != null) && operations.TryGetValue(operationName, out operation)) + { + return operation; + } + else + { + // did not find the right operation, will not know how + // to invoke the method. + return null; + } + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + if (ErrorBehavior.ShouldRethrowClientSideExceptionAsIs(e)) + { + throw; + } + throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(e); + } + } + + internal ProxyOperationRuntime GetOperationByName(string operationName) + { + ProxyOperationRuntime operation = null; + if (operations.TryGetValue(operationName, out operation)) + return operation; + else + return null; + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ImmutableCommunicationTimeouts.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ImmutableCommunicationTimeouts.cs new file mode 100644 index 000000000..6ee0e191c --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ImmutableCommunicationTimeouts.cs @@ -0,0 +1,55 @@ +using System; + +namespace CoreWCF.Dispatcher +{ + internal class ImmutableCommunicationTimeouts : IDefaultCommunicationTimeouts + { + TimeSpan close; + TimeSpan open; + TimeSpan receive; + TimeSpan send; + + internal ImmutableCommunicationTimeouts() + : this(null) + { + } + + internal ImmutableCommunicationTimeouts(IDefaultCommunicationTimeouts timeouts) + { + if (timeouts == null) + { + close = ServiceDefaults.CloseTimeout; + open = ServiceDefaults.OpenTimeout; + receive = ServiceDefaults.ReceiveTimeout; + send = ServiceDefaults.SendTimeout; + } + else + { + close = timeouts.CloseTimeout; + open = timeouts.OpenTimeout; + receive = timeouts.ReceiveTimeout; + send = timeouts.SendTimeout; + } + } + + TimeSpan IDefaultCommunicationTimeouts.CloseTimeout + { + get { return close; } + } + + TimeSpan IDefaultCommunicationTimeouts.OpenTimeout + { + get { return open; } + } + + TimeSpan IDefaultCommunicationTimeouts.ReceiveTimeout + { + get { return receive; } + } + + TimeSpan IDefaultCommunicationTimeouts.SendTimeout + { + get { return send; } + } + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ImmutableDispatchRuntime.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ImmutableDispatchRuntime.cs new file mode 100644 index 000000000..d7d70d5e0 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ImmutableDispatchRuntime.cs @@ -0,0 +1,1299 @@ +using System; +using System.Threading.Tasks; +using System.Collections.Specialized; +using CoreWCF.Runtime; +using CoreWCF.Channels; +using CoreWCF.Diagnostics; + +namespace CoreWCF.Dispatcher +{ + class ImmutableDispatchRuntime + { + //readonly AuthenticationBehavior authenticationBehavior; + //readonly AuthorizationBehavior authorizationBehavior; + readonly int correlationCount; + readonly ConcurrencyBehavior concurrency; + readonly IDemuxer demuxer; + readonly ErrorBehavior error; + readonly bool enableFaults; + //readonly bool ignoreTransactionFlow; + //readonly bool impersonateOnSerializingReply; + readonly IInputSessionShutdown[] inputSessionShutdownHandlers; + readonly InstanceBehavior instance; + readonly bool isOnServer; + readonly bool manualAddressing; + readonly IDispatchMessageInspector[] messageInspectors; + //readonly IRequestReplyCorrelator requestReplyCorrelator; + //readonly SecurityImpersonationBehavior securityImpersonation; + readonly TerminatingOperationBehavior terminate; + readonly ThreadBehavior thread; + //readonly TransactionBehavior transaction; + //readonly bool sendAsynchronously; + + //readonly MessageRpcProcessor processMessage1; + //readonly MessageRpcProcessor processMessage11; + //readonly MessageRpcProcessor processMessage2; + //readonly MessageRpcProcessor processMessage3; + //readonly MessageRpcProcessor processMessage31; + //readonly MessageRpcProcessor processMessage4; + //readonly MessageRpcProcessor processMessage41; + //readonly MessageRpcProcessor processMessage5; + //readonly MessageRpcProcessor processMessage6; + //readonly MessageRpcProcessor processMessage7; + //readonly MessageRpcProcessor processMessage8; + //readonly MessageRpcProcessor processMessage9; + //readonly MessageRpcProcessor processMessageCleanup; + readonly MessageRpcErrorHandler processMessageNonCleanupError; + readonly MessageRpcErrorHandler processMessageCleanupError; + + //static AsyncCallback onFinalizeCorrelationCompleted = + // Fx.ThunkCallback(new AsyncCallback(OnFinalizeCorrelationCompletedCallback)); + // TODO: Put the ThunkCallback back in + static Action onReplyCompleted = OnReplyCompletedCallback; + + //bool didTraceProcessMessage1 = false; + //bool didTraceProcessMessage2 = false; + //bool didTraceProcessMessage3 = false; + //bool didTraceProcessMessage31 = false; + //bool didTraceProcessMessage4 = false; + //bool didTraceProcessMessage41 = false; + + internal ImmutableDispatchRuntime(DispatchRuntime dispatch) + { + //this.authenticationBehavior = AuthenticationBehavior.TryCreate(dispatch); + //this.authorizationBehavior = AuthorizationBehavior.TryCreate(dispatch); + concurrency = new ConcurrencyBehavior(dispatch); + error = new ErrorBehavior(dispatch.ChannelDispatcher); + enableFaults = dispatch.EnableFaults; + inputSessionShutdownHandlers = EmptyArray.ToArray(dispatch.InputSessionShutdownHandlers); + instance = new InstanceBehavior(dispatch, this); + isOnServer = dispatch.IsOnServer; + manualAddressing = dispatch.ManualAddressing; + messageInspectors = EmptyArray.ToArray(dispatch.MessageInspectors); + //this.requestReplyCorrelator = new RequestReplyCorrelator(); + //this.securityImpersonation = SecurityImpersonationBehavior.CreateIfNecessary(dispatch); + //this.RequireClaimsPrincipalOnOperationContext = dispatch.RequireClaimsPrincipalOnOperationContext; + //this.impersonateOnSerializingReply = dispatch.ImpersonateOnSerializingReply; + terminate = TerminatingOperationBehavior.CreateIfNecessary(dispatch); + thread = new ThreadBehavior(dispatch); + ValidateMustUnderstand = dispatch.ValidateMustUnderstand; + //this.ignoreTransactionFlow = dispatch.IgnoreTransactionMessageProperty; + //this.transaction = TransactionBehavior.CreateIfNeeded(dispatch); + //sendAsynchronously = dispatch.ChannelDispatcher.SendAsynchronously; + ParameterInspectorCorrelationOffset = (dispatch.MessageInspectors.Count + + dispatch.MaxCallContextInitializers); + correlationCount = ParameterInspectorCorrelationOffset + dispatch.MaxParameterInspectors; + + DispatchOperationRuntime unhandled = new DispatchOperationRuntime(dispatch.UnhandledDispatchOperation, this); + + if (dispatch.OperationSelector == null) + { + ActionDemuxer demuxer = new ActionDemuxer(); + for (int i = 0; i < dispatch.Operations.Count; i++) + { + DispatchOperation operation = dispatch.Operations[i]; + DispatchOperationRuntime operationRuntime = new DispatchOperationRuntime(operation, this); + demuxer.Add(operation.Action, operationRuntime); + } + + demuxer.SetUnhandled(unhandled); + this.demuxer = demuxer; + } + else + { + throw new PlatformNotSupportedException(); + // CustomDemuxer demuxer = new CustomDemuxer(dispatch.OperationSelector); + // for (int i = 0; i < dispatch.Operations.Count; i++) + // { + // DispatchOperation operation = dispatch.Operations[i]; + // DispatchOperationRuntime operationRuntime = new DispatchOperationRuntime(operation, this); + // demuxer.Add(operation.Name, operationRuntime); + // } + + // demuxer.SetUnhandled(unhandled); + // this.demuxer = demuxer; + } + + //processMessage1 = new MessageRpcProcessor(ProcessMessage1); + //processMessage11 = new MessageRpcProcessor(ProcessMessage11); + //processMessage2 = new MessageRpcProcessor(ProcessMessage2); + //processMessage3 = new MessageRpcProcessor(ProcessMessage3); + //processMessage31 = new MessageRpcProcessor(ProcessMessage31); + //processMessage4 = new MessageRpcProcessor(ProcessMessage4); + //processMessage41 = new MessageRpcProcessor(ProcessMessage41); + //processMessage5 = new MessageRpcProcessor(ProcessMessage5); + //processMessage6 = new MessageRpcProcessor(ProcessMessage6); + //processMessage7 = new MessageRpcProcessor(ProcessMessage7); + //processMessage8 = new MessageRpcProcessor(ProcessMessage8); + //processMessage9 = new MessageRpcProcessor(ProcessMessage9); + //processMessageCleanup = new MessageRpcProcessor(ProcessMessageCleanup); + processMessageNonCleanupError = new MessageRpcErrorHandler(ProcessMessageNonCleanupError); + processMessageCleanupError = new MessageRpcErrorHandler(ProcessMessageCleanupError); + } + + internal int CallContextCorrelationOffset + { + get { return messageInspectors.Length; } + } + + internal int CorrelationCount + { + get { return correlationCount; } + } + + internal bool EnableFaults + { + get { return enableFaults; } + } + + // internal InstanceBehavior InstanceBehavior + // { + // get { return this.instance; } + // } + + // internal bool IsImpersonationEnabledOnSerializingReply + // { + // get { return this.impersonateOnSerializingReply; } + // } + + internal bool RequireClaimsPrincipalOnOperationContext { get; } + + internal bool ManualAddressing + { + get { return manualAddressing; } + } + + // TODO: Do we need this? It always returns 0 + internal int MessageInspectorCorrelationOffset + { + get { return 0; } + } + + internal int ParameterInspectorCorrelationOffset { get; } + + // internal IRequestReplyCorrelator RequestReplyCorrelator + // { + // get { return this.requestReplyCorrelator; } + // } + + // internal SecurityImpersonationBehavior SecurityImpersonation + // { + // get { return this.securityImpersonation; } + // } + + internal bool ValidateMustUnderstand { get; } + + internal ErrorBehavior ErrorBehavior + { + get { return error; } + } + + bool AcquireDynamicInstanceContext(ref MessageRpc rpc) + { + //if (rpc.InstanceContext.QuotaThrottle != null) + //{ + // return AcquireDynamicInstanceContextCore(ref rpc); + //} + //else + //{ + return true; + //} + } + + // bool AcquireDynamicInstanceContextCore(ref MessageRpc rpc) + // { + // bool success = rpc.InstanceContext.QuotaThrottle.Acquire(rpc.Pause()); + + // if (success) + // { + // rpc.UnPause(); + // } + + // return success; + // } + + internal void AfterReceiveRequest(ref MessageRpc rpc) + { + if (messageInspectors.Length > 0) + { + AfterReceiveRequestCore(ref rpc); + } + } + + internal void AfterReceiveRequestCore(ref MessageRpc rpc) + { + int offset = MessageInspectorCorrelationOffset; + try + { + for (int i = 0; i < messageInspectors.Length; i++) + { + rpc.Correlation[offset + i] = messageInspectors[i].AfterReceiveRequest(ref rpc.Request, (IClientChannel)rpc.Channel.Proxy, rpc.InstanceContext); + //if (TD.MessageInspectorAfterReceiveInvokedIsEnabled()) + //{ + // TD.MessageInspectorAfterReceiveInvoked(rpc.EventTraceActivity, this.messageInspectors[i].GetType().FullName); + //} + } + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + if (ErrorBehavior.ShouldRethrowExceptionAsIs(e)) + { + throw; + } + throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(e); + } + } + + void BeforeSendReply(ref MessageRpc rpc, ref Exception exception, ref bool thereIsAnUnhandledException) + { + if (messageInspectors.Length > 0) + { + BeforeSendReplyCore(ref rpc, ref exception, ref thereIsAnUnhandledException); + } + } + + internal void BeforeSendReplyCore(ref MessageRpc rpc, ref Exception exception, ref bool thereIsAnUnhandledException) + { + int offset = MessageInspectorCorrelationOffset; + for (int i = 0; i < messageInspectors.Length; i++) + { + try + { + Message originalReply = rpc.Reply; + Message reply = originalReply; + + messageInspectors[i].BeforeSendReply(ref reply, rpc.Correlation[offset + i]); + //if (TD.MessageInspectorBeforeSendInvokedIsEnabled()) + //{ + // TD.MessageInspectorBeforeSendInvoked(rpc.EventTraceActivity, this.messageInspectors[i].GetType().FullName); + //} + + if ((reply == null) && (originalReply != null)) + { + string message = SR.Format(SR.SFxNullReplyFromExtension2, messageInspectors[i].GetType().ToString(), (rpc.Operation.Name ?? "")); + ErrorBehavior.ThrowAndCatch(new InvalidOperationException(message)); + } + rpc.Reply = reply; + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + if (!ErrorBehavior.ShouldRethrowExceptionAsIs(e)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(e); + } + + if (exception == null) + { + exception = e; + } + thereIsAnUnhandledException = (!error.HandleError(e)) || thereIsAnUnhandledException; + } + } + } + + private async Task ReplyAsync(MessageRpc rpc) + { + rpc.RequestContextThrewOnReply = true; + rpc.SuccessfullySendReply = false; + + try + { + await rpc.RequestContext.ReplyAsync(rpc.Reply, rpc.ReplyTimeoutHelper.GetCancellationToken()); + rpc.RequestContextThrewOnReply = false; + rpc.SuccessfullySendReply = true; + + //if (TD.DispatchMessageStopIsEnabled()) + //{ + // TD.DispatchMessageStop(rpc.EventTraceActivity); + //} + } + catch (CommunicationException e) + { + error.HandleError(e); + } + catch (TimeoutException e) + { + error.HandleError(e); + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + + //if (DiagnosticUtility.ShouldTraceError) + //{ + // TraceUtility.TraceEvent(TraceEventType.Error, TraceCode.ServiceOperationExceptionOnReply, + // SR.Format(SR.TraceCodeServiceOperationExceptionOnReply), + // this, e); + //} + + if (!error.HandleError(e)) + { + rpc.RequestContextThrewOnReply = true; + rpc.CanSendReply = false; + } + } + + return rpc; + } + + void Reply(ref MessageRpc rpc) + { + rpc.RequestContextThrewOnReply = true; + rpc.SuccessfullySendReply = false; + + try + { + rpc.RequestContext.ReplyAsync(rpc.Reply, rpc.ReplyTimeoutHelper.GetCancellationToken()).GetAwaiter().GetResult(); + rpc.RequestContextThrewOnReply = false; + rpc.SuccessfullySendReply = true; + + //if (TD.DispatchMessageStopIsEnabled()) + //{ + // TD.DispatchMessageStop(rpc.EventTraceActivity); + //} + } + catch (CommunicationException e) + { + error.HandleError(e); + } + catch (TimeoutException e) + { + error.HandleError(e); + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + + //if (DiagnosticUtility.ShouldTraceError) + //{ + // TraceUtility.TraceEvent(TraceEventType.Error, TraceCode.ServiceOperationExceptionOnReply, + // SR.Format(SR.TraceCodeServiceOperationExceptionOnReply), + // this, e); + //} + + if (!error.HandleError(e)) + { + rpc.RequestContextThrewOnReply = true; + rpc.CanSendReply = false; + } + } + } + + internal Task DispatchAsync(ref MessageRpc rpc, bool isOperationContextSet) + { + rpc.ErrorProcessor = processMessageNonCleanupError; + rpc.AsyncProcessor = ProcessMessageAsync; + return rpc.ProcessAsync(isOperationContextSet); + } + + // void EndFinalizeCorrelation(ref MessageRpc rpc) + // { + // try + // { + // rpc.Reply = rpc.CorrelationCallback.EndFinalizeCorrelation(rpc.AsyncResult); + // } + // catch (Exception e) + // { + // if (Fx.IsFatal(e)) + // { + // throw; + // } + + // if (!this.error.HandleError(e)) + // { + // rpc.CanSendReply = false; + // } + // } + // } + + bool EndReply(ref MessageRpc rpc) + { + bool success = false; + + try + { + rpc.TaskResult.GetAwaiter().GetResult(); + rpc.RequestContextThrewOnReply = false; + success = true; + + //if (TD.DispatchMessageStopIsEnabled()) + //{ + // TD.DispatchMessageStop(rpc.EventTraceActivity); + //} + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + + error.HandleError(e); + } + + return success; + } + + internal void InputSessionDoneReceiving(ServiceChannel channel) + { + if (inputSessionShutdownHandlers.Length > 0) + { + InputSessionDoneReceivingCore(channel); + } + } + + void InputSessionDoneReceivingCore(ServiceChannel channel) + { + IDuplexContextChannel proxy = channel.Proxy as IDuplexContextChannel; + + if (proxy != null) + { + IInputSessionShutdown[] handlers = inputSessionShutdownHandlers; + try + { + for (int i = 0; i < handlers.Length; i++) + { + handlers[i].DoneReceiving(proxy); + } + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + if (!error.HandleError(e)) + { + proxy.Abort(); + } + } + } + } + + internal bool IsConcurrent(ref MessageRpc rpc) + { + return concurrency.IsConcurrent(ref rpc); + } + + internal void InputSessionFaulted(ServiceChannel channel) + { + if (inputSessionShutdownHandlers.Length > 0) + { + InputSessionFaultedCore(channel); + } + } + + void InputSessionFaultedCore(ServiceChannel channel) + { + IDuplexContextChannel proxy = channel.Proxy as IDuplexContextChannel; + + if (proxy != null) + { + IInputSessionShutdown[] handlers = inputSessionShutdownHandlers; + try + { + for (int i = 0; i < handlers.Length; i++) + { + handlers[i].ChannelFaulted(proxy); + } + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + if (!error.HandleError(e)) + { + proxy.Abort(); + } + } + } + } + + // static internal void GotDynamicInstanceContext(object state) + // { + // bool alreadyResumedNoLock; + // ((IResumeMessageRpc)state).Resume(out alreadyResumedNoLock); + + // if (alreadyResumedNoLock) + // { + // Fx.Assert("GotDynamicInstanceContext more than once for same call."); + // } + // } + + void AddMessageProperties(Message message, OperationContext context, ServiceChannel replyChannel) + { + if (context.InternalServiceChannel == replyChannel) + { + if (context.HasOutgoingMessageHeaders) + { + message.Headers.CopyHeadersFrom(context.OutgoingMessageHeaders); + } + + if (context.HasOutgoingMessageProperties) + { + message.Properties.MergeProperties(context.OutgoingMessageProperties); + } + } + } + + // static void OnFinalizeCorrelationCompletedCallback(IAsyncResult result) + // { + // if (result.CompletedSynchronously) + // { + // return; + // } + + // IResumeMessageRpc resume = result.AsyncState as IResumeMessageRpc; + + // if (resume == null) + // { + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.Format(SR.SFxInvalidAsyncResultState0)); + // } + + // resume.Resume(result); + // } + + static void OnReplyCompletedCallback(Task result, object state) + { + IResumeMessageRpc resume = state as IResumeMessageRpc; + + if (resume == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.SFxInvalidAsyncResultState0); + } + + resume.Resume(result); + } + + void PrepareReply(ref MessageRpc rpc) + { + RequestContext context = rpc.OperationContext.RequestContext; + Exception exception = null; + bool thereIsAnUnhandledException = false; + + if (!rpc.Operation.IsOneWay) + { + //if (DiagnosticUtility.ShouldTraceWarning) + //{ + // // If a service both returns null and sets RequestContext null, that + // // means they handled it (either by calling Close or Reply manually). + // // These traces catch accidents, where you accidentally return null, + // // or you accidentally close the context so we can't return your message. + // if ((rpc.Reply == null) && (context != null)) + // { + // TraceUtility.TraceEvent(System.Diagnostics.TraceEventType.Warning, + // TraceCode.ServiceOperationMissingReply, + // SR.Format(SR.TraceCodeServiceOperationMissingReply, rpc.Operation.Name ?? String.Empty), + // null, null); + // } + // else if ((context == null) && (rpc.Reply != null)) + // { + // TraceUtility.TraceEvent(System.Diagnostics.TraceEventType.Warning, + // TraceCode.ServiceOperationMissingReplyContext, + // SR.Format(SR.TraceCodeServiceOperationMissingReplyContext, rpc.Operation.Name ?? String.Empty), + // null, null); + // } + //} + + if ((context != null) && (rpc.Reply != null)) + { + try + { + rpc.CanSendReply = PrepareAndAddressReply(ref rpc); + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + thereIsAnUnhandledException = (!error.HandleError(e)) || thereIsAnUnhandledException; + exception = e; + } + } + } + + BeforeSendReply(ref rpc, ref exception, ref thereIsAnUnhandledException); + + if (rpc.Operation.IsOneWay) + { + rpc.CanSendReply = false; + } + + if (!rpc.Operation.IsOneWay && (context != null) && (rpc.Reply != null)) + { + if (exception != null) + { + // We don't call ProvideFault again, since we have already passed the + // point where SFx addresses the reply, and it is reasonable for + // ProvideFault to expect that SFx will address the reply. Instead + // we always just do 'internal server error' processing. + rpc.Error = exception; + error.ProvideOnlyFaultOfLastResort(ref rpc); + + try + { + rpc.CanSendReply = PrepareAndAddressReply(ref rpc); + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + error.HandleError(e); + } + } + } + else if ((exception != null) && thereIsAnUnhandledException) + { + rpc.Abort(); + } + } + + bool PrepareAndAddressReply(ref MessageRpc rpc) + { + bool canSendReply = true; + + if (!manualAddressing) + { + if (!object.ReferenceEquals(rpc.RequestID, null)) + { + RequestReplyCorrelator.PrepareReply(rpc.Reply, rpc.RequestID); + } + + if (!rpc.Channel.HasSession) + { + canSendReply = RequestReplyCorrelator.AddressReply(rpc.Reply, rpc.ReplyToInfo); + } + } + + AddMessageProperties(rpc.Reply, rpc.OperationContext, rpc.Channel); + //if (FxTrace.Trace.IsEnd2EndActivityTracingEnabled && rpc.EventTraceActivity != null) + //{ + // rpc.Reply.Properties[EventTraceActivity.Name] = rpc.EventTraceActivity; + //} + + return canSendReply; + } + + internal DispatchOperationRuntime GetOperation(ref Message message) + { + return demuxer.GetOperation(ref message); + } + + internal async Task ProcessMessageAsync(MessageRpc rpc) + { + if (rpc.Operation.IsOneWay) + { + await rpc.RequestContext.ReplyAsync(null); + rpc.OperationContext.RequestContext = null; + } + else + { + if (!rpc.Channel.IsReplyChannel && + ((object)rpc.RequestID == null) && + (rpc.Operation.Action != MessageHeaders.WildcardAction)) + { + CommunicationException error = new CommunicationException(SR.SFxOneWayMessageToTwoWayMethod0); + throw TraceUtility.ThrowHelperError(error, rpc.Request); + } + + if (!manualAddressing) + { + EndpointAddress replyTo = rpc.ReplyToInfo.ReplyTo; + if (replyTo != null && replyTo.IsNone && rpc.Channel.IsReplyChannel) + { + CommunicationException error = new CommunicationException(SR.SFxRequestReplyNone); + throw TraceUtility.ThrowHelperError(error, rpc.Request); + } + + if (isOnServer) + { + EndpointAddress remoteAddress = rpc.Channel.RemoteAddress; + if ((remoteAddress != null) && !remoteAddress.IsAnonymous) + { + MessageHeaders headers = rpc.Request.Headers; + Uri remoteUri = remoteAddress.Uri; + + if ((replyTo != null) && !replyTo.IsAnonymous && (remoteUri != replyTo.Uri)) + { + string text = SR.Format(SR.SFxRequestHasInvalidReplyToOnServer, replyTo.Uri, remoteUri); + Exception error = new InvalidOperationException(text); + throw TraceUtility.ThrowHelperError(error, rpc.Request); + } + + EndpointAddress faultTo = headers.FaultTo; + if ((faultTo != null) && !faultTo.IsAnonymous && (remoteUri != faultTo.Uri)) + { + string text = SR.Format(SR.SFxRequestHasInvalidFaultToOnServer, faultTo.Uri, remoteUri); + Exception error = new InvalidOperationException(text); + throw TraceUtility.ThrowHelperError(error, rpc.Request); + } + + if (rpc.RequestVersion.Addressing == AddressingVersion.WSAddressingAugust2004) + { + EndpointAddress from = headers.From; + if ((from != null) && !from.IsAnonymous && (remoteUri != from.Uri)) + { + string text = SR.Format(SR.SFxRequestHasInvalidFromOnServer, from.Uri, remoteUri); + Exception error = new InvalidOperationException(text); + throw TraceUtility.ThrowHelperError(error, rpc.Request); + } + } + } + } + } + } + + if (concurrency.IsConcurrent(ref rpc)) + { + rpc.Channel.IncrementActivity(); + rpc.SuccessfullyIncrementedActivity = true; + } + + //if (this.authenticationBehavior != null) + //{ + // this.authenticationBehavior.Authenticate(ref rpc); + //} + + //if (this.authorizationBehavior != null) + //{ + // this.authorizationBehavior.Authorize(ref rpc); + //} + + instance.EnsureInstanceContext(ref rpc); + // TODO: Work out what function the pending list has and re-add/replace/remove functionality + //TransferChannelFromPendingList(ref rpc); + + AcquireDynamicInstanceContext(ref rpc); + + AfterReceiveRequest(ref rpc); + + //if (!this.ignoreTransactionFlow) + //{ + // // Transactions need to have the context in the message + // rpc.TransactionMessageProperty = TransactionMessageProperty.TryGet(rpc.Request); + //} + + rpc = await concurrency.LockInstanceAsync(rpc); + + rpc.SuccessfullyLockedInstance = true; + + // This also needs to happen after LockInstance, in case + // we are using an AutoComplete=false transaction. + //if (this.transaction != null) + //{ + // this.transaction.ResolveTransaction(ref rpc); + // if (rpc.Operation.TransactionRequired) + // { + // this.transaction.SetCurrent(ref rpc); + // } + //} + + //rpc.NextProcessor = processMessage4; + + //if (this.transaction != null) + //{ + // if (rpc.Operation.TransactionRequired) + // { + // ReceiveContextRPCFacet receiveContext = rpc.ReceiveContext; + + // if (receiveContext != null) + // { + // rpc.ReceiveContext = null; + // receiveContext.Complete(this, ref rpc, TimeSpan.MaxValue, rpc.Transaction.Current); + // } + // } + //} + //rpc.NextProcessor = processMessage41; + + try + { + // TaskHelpers has an extension method which enables awaitting a sync context to run continuation on it. + await thread.GetSyncContext(rpc); + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + + throw DiagnosticUtility.ExceptionUtility.ThrowHelperFatal(e.Message, e); + } + + // This needs to happen after LockInstance--LockInstance guarantees + // in-order delivery, so we can't receive the next message until we + // have acquired the lock. + // + // This also needs to happen after BindThread, since EricZ believes + // that running on UI thread should guarantee in-order delivery if + // the SynchronizationContext is single threaded. + // Note: for IManualConcurrencyOperationInvoker, the invoke assumes full control over pumping. + // TODO: This is the concurrency gate. If the service is concurrent, this allows another receive to happen. This mechanism needs replacing. + //if (concurrency.IsConcurrent(ref rpc)) + //{ + // rpc.EnsureReceive(); + //} + + instance.EnsureServiceInstance(ref rpc); + + try + { + //if (!rpc.Operation.IsSynchronous) + //{ + // // If async call completes in sync, it tells us through the gate below + // rpc.PrepareInvokeContinueGate(); + //} + + //if (this.transaction != null) + //{ + // this.transaction.InitializeCallContext(ref rpc); + //} + + //SetActivityIdOnThread(ref rpc); + + rpc = await rpc.Operation.InvokeAsync(rpc); + } + catch + { + // This catch clause forces ClearCallContext to run prior to stackwalks exiting this frame. + throw; + } + + try + { + // Switch back to thread pool if we're using a non-default Sync Context + await TaskHelpers.EnsureDefaultTaskScheduler(); + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + + throw DiagnosticUtility.ExceptionUtility.ThrowHelperFatal(e.Message, e); + } + + try + { + error.ProvideMessageFault(ref rpc); + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + + error.HandleError(e); + } + + PrepareReply(ref rpc); + + if (rpc.CanSendReply) + { + rpc.ReplyTimeoutHelper = new TimeoutHelper(rpc.Channel.OperationTimeout); + } + + if (rpc.CanSendReply) + { + //if (rpc.Reply != null) + //{ + // TraceUtility.MessageFlowAtMessageSent(rpc.Reply, rpc.EventTraceActivity); + //} + + rpc = await ReplyAsync(rpc); + } + + // Logic for knowing when to close stuff: + // + // ASSUMPTIONS: + // Closing a stream over a message also closes the message. + // Closing a message over a stream does not close the stream. + // (OperationStreamProvider.ReleaseStream is no-op) + // + // This is a table of what should be disposed in what cases. + // The rows represent the type of parameter to the method and + // whether we are disposing parameters or not. The columns + // are for the inputs vs. the outputs. The cells contain the + // values that need to be Disposed. M^P means that exactly + // one of the message and parameter needs to be disposed, + // since they refer to the same object. + // + // Request Reply + // Message | M or P | M or P + // Dispose Stream | P | M and P + // Params | M and P | M and P + // | | + // Message | none | none + // NoDispose Stream | none | M + // Params | M | M + // + // By choosing to dispose the parameter in both of the "M or P" + // cases, the logic needed to generate this table is: + // + // CloseRequestMessage = IsParams + // CloseRequestParams = rpc.Operation.DisposeParameters + // CloseReplyMessage = rpc.Operation.SerializeReply + // CloseReplyParams = rpc.Operation.DisposeParameters + // + // IsParams can be calculated based on whether the request + // message was consumed after deserializing but before calling + // the user. This is stored as rpc.DidDeserializeRequestBody. + // + Fx.Assert( + !object.ReferenceEquals(rpc.ErrorProcessor, processMessageCleanupError), + "ProcessMessageCleanup run twice on the same MessageRpc!"); + rpc.ErrorProcessor = processMessageCleanupError; + + bool replyWasSent = false; + + if (rpc.CanSendReply) + { + replyWasSent = rpc.SuccessfullySendReply; + } + + try + { + try + { + if (rpc.DidDeserializeRequestBody) + { + rpc.Request.Close(); + } + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + error.HandleError(e); + } + + rpc.DisposeParameters(false); //Dispose all input/output/return parameters + + if (rpc.FaultInfo.IsConsideredUnhandled) + { + if (!replyWasSent) + { + rpc.AbortRequestContext(); + rpc.AbortChannel(); + } + else + { + rpc.CloseRequestContext(); + rpc.CloseChannel(); + } + rpc.AbortInstanceContext(); + } + else + { + if (rpc.RequestContextThrewOnReply) + { + rpc.AbortRequestContext(); + } + else + { + rpc.CloseRequestContext(); + } + } + + + if ((rpc.Reply != null) && (rpc.Reply != rpc.ReturnParameter)) + { + try + { + rpc.Reply.Close(); + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + error.HandleError(e); + } + } + + if ((rpc.FaultInfo.Fault != null) && (rpc.FaultInfo.Fault.State != MessageState.Closed)) + { + // maybe ProvideFault gave a Message, but then BeforeSendReply replaced it + // in that case, we need to close the one from ProvideFault + try + { + rpc.FaultInfo.Fault.Close(); + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + error.HandleError(e); + } + } + + try + { + rpc.OperationContext.FireOperationCompleted(); + } + + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(e); + } + + instance.AfterReply(ref rpc, error); + + if (rpc.SuccessfullyLockedInstance) + { + try + { + concurrency.UnlockInstance(ref rpc); + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + + Fx.Assert("Exceptions should be caught by callee"); + rpc.InstanceContext.FaultInternal(); + error.HandleError(e); + } + } + + if (terminate != null) + { + try + { + terminate.AfterReply(ref rpc); + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + error.HandleError(e); + } + } + + if (rpc.SuccessfullyIncrementedActivity) + { + try + { + rpc.Channel.DecrementActivity(); + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + error.HandleError(e); + } + } + } + finally + { + //if (rpc.MessageRpcOwnsInstanceContextThrottle && rpc.channelHandler.InstanceContextServiceThrottle != null) + //{ + // rpc.channelHandler.InstanceContextServiceThrottle.DeactivateInstanceContext(); + //} + + //if (rpc.Activity != null && DiagnosticUtility.ShouldUseActivity) + //{ + // rpc.Activity.Stop(); + //} + } + + error.HandleError(ref rpc); + return rpc; + } + + void ProcessMessageNonCleanupError(ref MessageRpc rpc) + { + try + { + this.error.ProvideMessageFault(ref rpc); + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + + this.error.HandleError(e); + } + + this.PrepareReply(ref rpc); + } + + void ProcessMessageCleanupError(ref MessageRpc rpc) + { + error.HandleError(ref rpc); + } + + // void ResolveTransactionOutcome(ref MessageRpc rpc) + // { + // if (this.transaction != null) + // { + // try + // { + // bool hadError = (rpc.Error != null); + // try + // { + // this.transaction.ResolveOutcome(ref rpc); + // } + // catch (FaultException e) + // { + // if (rpc.Error == null) + // { + // rpc.Error = e; + // } + // } + // finally + // { + // if (!hadError && rpc.Error != null) + // { + // this.error.ProvideMessageFault(ref rpc); + // this.PrepareAndAddressReply(ref rpc); + // } + // } + // } + // catch (Exception e) + // { + // if (Fx.IsFatal(e)) + // { + // throw; + // } + // this.error.HandleError(e); + // } + + // } + // } + + // [Fx.Tag.SecurityNote(Critical = "Calls security critical method to set the ActivityId on the thread", + // Safe = "Set the ActivityId only when MessageRpc is available")] + // [SecuritySafeCritical] + // void SetActivityIdOnThread(ref MessageRpc rpc) + // { + // if (FxTrace.Trace.IsEnd2EndActivityTracingEnabled && rpc.EventTraceActivity != null) + // { + // // Propogate the ActivityId to the service operation + // EventTraceActivityHelper.SetOnThread(rpc.EventTraceActivity); + // } + // } + + interface IDemuxer + { + DispatchOperationRuntime GetOperation(ref Message request); + } + + class ActionDemuxer : IDemuxer + { + HybridDictionary map; + DispatchOperationRuntime unhandled; + + internal ActionDemuxer() + { + map = new HybridDictionary(); + } + + internal void Add(string action, DispatchOperationRuntime operation) + { + if (map.Contains(action)) + { + DispatchOperationRuntime existingOperation = (DispatchOperationRuntime)map[action]; + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxActionDemuxerDuplicate, existingOperation.Name, operation.Name, action))); + } + map.Add(action, operation); + } + + internal void SetUnhandled(DispatchOperationRuntime operation) + { + unhandled = operation; + } + + public DispatchOperationRuntime GetOperation(ref Message request) + { + string action = request.Headers.Action; + if (action == null) + { + action = MessageHeaders.WildcardAction; + } + DispatchOperationRuntime operation = (DispatchOperationRuntime)map[action]; + if (operation != null) + { + return operation; + } + + return unhandled; + } + } + + // class CustomDemuxer : IDemuxer + // { + // Dictionary map; + // IDispatchOperationSelector selector; + // DispatchOperationRuntime unhandled; + + // internal CustomDemuxer(IDispatchOperationSelector selector) + // { + // this.selector = selector; + // this.map = new Dictionary(); + // } + + // internal void Add(string name, DispatchOperationRuntime operation) + // { + // this.map.Add(name, operation); + // } + + // internal void SetUnhandled(DispatchOperationRuntime operation) + // { + // this.unhandled = operation; + // } + + // public DispatchOperationRuntime GetOperation(ref Message request) + // { + // string operationName = this.selector.SelectOperation(ref request); + // DispatchOperationRuntime operation = null; + // if (this.map.TryGetValue(operationName, out operation)) + // { + // return operation; + // } + // else + // { + // return this.unhandled; + // } + // } + // } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/InputChannelBinder.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/InputChannelBinder.cs new file mode 100644 index 000000000..27edaf408 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/InputChannelBinder.cs @@ -0,0 +1,136 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using CoreWCF.Runtime; +using CoreWCF.Channels; +using CoreWCF.Diagnostics; + +namespace CoreWCF.Dispatcher +{ + internal class InputChannelBinder : IChannelBinder + { + IInputChannel channel; + Uri listenUri; + + internal InputChannelBinder(IInputChannel channel, Uri listenUri) + { + if (!((channel != null))) + { + Fx.Assert("InputChannelBinder.InputChannelBinder: (channel != null)"); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("channel"); + } + this.channel = channel; + this.listenUri = listenUri; + } + + public IChannel Channel + { + get { return channel; } + } + + public bool HasSession + { + get { return channel is ISessionChannel; } + } + + public Uri ListenUri + { + get { return listenUri; } + } + + public EndpointAddress LocalAddress + { + get { return channel.LocalAddress; } + } + + public EndpointAddress RemoteAddress + { + get + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException()); + } + } + + public void Abort() + { + channel.Abort(); + } + + public void CloseAfterFault(TimeSpan timeout) + { + var helper = new TimeoutHelper(timeout); + channel.CloseAsync(helper.GetCancellationToken()).GetAwaiter().GetResult(); + } + + public RequestContext CreateRequestContext(Message message) + { + return WrapMessage(message); + } + + public Task SendAsync(Message message, CancellationToken token) + { + throw TraceUtility.ThrowHelperError(new NotImplementedException(), message); + } + + public async Task> TryReceiveAsync(CancellationToken token) + { + var result = await channel.TryReceiveAsync(token); + if (result.Success) + { + return TryAsyncResult.FromResult(WrapMessage(result.Result)); + } + else + { + return TryAsyncResult.FailedResult; + } + } + + public Task RequestAsync(Message message, CancellationToken token) + { + throw TraceUtility.ThrowHelperError(new NotImplementedException(), message); + } + + public Task WaitForMessageAsync(CancellationToken token) + { + return channel.WaitForMessageAsync(token); + } + + RequestContext WrapMessage(Message message) + { + if (message == null) + { + return null; + } + else + { + return new InputRequestContext(message, this); + } + } + + class InputRequestContext : RequestContextBase + { + InputChannelBinder binder; + + internal InputRequestContext(Message request, InputChannelBinder binder) + : base(request, TimeSpan.Zero, TimeSpan.Zero) + { + this.binder = binder; + } + + protected override void OnAbort() + { + } + + protected override Task OnCloseAsync(CancellationToken token) + { + return Task.CompletedTask; + } + + protected override Task OnReplyAsync(Message message, CancellationToken token) + { + return Task.CompletedTask; + } + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/InstanceBehavior.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/InstanceBehavior.cs new file mode 100644 index 000000000..96909f690 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/InstanceBehavior.cs @@ -0,0 +1,292 @@ +using System; +using System.Reflection; +using CoreWCF.Runtime; +using CoreWCF.Channels; +using CoreWCF.Diagnostics; + +namespace CoreWCF.Dispatcher +{ + internal class InstanceBehavior + { + const BindingFlags DefaultBindingFlags = BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public; + + IInstanceContextInitializer[] initializers; + IInstanceContextProvider instanceContextProvider; + IInstanceProvider provider; + InstanceContext singleton; + //bool transactionAutoCompleteOnSessionClose; + //bool releaseServiceInstanceOnTransactionComplete = true; + bool isSynchronized; + ImmutableDispatchRuntime immutableRuntime; + + internal InstanceBehavior(DispatchRuntime dispatch, ImmutableDispatchRuntime immutableRuntime) + { + this.immutableRuntime = immutableRuntime; + initializers = EmptyArray.ToArray(dispatch.InstanceContextInitializers); + provider = dispatch.InstanceProvider; + singleton = dispatch.SingletonInstanceContext; + // this.transactionAutoCompleteOnSessionClose = dispatch.TransactionAutoCompleteOnSessionClose; + // this.releaseServiceInstanceOnTransactionComplete = dispatch.ReleaseServiceInstanceOnTransactionComplete; + isSynchronized = (dispatch.ConcurrencyMode != ConcurrencyMode.Multiple); + instanceContextProvider = dispatch.InstanceContextProvider; + + if (provider == null) + { + ConstructorInfo constructor = null; + if (dispatch.Type != null) + { + constructor = InstanceBehavior.GetConstructor(dispatch.Type); + } + + if (singleton == null) + { + if (dispatch.Type != null && (dispatch.Type.GetTypeInfo().IsAbstract || dispatch.Type.GetTypeInfo().IsInterface)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.SFxServiceTypeNotCreatable)); + } + + if (constructor == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.SFxNoDefaultConstructor)); + } + } + + if (constructor != null) + { + if (singleton == null || !singleton.IsWellKnown) + { + InvokerUtil util = new InvokerUtil(); + CreateInstanceDelegate creator = util.GenerateCreateInstanceDelegate(dispatch.Type, constructor); + provider = new InstanceProvider(creator); + } + } + } + + if (singleton != null) + { + singleton.Behavior = this; + } + } + + //internal bool TransactionAutoCompleteOnSessionClose + //{ + // get + // { + // return this.transactionAutoCompleteOnSessionClose; + // } + //} + + //internal bool ReleaseServiceInstanceOnTransactionComplete + //{ + // get + // { + // return this.releaseServiceInstanceOnTransactionComplete; + // } + //} + + internal IInstanceContextProvider InstanceContextProvider + { + get + { + return instanceContextProvider; + } + } + + internal void AfterReply(ref MessageRpc rpc, ErrorBehavior error) + { + InstanceContext context = rpc.InstanceContext; + + if (context != null) + { + try + { + if (rpc.Operation.ReleaseInstanceAfterCall) + { + if (context.State == CommunicationState.Opened) + { + context.ReleaseServiceInstance(); + } + } + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + error.HandleError(e); + } + + try + { + context.UnbindRpc(ref rpc); + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + error.HandleError(e); + } + } + } + + internal bool CanUnload(InstanceContext instanceContext) + { + if (InstanceContextProviderBase.IsProviderSingleton(instanceContextProvider)) + return false; + + if (InstanceContextProviderBase.IsProviderPerCall(instanceContextProvider) || + InstanceContextProviderBase.IsProviderSessionful(instanceContextProvider)) + return true; + + //User provided InstanceContextProvider. Call the provider to check for idle. + if (!instanceContextProvider.IsIdle(instanceContext)) + { + instanceContextProvider.NotifyIdle(InstanceContext.NotifyIdleCallback, instanceContext); + return false; + } + return true; + } + + internal void EnsureInstanceContext(ref MessageRpc rpc) + { + if (rpc.InstanceContext == null) + { + rpc.InstanceContext = new InstanceContext(rpc.Host, false); + } + + rpc.OperationContext.SetInstanceContext(rpc.InstanceContext); + rpc.InstanceContext.Behavior = this; + + if (rpc.InstanceContext.State == CommunicationState.Created) + { + lock (rpc.InstanceContext.ThisLock) + { + if (rpc.InstanceContext.State == CommunicationState.Created) + { + var helper = new TimeoutHelper(rpc.Channel.CloseTimeout); + rpc.InstanceContext.OpenAsync(helper.GetCancellationToken()).GetAwaiter().GetResult(); + } + } + } + rpc.InstanceContext.BindRpc(ref rpc); + } + + static ConstructorInfo GetConstructor(Type type) + { + foreach (var constructor in type.GetConstructors(DefaultBindingFlags)) + { + if (constructor.GetParameters().Length == 0) + return constructor; + } + return null; + } + + internal object GetInstance(InstanceContext instanceContext) + { + if (provider == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.SFxNoDefaultConstructor)); + } + + return provider.GetInstance(instanceContext); + } + + internal object GetInstance(InstanceContext instanceContext, Message request) + { + if (provider == null) + { + throw TraceUtility.ThrowHelperError(new InvalidOperationException(SR.SFxNoDefaultConstructor), request); + } + + return provider.GetInstance(instanceContext, request); + } + + internal void Initialize(InstanceContext instanceContext) + { + OperationContext current = OperationContext.Current; + Message message = (current != null) ? current.IncomingMessage : null; + + if (current != null && current.InternalServiceChannel != null) + { + IContextChannel transparentProxy = (IContextChannel)current.InternalServiceChannel.Proxy; + instanceContextProvider.InitializeInstanceContext(instanceContext, message, transparentProxy); + } + + for (int i = 0; i < initializers.Length; i++) + initializers[i].Initialize(instanceContext, message); + } + + internal void EnsureServiceInstance(ref MessageRpc rpc) + { + if (rpc.Operation.ReleaseInstanceBeforeCall) + { + rpc.InstanceContext.ReleaseServiceInstance(); + } + + //if (TD.GetServiceInstanceStartIsEnabled()) + //{ + // TD.GetServiceInstanceStart(rpc.EventTraceActivity); + //} + + rpc.Instance = rpc.InstanceContext.GetServiceInstance(rpc.Request); + + //if (TD.GetServiceInstanceStopIsEnabled()) + //{ + // TD.GetServiceInstanceStop(rpc.EventTraceActivity); + //} + } + + internal void ReleaseInstance(InstanceContext instanceContext, object instance) + { + if (provider != null) + { + try + { + provider.ReleaseInstance(instanceContext, instance); + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + immutableRuntime.ErrorBehavior.HandleError(e); + } + } + } + } + + class InstanceProvider : IInstanceProvider + { + CreateInstanceDelegate creator; + + internal InstanceProvider(CreateInstanceDelegate creator) + { + if (creator == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("creator"); + + this.creator = creator; + } + + public object GetInstance(InstanceContext instanceContext) + { + return creator(); + } + + public object GetInstance(InstanceContext instanceContext, Message message) + { + return creator(); + } + + public void ReleaseInstance(InstanceContext instanceContext, object instance) + { + IDisposable dispose = instance as IDisposable; + if (dispose != null) + dispose.Dispose(); + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/InstanceContextManager.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/InstanceContextManager.cs new file mode 100644 index 000000000..7603dc34e --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/InstanceContextManager.cs @@ -0,0 +1,245 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using CoreWCF.Runtime; +using CoreWCF.Channels; +using System.Diagnostics; + +namespace CoreWCF.Dispatcher +{ + internal interface IInstanceContextManager + { + void Abort(); + void Add(InstanceContext instanceContext); + Task CloseAsync(CancellationToken token); + Task CloseInputAsync(CancellationToken token); + bool Remove(InstanceContext instanceContext); + InstanceContext[] ToArray(); + } + + internal class InstanceContextManager : LifetimeManager, IInstanceContextManager + { + int firstFreeIndex; + Item[] items; + + public InstanceContextManager(object mutex) + : base(mutex) + { + } + + public void Add(InstanceContext instanceContext) + { + bool added = false; + + lock (ThisLock) + { + if (State == LifetimeState.Opened) + { + if (instanceContext.InstanceContextManagerIndex != 0) + return; + if (firstFreeIndex == 0) + GrowItems(); + AddItem(instanceContext); + base.IncrementBusyCountWithoutLock(); + added = true; + } + } + + if (!added) + { + instanceContext.Abort(); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ObjectDisposedException(GetType().ToString())); + } + } + + void AddItem(InstanceContext instanceContext) + { + int index = firstFreeIndex; + firstFreeIndex = items[index].nextFreeIndex; + items[index].instanceContext = instanceContext; + instanceContext.InstanceContextManagerIndex = index; + } + + private async Task CloseInitiateAsync(CancellationToken token) + { + InstanceContext[] instances = ToArray(); + for (int index = 0; index < instances.Length; index++) + { + InstanceContext instance = instances[index]; + try + { + if (instance.State == CommunicationState.Opened) + { + Task result = instance.CloseAsync(token); + if (!result.IsCompleted) + { + ContinueCloseInstanceContext(result); + continue; + } + + await result; + } + else + { + instance.Abort(); + } + } + catch (ObjectDisposedException e) + { + DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); + } + catch (InvalidOperationException e) + { + DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); + } + catch (CommunicationException e) + { + DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); + } + catch (TimeoutException e) + { + //if (TD.CloseTimeoutIsEnabled()) + //{ + // TD.CloseTimeout(e.Message); + //} + DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); + } + } + } + + public async Task CloseInputAsync(CancellationToken token) + { + InstanceContext[] instances = ToArray(); + for (int index = 0; index < instances.Length; index++) + await instances[index].CloseInputAsync(token); + } + + private static async void ContinueCloseInstanceContext(Task result) + { + try + { + await result; + } + catch (ObjectDisposedException e) + { + DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); + } + catch (InvalidOperationException e) + { + DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); + } + catch (CommunicationException e) + { + DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); + } + catch (TimeoutException e) + { + //if (TD.CloseTimeoutIsEnabled()) + //{ + // TD.CloseTimeout(e.Message); + //} + DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); + } + } + + void GrowItems() + { + Item[] existingItems = items; + if (existingItems != null) + { + InitItems(existingItems.Length * 2); + for (int i = 1; i < existingItems.Length; i++) + AddItem(existingItems[i].instanceContext); + } + else + { + InitItems(4); + } + } + + void InitItems(int count) + { + items = new Item[count]; + for (int i = count - 2; i > 0; i--) + { + items[i].nextFreeIndex = i + 1; + } + firstFreeIndex = 1; + } + + protected override void OnAbort() + { + InstanceContext[] instances = ToArray(); + for (int index = 0; index < instances.Length; index++) + { + instances[index].Abort(); + } + + base.OnAbort(); + } + + protected override async Task OnCloseAsync(CancellationToken token) + { + await CloseInitiateAsync(token); + await base.OnCloseAsync(token); + } + + public bool Remove(InstanceContext instanceContext) + { + if (instanceContext == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(instanceContext)); + + lock (ThisLock) + { + int index = instanceContext.InstanceContextManagerIndex; + if (index == 0) + return false; + instanceContext.InstanceContextManagerIndex = 0; + items[index].nextFreeIndex = firstFreeIndex; + items[index].instanceContext = null; + firstFreeIndex = index; + } + + base.DecrementBusyCount(); + return true; + } + + public InstanceContext[] ToArray() + { + if (items == null) + { + return Array.Empty(); + } + + lock (ThisLock) + { + int count = 0; + for (int i = 1; i < items.Length; i++) + if (items[i].instanceContext != null) + count++; + + if (count == 0) + return Array.Empty(); + + InstanceContext[] array = new InstanceContext[count]; + count = 0; + for (int i = 1; i < items.Length; i++) + { + InstanceContext instanceContext = items[i].instanceContext; + if (instanceContext != null) + { + array[count++] = instanceContext; + } + } + + return array; + } + } + + struct Item + { + public int nextFreeIndex; + public InstanceContext instanceContext; + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/InvokerUtil.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/InvokerUtil.cs new file mode 100644 index 000000000..ea27106e2 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/InvokerUtil.cs @@ -0,0 +1,158 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Security; + +namespace CoreWCF.Dispatcher +{ + delegate object InvokeDelegate(object target, object[] inputs, object[] outputs); + delegate IAsyncResult InvokeBeginDelegate(object target, object[] inputs, AsyncCallback asyncCallback, object state); + delegate object InvokeEndDelegate(object target, object[] outputs, IAsyncResult result); + delegate object CreateInstanceDelegate(); + + internal sealed class InvokerUtil + { + private CriticalHelper helper; + + public InvokerUtil() + { + helper = new CriticalHelper(); + } + + internal CreateInstanceDelegate GenerateCreateInstanceDelegate(Type type, ConstructorInfo constructor) + { + return helper.GenerateCreateInstanceDelegate(type, constructor); + } + + internal InvokeDelegate GenerateInvokeDelegate(MethodInfo method, out int inputParameterCount, + out int outputParameterCount) + { + return helper.GenerateInvokeDelegate(method, out inputParameterCount, out outputParameterCount); + } + + //internal InvokeBeginDelegate GenerateInvokeBeginDelegate(MethodInfo method, out int inputParameterCount) + //{ + // return helper.GenerateInvokeBeginDelegate(method, out inputParameterCount); + //} + + //internal InvokeEndDelegate GenerateInvokeEndDelegate(MethodInfo method, out int outputParameterCount) + //{ + // return helper.GenerateInvokeEndDelegate(method, out outputParameterCount); + //} + + private class CriticalHelper + { + internal CreateInstanceDelegate GenerateCreateInstanceDelegate(Type type, ConstructorInfo constructor) + { + + if (type.GetTypeInfo().IsValueType) + { + MethodInfo method = typeof(CriticalHelper).GetMethod(nameof(CreateInstanceOfStruct), + BindingFlags.NonPublic | BindingFlags.Static); + MethodInfo generic = method.MakeGenericMethod(type); + return (CreateInstanceDelegate)generic.CreateDelegate(typeof(CreateInstanceDelegate)); + } + else + { + MethodInfo method = typeof(CriticalHelper).GetMethod(nameof(CreateInstanceOfClass), + BindingFlags.NonPublic | BindingFlags.Static); + MethodInfo generic = method.MakeGenericMethod(type); + return (CreateInstanceDelegate)generic.CreateDelegate(typeof(CreateInstanceDelegate)); + } + } + + internal static object CreateInstanceOfClass() where T : class, new() + { + return new T(); + } + + internal static object CreateInstanceOfStruct() where T : struct + { + return default(T); + } + + internal InvokeDelegate GenerateInvokeDelegate(MethodInfo method, out int inputParameterCount, out int outputParameterCount) + { + ParameterInfo[] parameters = method.GetParameters(); + bool returnsValue = method.ReturnType != typeof(void); + var inputCount = parameters.Length; + inputParameterCount = inputCount; + + var outputParamPositions = new List(); + for (int i = 0; i < inputParameterCount; i++) + { + if (parameters[i].ParameterType.IsByRef) + { + outputParamPositions.Add(i); + } + } + + var outputPos = outputParamPositions.ToArray(); + outputParameterCount = outputPos.Length; + + InvokeDelegate lambda = delegate (object target, object[] inputs, object[] outputs) + { + object[] inputsLocal = null; + if (inputCount > 0) + { + inputsLocal = new object[inputCount]; + for (var i = 0; i < inputCount; i++) + { + inputsLocal[i] = inputs[i]; + } + } + object result = null; + if (returnsValue) + { + result = method.Invoke(target, inputsLocal); + } + else + { + method.Invoke(target, inputsLocal); + } + for (var i = 0; i < outputPos.Length; i++) + { + outputs[i] = inputs[outputPos[i]]; + } + + return result; + }; + + return lambda; + } + + //public InvokeBeginDelegate GenerateInvokeBeginDelegate(MethodInfo method, out int inputParameterCount) + //{ + // ParameterInfo[] parameters = method.GetParameters(); + // var inputCount = parameters.Length; + // inputParameterCount = inputCount; + + // InvokeBeginDelegate lambda = + // delegate(object target, object[] inputs, AsyncCallback callback, object state) + // { + // object[] inputsLocal = new object[inputCount]; + // for (var i = 0; i < inputCount; i++) + // { + // inputsLocal[i] = inputs[i]; + // } + // inputsLocal[inputCount] = callback; + // inputsLocal[inputCount + 1] = state; + // object result = method.Invoke(target, inputs); + // return result as IAsyncResult; + // }; + + // return lambda; + //} + + //public InvokeEndDelegate GenerateInvokeEndDelegate(MethodInfo method, out int outParameterCount) + //{ + + // InvokeEndDelegate lambda = + // delegate(object target, object[] outputs, IAsyncResult result) + // { + + // } + //} + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ListenerBinder.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ListenerBinder.cs new file mode 100644 index 000000000..fd0bcf20b --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ListenerBinder.cs @@ -0,0 +1,233 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using CoreWCF.Channels; + +namespace CoreWCF.Dispatcher +{ + static class ListenerBinder + { + internal static IListenerBinder GetBinder(IChannelListener listener, MessageVersion messageVersion) + { + IChannelListener input = listener as IChannelListener; + if (input != null) + return new InputListenerBinder(input, messageVersion); + + IChannelListener inputSession = listener as IChannelListener; + if (inputSession != null) + return new InputSessionListenerBinder(inputSession, messageVersion); + + IChannelListener reply = listener as IChannelListener; + if (reply != null) + return new ReplyListenerBinder(reply, messageVersion); + + IChannelListener replySession = listener as IChannelListener; + if (replySession != null) + return new ReplySessionListenerBinder(replySession, messageVersion); + + IChannelListener duplex = listener as IChannelListener; + if (duplex != null) + return new DuplexListenerBinder(duplex, messageVersion); + + IChannelListener duplexSession = listener as IChannelListener; + if (duplexSession != null) + return new DuplexSessionListenerBinder(duplexSession, messageVersion); + + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.UnknownListenerType1, listener.Uri.AbsoluteUri))); + } + + // ------------------------------------------------------------------------------------------------------------ + // Listener Binders + + class DuplexListenerBinder : IListenerBinder + { + IRequestReplyCorrelator correlator; + IChannelListener listener; + MessageVersion messageVersion; + + internal DuplexListenerBinder(IChannelListener listener, MessageVersion messageVersion) + { + correlator = new RequestReplyCorrelator(); + this.listener = listener; + this.messageVersion = messageVersion; + } + + public IChannelListener Listener + { + get { return listener; } + } + + public MessageVersion MessageVersion + { + get { return messageVersion; } + } + + public async Task AcceptAsync(CancellationToken token) + { + IDuplexChannel channel = await listener.AcceptChannelAsync(token); + if (channel == null) + return null; + return null; + //return new DuplexChannelBinder(channel, correlator, listener.Uri); + + } + } + + class DuplexSessionListenerBinder : IListenerBinder + { + IRequestReplyCorrelator correlator; + IChannelListener listener; + MessageVersion messageVersion; + + internal DuplexSessionListenerBinder(IChannelListener listener, MessageVersion messageVersion) + { + correlator = new RequestReplyCorrelator(); + this.listener = listener; + this.messageVersion = messageVersion; + } + + public IChannelListener Listener + { + get { return listener; } + } + + public MessageVersion MessageVersion + { + get { return messageVersion; } + } + + public async Task AcceptAsync(CancellationToken token) + { + IDuplexSessionChannel channel = await listener.AcceptChannelAsync(token); + if (channel == null) + return null; + return null; + //return new DuplexChannelBinder(channel, correlator, listener.Uri); + } + } + + class InputListenerBinder : IListenerBinder + { + IChannelListener listener; + MessageVersion messageVersion; + + internal InputListenerBinder(IChannelListener listener, MessageVersion messageVersion) + { + this.listener = listener; + this.messageVersion = messageVersion; + } + + public IChannelListener Listener + { + get { return listener; } + } + + public MessageVersion MessageVersion + { + get { return messageVersion; } + } + + public async Task AcceptAsync(CancellationToken token) + { + IInputChannel channel = await listener.AcceptChannelAsync(token); + if (channel == null) + return null; + + return new InputChannelBinder(channel, listener.Uri); + } + } + + class InputSessionListenerBinder : IListenerBinder + { + IChannelListener listener; + MessageVersion messageVersion; + + internal InputSessionListenerBinder(IChannelListener listener, MessageVersion messageVersion) + { + this.listener = listener; + this.messageVersion = messageVersion; + } + + public IChannelListener Listener + { + get { return listener; } + } + + public MessageVersion MessageVersion + { + get { return messageVersion; } + } + + public async Task AcceptAsync(CancellationToken token) + { + IInputSessionChannel channel = await listener.AcceptChannelAsync(token); + if (channel == null) + return null; + + return new InputChannelBinder(channel, listener.Uri); + } + } + + class ReplyListenerBinder : IListenerBinder + { + IChannelListener listener; + MessageVersion messageVersion; + + internal ReplyListenerBinder(IChannelListener listener, MessageVersion messageVersion) + { + this.listener = listener; + this.messageVersion = messageVersion; + } + + public IChannelListener Listener + { + get { return listener; } + } + + public MessageVersion MessageVersion + { + get { return messageVersion; } + } + + public async Task AcceptAsync(CancellationToken token) + { + IReplyChannel channel = await listener.AcceptChannelAsync(token); + if (channel == null) + return null; + return null; + //return new ReplyChannelBinder(channel, listener.Uri); + } + } + + class ReplySessionListenerBinder : IListenerBinder + { + IChannelListener listener; + MessageVersion messageVersion; + + internal ReplySessionListenerBinder(IChannelListener listener, MessageVersion messageVersion) + { + this.listener = listener; + this.messageVersion = messageVersion; + } + + public IChannelListener Listener + { + get { return listener; } + } + + public MessageVersion MessageVersion + { + get { return messageVersion; } + } + + public async Task AcceptAsync(CancellationToken token) + { + IReplySessionChannel channel = await listener.AcceptChannelAsync(token); + if (channel == null) + return null; + return null; + //return new ReplyChannelBinder(channel, listener.Uri); + } + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ListenerHandler.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ListenerHandler.cs new file mode 100644 index 000000000..423ab0864 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ListenerHandler.cs @@ -0,0 +1,448 @@ +using System; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; +using CoreWCF.Runtime; +using CoreWCF.Channels; +using SessionIdleManager = CoreWCF.Channels.ServiceChannel.SessionIdleManager; + +namespace CoreWCF.Dispatcher +{ + class ListenerHandler : CommunicationObject //, ISessionThrottleNotification + { + readonly ErrorHandlingAcceptor acceptor; + readonly ChannelDispatcher channelDispatcher; + ListenerChannel channel; + SessionIdleManager idleManager; + bool acceptedNull; + bool doneAccepting; + EndpointDispatcherTable endpoints; + readonly ServiceHostBase host; + readonly IListenerBinder listenerBinder; + //readonly ServiceThrottle throttle; + IDefaultCommunicationTimeouts timeouts; + /*WrappedTransaction wrappedTransaction;*/ + CancellationTokenSource closingTokenSource; + + internal ListenerHandler(IListenerBinder listenerBinder, ChannelDispatcher channelDispatcher, ServiceHostBase host, /*ServiceThrottle throttle,*/ IDefaultCommunicationTimeouts timeouts) + { + this.listenerBinder = listenerBinder; + if (!((this.listenerBinder != null))) + { + Fx.Assert("ListenerHandler.ctor: (this.listenerBinder != null)"); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(listenerBinder)); + } + + this.channelDispatcher = channelDispatcher; + if (!((this.channelDispatcher != null))) + { + Fx.Assert("ListenerHandler.ctor: (this.channelDispatcher != null)"); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(channelDispatcher)); + } + + this.host = host; + if (!((this.host != null))) + { + Fx.Assert("ListenerHandler.ctor: (this.host != null)"); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(host)); + } + + //this.throttle = throttle; + //if (!((this.throttle != null))) + //{ + // Fx.Assert("ListenerHandler.ctor: (this.throttle != null)"); + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(throttle)); + //} + + this.timeouts = timeouts; + + //endpoints = channelDispatcher.EndpointDispatcherTable; + acceptor = new ErrorHandlingAcceptor(listenerBinder, channelDispatcher); + closingTokenSource = new CancellationTokenSource(); + } + + internal ChannelDispatcher ChannelDispatcher + { + get { return channelDispatcher; } + } + + internal ListenerChannel Channel + { + get { return channel; } + } + + protected override TimeSpan DefaultCloseTimeout + { + get { return host.CloseTimeout; } + } + + protected override TimeSpan DefaultOpenTimeout + { + get { return host.OpenTimeout; } + } + + internal EndpointDispatcherTable Endpoints + { + get { return endpoints; } + set { endpoints = value; } + } + + internal ServiceHostBase Host + { + get { return host; } + } + + new internal object ThisLock + { + get { return base.ThisLock; } + } + + protected override Task OnOpenAsync(CancellationToken token) + { + return Task.CompletedTask; + } + + protected override void OnOpened() + { + base.OnOpened(); + //channelDispatcher.Channels.IncrementActivityCount(); + //if (this.channelDispatcher.IsTransactedReceive && this.channelDispatcher.ReceiveContextEnabled && this.channelDispatcher.MaxTransactedBatchSize > 0) + //{ + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.IncompatibleBehaviors)); + //} + NewChannelPump(); + } + + internal void NewChannelPump() + { + using (TaskHelpers.RunTaskContinuationsOnOurThreads()) + { + ChannelPumpAsync(); + } + } + + static void InitiateChannelPump(object state) + { + ListenerHandler listenerHandler = state as ListenerHandler; + listenerHandler.ChannelPumpAsync(); + } + + async void ChannelPumpAsync() + { + IChannelListener listener = listenerBinder.Listener; + + for (;;) + { + if (acceptedNull || (listener.State == CommunicationState.Faulted)) + { + DoneAccepting(); + break; + } + + if (!await AcceptAndAcquireThrottleAsync()) + { + break; + } + + Dispatch(); + } + } + + void AbortChannels() + { + IChannel[] channels = Array.Empty();// channelDispatcher.Channels.ToArray(); + for (int index = 0; index < channels.Length; index++) + { + channels[index].Abort(); + } + } + + async Task AcceptAndAcquireThrottleAsync() + { + var result = await acceptor.TryAcceptAsync(closingTokenSource.Token); + if (result.Success) + { + var binder = result.Result; + if (binder != null) + { + channel = new ListenerChannel(binder); + } + else + { + AcceptedNull(); + channel = null; + } + } + else + { + channel = null; + } + + if (channel != null) + { + Fx.Assert(idleManager == null, "There cannot be an existing idle manager"); + //idleManager = SessionIdleManager.CreateIfNeeded(channel.Binder, channelDispatcher.DefaultCommunicationTimeouts.ReceiveTimeout); + } + else + { + DoneAccepting(); + return true; + } + + return AcquireThrottle(); + } + + bool AcquireThrottle() + { + //if ((this.channel != null) && (this.throttle != null) && (this.channelDispatcher.Session)) + //{ + // return this.throttle.AcquireSession(this); + //} + + return true; + } + + async Task CloseChannelAsync(IChannel channel, CancellationToken token) + { + try + { + if (channel.State != CommunicationState.Closing && channel.State != CommunicationState.Closed) + { + CloseChannelState state = new CloseChannelState(this, channel); + if (channel is ISessionChannel) + { + IDuplexSession duplexSession = ((ISessionChannel)channel).Session; + await duplexSession.CloseOutputSessionAsync(token); + } + else + { + await channel.CloseAsync(token); + } + } + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + HandleError(e); + + if (channel is ISessionChannel) + { + channel.Abort(); + } + } + } + + public async Task CloseInputAsync(CancellationToken token) + { + closingTokenSource.Cancel(); + //TimeoutHelper timeoutHelper = new TimeoutHelper(timeout); + // Close all datagram channels + IChannel[] channels = Array.Empty();// channelDispatcher.Channels.ToArray(); + for (int index = 0; index < channels.Length; index++) + { + IChannel channel = channels[index]; + if (!IsSessionChannel(channel)) + { + try + { + await channel.CloseAsync(token); + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + HandleError(e); + } + } + } + } + + async Task CloseChannelsAsync(CancellationToken token) + { + IChannel[] channels = Array.Empty();// channelDispatcher.Channels.ToArray(); + Task[] tasks = new Task[channels.Length]; + for (int index = 0; index < channels.Length; index++) + tasks[index] = CloseChannelAsync(channels[index], token); + await Task.WhenAll(tasks); + } + + void Dispatch() + { + ListenerChannel channel = this.channel; + SessionIdleManager idleManager = this.idleManager; + this.channel = null; + this.idleManager = null; + + try + { + if (channel != null) + { + ChannelHandler handler = new ChannelHandler(listenerBinder.MessageVersion, channel.Binder, /*this.throttle,*/ this, /*(channel.Throttle != null),*/ /*this.wrappedTransaction,*/ idleManager); + + if (!channel.Binder.HasSession) + { + //channelDispatcher.Channels.Add(channel.Binder.Channel); + } + + if (channel.Binder is DuplexChannelBinder) + { + DuplexChannelBinder duplexChannelBinder = channel.Binder as DuplexChannelBinder; + duplexChannelBinder.ChannelHandler = handler; + duplexChannelBinder.DefaultCloseTimeout = DefaultCloseTimeout; + + if (timeouts == null) + duplexChannelBinder.DefaultSendTimeout = ServiceDefaults.SendTimeout; + else + duplexChannelBinder.DefaultSendTimeout = timeouts.SendTimeout; + } + + ChannelHandler.Register(handler); + channel = null; + idleManager = null; + } + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + HandleError(e); + } + finally + { + if (channel != null) + { + channel.Binder.Channel.Abort(); + //if (this.throttle != null && this.channelDispatcher.Session) + //{ + // this.throttle.DeactivateChannel(); + //} + if (idleManager != null) + { + idleManager.CancelTimer(); + } + } + } + } + + void AcceptedNull() + { + acceptedNull = true; + } + + void DoneAccepting() + { + lock (ThisLock) + { + if (!doneAccepting) + { + doneAccepting = true; + //channelDispatcher.Channels.DecrementActivityCount(); + } + } + } + + bool IsSessionChannel(IChannel channel) + { + return (channel is ISessionChannel || + channel is ISessionChannel || + channel is ISessionChannel); + } + + void CancelPendingIdleManager() + { + SessionIdleManager idleManager = this.idleManager; + if (idleManager != null) + { + idleManager.CancelTimer(); + } + } + + protected override void OnAbort() + { + // if there's an idle manager that has not been transferred to the channel handler, cancel it + CancelPendingIdleManager(); + + // Start aborting incoming channels + //channelDispatcher.Channels.CloseInput(); + + // Abort existing channels + AbortChannels(); + + // Wait for channels to finish aborting + //channelDispatcher.Channels.Abort(); + + } + + protected override async Task OnCloseAsync(CancellationToken token) + { + // if there's an idle manager that has not been cancelled, cancel it + CancelPendingIdleManager(); + + // Start aborting incoming channels + //channelDispatcher.Channels.CloseInput(); + + // Start closing existing channels + await CloseChannelsAsync(token); + + // Wait for channels to finish closing + //await channelDispatcher.Channels.CloseAsync(token); + } + + bool HandleError(Exception e) + { + return channelDispatcher.HandleError(e); + } + + class CloseChannelState + { + ListenerHandler listenerHandler; + IChannel channel; + + internal CloseChannelState(ListenerHandler listenerHandler, IChannel channel) + { + this.listenerHandler = listenerHandler; + this.channel = channel; + } + + internal ListenerHandler ListenerHandler + { + get { return listenerHandler; } + } + + internal IChannel Channel + { + get { return channel; } + } + } + } + + class ListenerChannel + { + IChannelBinder binder; + //ServiceThrottle throttle; + + public ListenerChannel(IChannelBinder binder) + { + this.binder = binder; + } + + public IChannelBinder Binder + { + get { return binder; } + } + + //public ServiceThrottle Throttle + //{ + // get { return this.throttle; } + // set { this.throttle = value; } + //} + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/MatchAllMessageFilter.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/MatchAllMessageFilter.cs new file mode 100644 index 000000000..4a2699a4c --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/MatchAllMessageFilter.cs @@ -0,0 +1,33 @@ +using System.Runtime.Serialization; +using CoreWCF.Channels; + +namespace CoreWCF.Dispatcher +{ + [DataContract] + internal class MatchAllMessageFilter : MessageFilter + { + public MatchAllMessageFilter() + : base() + { + } + + public override bool Match(MessageBuffer messageBuffer) + { + if (messageBuffer == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("messageBuffer"); + } + return true; + } + + public override bool Match(Message message) + { + if (message == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("message"); + } + return true; + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/MatchNoneMessageFilter.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/MatchNoneMessageFilter.cs new file mode 100644 index 000000000..1643d8dd0 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/MatchNoneMessageFilter.cs @@ -0,0 +1,32 @@ +using System.Runtime.Serialization; +using CoreWCF.Channels; + +namespace CoreWCF.Dispatcher +{ + [DataContract] + internal class MatchNoneMessageFilter : MessageFilter + { + public MatchNoneMessageFilter() + : base() + { + } + + public override bool Match(MessageBuffer buffer) + { + if (buffer == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(buffer)); + } + return false; + } + + public override bool Match(Message message) + { + if (message == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(message)); + } + return false; + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/MessageFilter.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/MessageFilter.cs new file mode 100644 index 000000000..be6bc8d38 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/MessageFilter.cs @@ -0,0 +1,311 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Runtime.Serialization; +using CoreWCF.Runtime; +using CoreWCF.Channels; +using CoreWCF.Diagnostics; + +namespace CoreWCF.Dispatcher +{ + [DataContract] + //[KnownType(typeof(XPathMessageFilter))] + //[KnownType(typeof(ActionMessageFilter))] + //[KnownType(typeof(MatchAllMessageFilter))] + //[KnownType(typeof(MatchNoneMessageFilter))] + internal abstract class MessageFilter + { + protected MessageFilter() + { + } + + protected internal virtual IMessageFilterTable CreateFilterTable() + { + return null; + } + + /// + /// Tests whether the filter matches the given message. + /// + public abstract bool Match(MessageBuffer buffer); + + /// + /// Tests whether the filter matches the given message without examining its body. + /// Note: since this method never probes the message body, it should NOT close the message + /// If the filter probes the message body, then the filter must THROW an Exception. The filter should not return false + /// This is deliberate - we don't want to produce false positives. + /// + public abstract bool Match(Message message); + } + + internal class SequentialMessageFilterTable : IMessageFilterTable + { + Dictionary filters; + + public SequentialMessageFilterTable() + { + filters = new Dictionary(); + } + + // + // IMessageFilterTable methods + // + + public int Count + { + get + { + return filters.Count; + } + } + + public void Clear() + { + filters.Clear(); + } + + public bool GetMatchingValue(Message message, out FilterData data) + { + bool dataSet = false; + MessageFilter filter = null; + data = default(FilterData); + foreach (KeyValuePair item in filters) + { + if (item.Key.Match(message)) + { + if (dataSet) + { + Collection f = new Collection(); + f.Add(filter); + f.Add(item.Key); + throw TraceUtility.ThrowHelperError(new MultipleFilterMatchesException(SR.FilterMultipleMatches, null, f), message); + } + + filter = item.Key; + data = item.Value; + dataSet = true; + } + } + + return dataSet; + } + + public bool GetMatchingValue(MessageBuffer buffer, out FilterData data) + { + bool dataSet = false; + MessageFilter filter = null; + data = default(FilterData); + foreach (KeyValuePair item in filters) + { + if (item.Key.Match(buffer)) + { + if (dataSet) + { + Collection f = new Collection(); + f.Add(filter); + f.Add(item.Key); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MultipleFilterMatchesException(SR.FilterMultipleMatches, null, f)); + } + + filter = item.Key; + data = item.Value; + dataSet = true; + } + } + + return dataSet; + } + + public bool GetMatchingValues(Message message, ICollection results) + { + int count = results.Count; + foreach (KeyValuePair item in filters) + { + if (item.Key.Match(message)) + { + results.Add(item.Value); + } + } + return count != results.Count; + } + + public bool GetMatchingValues(MessageBuffer buffer, ICollection results) + { + int count = results.Count; + foreach (KeyValuePair item in filters) + { + if (item.Key.Match(buffer)) + { + results.Add(item.Value); + } + } + return count != results.Count; + } + + public bool GetMatchingFilter(Message message, out MessageFilter filter) + { + filter = null; + foreach (KeyValuePair item in filters) + { + if (item.Key.Match(message)) + { + if (filter != null) + { + Collection f = new Collection(); + f.Add(filter); + f.Add(item.Key); + throw TraceUtility.ThrowHelperError(new MultipleFilterMatchesException(SR.FilterMultipleMatches, null, f), message); + } + + filter = item.Key; + } + } + + return filter != null; + } + + public bool GetMatchingFilter(MessageBuffer buffer, out MessageFilter filter) + { + filter = null; + foreach (KeyValuePair item in filters) + { + if (item.Key.Match(buffer)) + { + if (filter != null) + { + Collection f = new Collection(); + f.Add(filter); + f.Add(item.Key); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MultipleFilterMatchesException(SR.FilterMultipleMatches, null, f)); + } + + filter = item.Key; + } + } + + return filter != null; + } + + public bool GetMatchingFilters(Message message, ICollection results) + { + int count = results.Count; + foreach (KeyValuePair item in filters) + { + if (item.Key.Match(message)) + { + results.Add(item.Key); + } + } + return count != results.Count; + } + + public bool GetMatchingFilters(MessageBuffer buffer, ICollection results) + { + int count = results.Count; + foreach (KeyValuePair item in filters) + { + if (item.Key.Match(buffer)) + { + results.Add(item.Key); + } + } + return count != results.Count; + } + + // + // IDictionary methods + // + + public FilterData this[MessageFilter key] + { + get + { + return filters[key]; + } + set + { + filters[key] = value; + } + } + + public ICollection Keys + { + get + { + return filters.Keys; + } + } + + public ICollection Values + { + get + { + return filters.Values; + } + } + + public bool ContainsKey(MessageFilter key) + { + return filters.ContainsKey(key); + } + + public void Add(MessageFilter key, FilterData value) + { + filters.Add(key, value); + } + + public bool Remove(MessageFilter key) + { + return filters.Remove(key); + } + + // + // ICollection> methods + // + + bool ICollection>.IsReadOnly + { + get + { + return false; + } + } + + void ICollection>.Add(KeyValuePair item) + { + ((ICollection>)filters).Add(item); + } + + bool ICollection>.Contains(KeyValuePair item) + { + return ((ICollection>)filters).Contains(item); + } + + void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex) + { + ((ICollection>)filters).CopyTo(array, arrayIndex); + } + + bool ICollection>.Remove(KeyValuePair item) + { + return ((ICollection>)filters).Remove(item); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return ((IEnumerable>)this).GetEnumerator(); + } + + IEnumerator> IEnumerable>.GetEnumerator() + { + return ((ICollection>)filters).GetEnumerator(); + } + + public bool TryGetValue(MessageFilter filter, out FilterData data) + { + return filters.TryGetValue(filter, out data); + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/MessageFilterTable.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/MessageFilterTable.cs new file mode 100644 index 000000000..187ba0b23 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/MessageFilterTable.cs @@ -0,0 +1,657 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Reflection; +using System.Runtime.Serialization; +using CoreWCF.Channels; +using CoreWCF.Diagnostics; + +namespace CoreWCF.Dispatcher +{ + internal class MessageFilterTable : IMessageFilterTable + { + Dictionary filterTypeMappings; + Dictionary filters; + SortedBuffer tables; + int defaultPriority; + + static readonly TableEntryComparer staticComparerInstance = new TableEntryComparer(); + + public MessageFilterTable() + : this(0) + { + } + + public MessageFilterTable(int defaultPriority) + { + Init(defaultPriority); + } + + [OnDeserializing] + private void OnDeserializing(StreamingContext context) + { + Init(0); + } + + void Init(int defaultPriority) + { + CreateEmptyTables(); + this.defaultPriority = defaultPriority; + } + + public TFilterData this[MessageFilter filter] + { + get + { + return filters[filter]; + } + set + { + if (ContainsKey(filter)) + { + int p = GetPriority(filter); + Remove(filter); + Add(filter, value, p); + } + else + { + Add(filter, value, defaultPriority); + } + } + } + + public int Count + { + get + { + return filters.Count; + } + } + + [DataMember] + public int DefaultPriority + { + get + { + return defaultPriority; + } + set + { + defaultPriority = value; + } + } + + [DataMember] + Entry[] Entries + { + get + { + Entry[] entries = new Entry[Count]; + int i = 0; + foreach (KeyValuePair item in filters) + { + entries[i++] = new Entry(item.Key, item.Value, GetPriority(item.Key)); + } + return entries; + } + set + { + for (int i = 0; i < value.Length; ++i) + { + Entry e = value[i]; + Add(e.filter, e.data, e.priority); + } + } + } + + public bool IsReadOnly + { + get + { + return false; + } + } + + public ICollection Keys + { + get + { + return filters.Keys; + } + } + + public ICollection Values + { + get + { + return filters.Values; + } + } + + public void Add(MessageFilter filter, TFilterData data) + { + Add(filter, data, defaultPriority); + } + + public void Add(MessageFilter filter, TFilterData data, int priority) + { + if (filter == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("filter"); + } + + if (filters.ContainsKey(filter)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("filter", SR.FilterExists); + } + + Type filterType = filter.GetType(); + Type tableType = null; + IMessageFilterTable table = null; + + if (filterTypeMappings.TryGetValue(filterType, out tableType)) + { + for (int i = 0; i < tables.Count; ++i) + { + if (tables[i].priority == priority && tables[i].table.GetType().Equals(tableType)) + { + table = tables[i].table; + break; + } + } + if (table == null) + { + table = CreateFilterTable(filter); + ValidateTable(table); + if (!table.GetType().Equals(tableType)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.FilterTableTypeMismatch)); + } + table.Add(filter, data); + tables.Add(new FilterTableEntry(priority, table)); + } + else + { + table.Add(filter, data); + } + } + else + { + table = CreateFilterTable(filter); + ValidateTable(table); + filterTypeMappings.Add(filterType, table.GetType()); + + FilterTableEntry entry = new FilterTableEntry(priority, table); + int idx = tables.IndexOf(entry); + if (idx >= 0) + { + table = tables[idx].table; + } + else + { + tables.Add(entry); + } + + table.Add(filter, data); + } + + filters.Add(filter, data); + } + + public void Add(KeyValuePair item) + { + Add(item.Key, item.Value); + } + + public void Clear() + { + filters.Clear(); + tables.Clear(); + } + + public bool Contains(KeyValuePair item) + { + return ((ICollection>)filters).Contains(item); + } + + public bool ContainsKey(MessageFilter filter) + { + return filters.ContainsKey(filter); + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + ((ICollection>)filters).CopyTo(array, arrayIndex); + } + + void CreateEmptyTables() + { + filterTypeMappings = new Dictionary(); + filters = new Dictionary(); + tables = new SortedBuffer(staticComparerInstance); + } + + protected virtual IMessageFilterTable CreateFilterTable(MessageFilter filter) + { + IMessageFilterTable ft = filter.CreateFilterTable(); + + if (ft == null) + return new SequentialMessageFilterTable(); + + return ft; + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public IEnumerator> GetEnumerator() + { + return ((ICollection>)filters).GetEnumerator(); + } + + public int GetPriority(MessageFilter filter) + { + TFilterData d = filters[filter]; + for (int i = 0; i < tables.Count; ++i) + { + if (tables[i].table.ContainsKey(filter)) + { + return tables[i].priority; + } + } + + + throw DiagnosticUtility.ExceptionUtility.ThrowHelperCritical(new InvalidOperationException(SR.FilterTableInvalidForLookup)); + } + + public bool GetMatchingValue(Message message, out TFilterData data) + { + bool dataSet = false; + int pri = int.MinValue; + + data = default(TFilterData); + for (int i = 0; i < tables.Count; ++i) + { + // Watch for the end of a bucket + if (pri > tables[i].priority && dataSet) + { + break; + } + pri = tables[i].priority; + + TFilterData currentData; + + if (tables[i].table.GetMatchingValue(message, out currentData)) + { + if (dataSet) + { + throw TraceUtility.ThrowHelperError(new MultipleFilterMatchesException(SR.FilterMultipleMatches, null, null), message); + } + + data = currentData; + dataSet = true; + } + } + + return dataSet; + } + + internal bool GetMatchingValue(Message message, out TFilterData data, out bool addressMatched) + { + bool dataSet = false; + int pri = int.MinValue; + data = default(TFilterData); + addressMatched = false; + for (int i = 0; i < tables.Count; ++i) + { + // Watch for the end of a bucket + if (pri > tables[i].priority && dataSet) + { + break; + } + pri = tables[i].priority; + + bool matchResult; + TFilterData currentData; + IMessageFilterTable table = tables[i].table; + AndMessageFilterTable andTable = table as AndMessageFilterTable; + if (andTable != null) + { + bool addressResult; + matchResult = andTable.GetMatchingValue(message, out currentData, out addressResult); + addressMatched |= addressResult; + } + else + { + matchResult = table.GetMatchingValue(message, out currentData); + } + + if (matchResult) + { + if (dataSet) + { + throw TraceUtility.ThrowHelperError(new MultipleFilterMatchesException(SR.FilterMultipleMatches, null, null), message); + } + + addressMatched = true; + data = currentData; + dataSet = true; + } + } + + return dataSet; + } + + public bool GetMatchingValue(MessageBuffer buffer, out TFilterData data) + { + return GetMatchingValue(buffer, null, out data); + } + + // this optimization is only for CorrelationActionMessageFilter and ActionMessageFilter if they override CreateFilterTable to return ActionMessageFilterTable + internal bool GetMatchingValue(MessageBuffer buffer, Message messageToReadHeaders, out TFilterData data) + { + bool dataSet = false; + int pri = int.MinValue; + data = default(TFilterData); + for (int i = 0; i < tables.Count; ++i) + { + // Watch for the end of a bucket + if (pri > tables[i].priority && dataSet) + { + break; + } + pri = tables[i].priority; + + TFilterData currentData; + bool result = false; + if (messageToReadHeaders != null && tables[i].table is ActionMessageFilterTable) + { + // this is an action message, in this case we can pass in the message itself since the filter will only read from the header + result = tables[i].table.GetMatchingValue(messageToReadHeaders, out currentData); + } + else + { + // this is a custom filter that might read from the message body, pass in the message buffer itself in this case + result = tables[i].table.GetMatchingValue(buffer, out currentData); + } + if (result) + { + if (dataSet) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MultipleFilterMatchesException(SR.FilterMultipleMatches, null, null)); + } + + data = currentData; + dataSet = true; + } + } + + return dataSet; + } + + public bool GetMatchingValues(Message message, ICollection results) + { + if (results == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("results"); + } + int count = results.Count; + int pri = int.MinValue; + for (int i = 0; i < tables.Count; ++i) + { + // Watch for the end of a bucket + if (pri > tables[i].priority && count != results.Count) + { + break; + } + pri = tables[i].priority; + tables[i].table.GetMatchingValues(message, results); + } + + return count != results.Count; + } + + public bool GetMatchingValues(MessageBuffer buffer, ICollection results) + { + if (results == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("results"); + } + int count = results.Count; + int pri = int.MinValue; + for (int i = 0; i < tables.Count; ++i) + { + // Watch for the end of a bucket + if (pri > tables[i].priority && count != results.Count) + { + break; + } + pri = tables[i].priority; + tables[i].table.GetMatchingValues(buffer, results); + } + + return count != results.Count; + } + + public bool GetMatchingFilter(Message message, out MessageFilter filter) + { + MessageFilter f; + int pri = int.MinValue; + filter = null; + for (int i = 0; i < tables.Count; ++i) + { + // Watch for the end of a bucket + if (pri > tables[i].priority && filter != null) + { + break; + } + pri = tables[i].priority; + + if (tables[i].table.GetMatchingFilter(message, out f)) + { + if (filter == null) + { + filter = f; + } + else + { + Collection c = new Collection(); + c.Add(filter); + c.Add(f); + throw TraceUtility.ThrowHelperError(new MultipleFilterMatchesException(SR.FilterMultipleMatches, null, c), message); + } + } + } + + return filter != null; + } + + public bool GetMatchingFilter(MessageBuffer buffer, out MessageFilter filter) + { + MessageFilter f; + int pri = int.MinValue; + filter = null; + for (int i = 0; i < tables.Count; ++i) + { + // Watch for the end of a bucket + if (pri > tables[i].priority && filter != null) + { + break; + } + pri = tables[i].priority; + + if (tables[i].table.GetMatchingFilter(buffer, out f)) + { + if (filter == null) + { + filter = f; + } + else + { + Collection c = new Collection(); + c.Add(filter); + c.Add(f); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MultipleFilterMatchesException(SR.FilterMultipleMatches, null, c)); + } + } + } + + return filter != null; + } + + public bool GetMatchingFilters(Message message, ICollection results) + { + if (results == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("results"); + } + int count = results.Count; + int pri = int.MinValue; + for (int i = 0; i < tables.Count; ++i) + { + // Watch for the end of a bucket + if (pri > tables[i].priority && count != results.Count) + { + break; + } + pri = tables[i].priority; + tables[i].table.GetMatchingFilters(message, results); + } + + return count != results.Count; + } + + public bool GetMatchingFilters(MessageBuffer buffer, ICollection results) + { + if (results == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("results"); + } + int count = results.Count; + int pri = int.MinValue; + for (int i = 0; i < tables.Count; ++i) + { + // Watch for the end of a bucket + if (pri > tables[i].priority && count != results.Count) + { + break; + } + pri = tables[i].priority; + tables[i].table.GetMatchingFilters(buffer, results); + } + + return count != results.Count; + } + + public bool Remove(MessageFilter filter) + { + for (int i = 0; i < tables.Count; ++i) + { + if (tables[i].table.Remove(filter)) + { + if (tables[i].table.Count == 0) + { + tables.RemoveAt(i); + } + return filters.Remove(filter); + } + } + return false; + } + + public bool Remove(KeyValuePair item) + { + if (((ICollection>)filters).Contains(item)) + { + return Remove(item.Key); + } + return false; + } + + public bool TryGetValue(MessageFilter filter, out TFilterData data) + { + return filters.TryGetValue(filter, out data); + } + + void ValidateTable(IMessageFilterTable table) + { + Type t = GetType(); + if (t.IsInstanceOfType(table)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.FilterBadTableType)); + } + } + + /////////////////////////////////////////////////// + + struct FilterTableEntry + { + internal IMessageFilterTable table; + internal int priority; + + internal FilterTableEntry(int pri, IMessageFilterTable t) + { + priority = pri; + table = t; + } + } + + class TableEntryComparer : IComparer + { + public TableEntryComparer() { } + + public int Compare(FilterTableEntry x, FilterTableEntry y) + { + // Highest priority first + int p = y.priority.CompareTo(x.priority); + if (p != 0) + { + return p; + } + + return x.table.GetType().FullName.CompareTo(y.table.GetType().FullName); + } + + public bool Equals(FilterTableEntry x, FilterTableEntry y) + { + // Highest priority first + int p = y.priority.CompareTo(x.priority); + if (p != 0) + { + return false; + } + + return x.table.GetType().FullName.Equals(y.table.GetType().FullName); + } + + public int GetHashCode(FilterTableEntry table) + { + return table.GetHashCode(); + } + } + + [DataContract] + class Entry + { + [DataMember(IsRequired = true)] + internal MessageFilter filter; + + [DataMember(IsRequired = true)] + internal TFilterData data; + + [DataMember(IsRequired = true)] + internal int priority; + + internal Entry(MessageFilter f, TFilterData d, int p) + { + filter = f; + data = d; + priority = p; + } + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/MessageOperationFormatter.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/MessageOperationFormatter.cs new file mode 100644 index 000000000..f885fe417 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/MessageOperationFormatter.cs @@ -0,0 +1,75 @@ +using System; +using CoreWCF.Channels; +using CoreWCF.Diagnostics; + +namespace CoreWCF.Dispatcher +{ + internal sealed class MessageOperationFormatter : IClientMessageFormatter, IDispatchMessageFormatter + { + static MessageOperationFormatter instance; + + internal static MessageOperationFormatter Instance + { + get + { + if (instance == null) + instance = new MessageOperationFormatter(); + return instance; + } + } + + public object DeserializeReply(Message message, object[] parameters) + { + if (message == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(message)); + if (parameters != null && parameters.Length > 0) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.SFxParametersMustBeEmpty); + + return message; + } + + public void DeserializeRequest(Message message, object[] parameters) + { + if (message == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(message)); + if (parameters == null) + throw TraceUtility.ThrowHelperError(new ArgumentNullException(nameof(parameters)), message); + if (parameters.Length != 1) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.SFxParameterMustBeArrayOfOneElement); + + parameters[0] = message; + } + + public bool IsFault(string operation, Exception error) + { + return false; + } + + public MessageFault SerializeFault(Exception error) + { + + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.SFxMessageOperationFormatterCannotSerializeFault)); + } + + public Message SerializeReply(MessageVersion messageVersion, object[] parameters, object result) + { + if (!(result is Message)) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.SFxResultMustBeMessage); + if (parameters != null && parameters.Length > 0) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.SFxParametersMustBeEmpty); + + return (Message)result; + } + + public Message SerializeRequest(MessageVersion messageVersion, object[] parameters) + { + if (parameters == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(parameters)); + if (parameters.Length != 1 || !(parameters[0] is Message)) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.SFxParameterMustBeMessage); + + return (Message)parameters[0]; + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/MessageRpc.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/MessageRpc.cs new file mode 100644 index 000000000..097260fb6 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/MessageRpc.cs @@ -0,0 +1,576 @@ +using System; +using System.Collections.ObjectModel; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Security; +using System.Threading; +using System.Threading.Tasks; +using System.Xml; +using CoreWCF.Runtime; +using CoreWCF.Channels; +using CoreWCF.Diagnostics; +using System.Diagnostics; + +namespace CoreWCF.Dispatcher +{ + delegate Task MessageRpcProcessor(MessageRpc rpc); + delegate void MessageRpcErrorHandler(ref MessageRpc rpc); + + struct MessageRpc + { + internal readonly ServiceChannel Channel; + //internal readonly ChannelHandler channelHandler; + internal readonly object[] Correlation; + internal readonly ServiceHostBase Host; + internal readonly OperationContext OperationContext; + //internal ServiceModelActivity Activity; + internal Guid ResponseActivityId; + internal IAsyncResult AsyncResult; + internal Task TaskResult; + internal bool CanSendReply; + internal bool SuccessfullySendReply; + internal object[] InputParameters; + internal object[] OutputParameters; + internal object ReturnParameter; + internal bool ParametersDisposed; + internal bool DidDeserializeRequestBody; + //internal TransactionMessageProperty TransactionMessageProperty; + //internal TransactedBatchContext TransactedBatchContext; + internal Exception Error; + internal MessageRpcErrorHandler ErrorProcessor; + internal ErrorHandlerFaultInfo FaultInfo; + internal bool HasSecurityContext; + internal object Instance; + internal MessageRpcProcessor AsyncProcessor; + internal Collection NotUnderstoodHeaders; + internal DispatchOperationRuntime Operation; + internal Message Request; + internal RequestContext RequestContext; + internal bool RequestContextThrewOnReply; + internal UniqueId RequestID; + internal Message Reply; + internal TimeoutHelper ReplyTimeoutHelper; + internal RequestReplyCorrelator.ReplyToInfo ReplyToInfo; + internal MessageVersion RequestVersion; + //internal ServiceSecurityContext SecurityContext; + internal InstanceContext InstanceContext; + internal bool SuccessfullyBoundInstance; + internal bool SuccessfullyIncrementedActivity; + internal bool SuccessfullyLockedInstance; + //internal TransactionRpcFacet transaction; + //internal IAspNetMessageProperty HostingProperty; + //internal MessageRpcInvokeNotification InvokeNotification; + //internal EventTraceActivity EventTraceActivity; + + bool paused; + bool switchedThreads; + bool isInstanceContextSingleton; + SignalGate invokeContinueGate; + + internal MessageRpc(RequestContext requestContext, Message request, DispatchOperationRuntime operation, + ServiceChannel channel, ServiceHostBase host, bool cleanThread, + OperationContext operationContext, InstanceContext instanceContext/*, EventTraceActivity eventTraceActivity*/) + { + Fx.Assert((operationContext != null), "correwcf.Dispatcher.MessageRpc.MessageRpc(), operationContext == null"); + // TODO: ChannelHandler supplied an ErrorHandler, need to supply this some other way. + //Fx.Assert(channelHandler != null, "System.ServiceModel.Dispatcher.MessageRpc.MessageRpc(), channelHandler == null"); + + //this.Activity = null; + //this.EventTraceActivity = eventTraceActivity; + AsyncResult = null; + TaskResult = null; + CanSendReply = true; + Channel = channel; + //this.channelHandler = channelHandler; + Correlation = EmptyArray.Allocate(operation.Parent.CorrelationCount); + DidDeserializeRequestBody = false; + //this.TransactionMessageProperty = null; + //this.TransactedBatchContext = null; + Error = null; + ErrorProcessor = null; + FaultInfo = new ErrorHandlerFaultInfo(request.Version.Addressing.DefaultFaultAction); + HasSecurityContext = false; + Host = host; + Instance = null; + AsyncProcessor = null; + NotUnderstoodHeaders = null; + Operation = operation; + OperationContext = operationContext; + paused = false; + ParametersDisposed = false; + Request = request; + RequestContext = requestContext; + RequestContextThrewOnReply = false; + SuccessfullySendReply = false; + RequestVersion = request.Version; + Reply = null; + ReplyTimeoutHelper = new TimeoutHelper(); + //this.SecurityContext = null; + InstanceContext = instanceContext; + SuccessfullyBoundInstance = false; + SuccessfullyIncrementedActivity = false; + SuccessfullyLockedInstance = false; + switchedThreads = !cleanThread; + //this.transaction = null; + InputParameters = null; + OutputParameters = null; + ReturnParameter = null; + isInstanceContextSingleton = InstanceContextProviderBase.IsProviderSingleton(Channel.DispatchRuntime.InstanceContextProvider); + invokeContinueGate = null; + + if (!operation.IsOneWay && !operation.Parent.ManualAddressing) + { + RequestID = request.Headers.MessageId; + ReplyToInfo = new RequestReplyCorrelator.ReplyToInfo(request); + } + else + { + RequestID = null; + ReplyToInfo = new RequestReplyCorrelator.ReplyToInfo(); + } + + //this.HostingProperty = AspNetEnvironment.Current.GetHostingProperty(request, true); + + //if (DiagnosticUtility.ShouldUseActivity) + //{ + // this.Activity = TraceUtility.ExtractActivity(this.Request); + //} + + //if (DiagnosticUtility.ShouldUseActivity || TraceUtility.ShouldPropagateActivity) + //{ + // this.ResponseActivityId = ActivityIdHeader.ExtractActivityId(this.Request); + //} + //else + //{ + ResponseActivityId = Guid.Empty; + //} + + //InvokeNotification = new MessageRpcInvokeNotification(/*this.Activity,*/ this.channelHandler); + + //if (this.EventTraceActivity == null && FxTrace.Trace.IsEnd2EndActivityTracingEnabled) + //{ + // if (this.Request != null) + // { + // this.EventTraceActivity = EventTraceActivityHelper.TryExtractActivity(this.Request, true); + // } + //} + } + + internal bool IsPaused + { + get { return paused; } + } + + internal bool SwitchedThreads + { + get { return switchedThreads; } + } + + internal bool IsInstanceContextSingleton + { + set + { + isInstanceContextSingleton = value; + } + } + + //internal TransactionRpcFacet Transaction + //{ + // get + // { + // if (this.transaction == null) + // { + // this.transaction = new TransactionRpcFacet(ref this); + // } + // return this.transaction; + // } + //} + + internal void Abort() + { + AbortRequestContext(); + AbortChannel(); + AbortInstanceContext(); + } + + void AbortRequestContext(RequestContext requestContext) + { + try + { + requestContext.Abort(); + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + + throw; + //channelHandler.HandleError(e); + } + } + + internal void AbortRequestContext() + { + if (OperationContext.RequestContext != null) + { + AbortRequestContext(OperationContext.RequestContext); + } + if ((RequestContext != null) && (RequestContext != OperationContext.RequestContext)) + { + AbortRequestContext(RequestContext); + } + TraceCallDurationInDispatcherIfNecessary(false); + } + + void TraceCallDurationInDispatcherIfNecessary(bool requestContextWasClosedSuccessfully) + { + // only need to trace once (either for the failure or success case) + //if (TD.DispatchFailedIsEnabled()) + //{ + // if (requestContextWasClosedSuccessfully) + // { + // TD.DispatchSuccessful(this.EventTraceActivity, this.Operation.Name); + // } + // else + // { + // TD.DispatchFailed(this.EventTraceActivity, this.Operation.Name); + // } + //} + } + + internal void CloseRequestContext() + { + if (OperationContext.RequestContext != null) + { + DisposeRequestContext(OperationContext.RequestContext); + } + if ((RequestContext != null) && (RequestContext != OperationContext.RequestContext)) + { + DisposeRequestContext(RequestContext); + } + TraceCallDurationInDispatcherIfNecessary(true); + } + + void DisposeRequestContext(RequestContext context) + { + try + { + context.CloseAsync().GetAwaiter().GetResult(); + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + AbortRequestContext(context); + //channelHandler.HandleError(e); + throw; + } + } + + internal void AbortChannel() + { + if ((Channel != null) && Channel.HasSession) + { + try + { + Channel.Abort(); + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + //channelHandler.HandleError(e); + throw; + } + } + } + + // TODO: Make async + internal void CloseChannel() + { + if ((Channel != null) && Channel.HasSession) + { + try + { + var helper = new TimeoutHelper(ChannelHandler.CloseAfterFaultTimeout); + Channel.CloseAsync(helper.GetCancellationToken()).GetAwaiter().GetResult(); + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + //channelHandler.HandleError(e); + throw; + } + } + } + + internal void AbortInstanceContext() + { + if (InstanceContext != null && !isInstanceContextSingleton) + { + try + { + InstanceContext.Abort(); + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + //channelHandler.HandleError(e); + throw; + } + } + } + + bool ProcessError(Exception e) + { + MessageRpcErrorHandler handler = ErrorProcessor; + try + { + Type exceptionType = e.GetType(); + + if (exceptionType.IsAssignableFrom(typeof(FaultException))) + { + DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); + } + else + { + DiagnosticUtility.TraceHandledException(e, TraceEventType.Error); + } + + //if (TraceUtility.MessageFlowTracingOnly) + //{ + // TraceUtility.SetActivityId(this.Request.Properties); + // if (Guid.Empty == DiagnosticTraceBase.ActivityId) + // { + // Guid receivedActivityId = TraceUtility.ExtractActivityId(this.Request); + // if (Guid.Empty != receivedActivityId) + // { + // DiagnosticTraceBase.ActivityId = receivedActivityId; + // } + // } + //} + + + Error = e; + + if (ErrorProcessor != null) + { + ErrorProcessor(ref this); + } + + return (Error == null); + } + catch (Exception e2) + { + if (Fx.IsFatal(e2)) + { + throw; + } + + return ((handler != ErrorProcessor) && ProcessError(e2)); + } + } + + internal void DisposeParameters(bool excludeInput) + { + if (Operation.DisposeParameters) + { + DisposeParametersCore(excludeInput); + } + } + + internal void DisposeParametersCore(bool excludeInput) + { + if (!ParametersDisposed) + { + if (!excludeInput) + { + DisposeParameterList(InputParameters); + } + + DisposeParameterList(OutputParameters); + + IDisposable disposableParameter = ReturnParameter as IDisposable; + if (disposableParameter != null) + { + try + { + disposableParameter.Dispose(); + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + //channelHandler.HandleError(e); + throw; + } + } + ParametersDisposed = true; + } + } + + void DisposeParameterList(object[] parameters) + { + IDisposable disposableParameter = null; + if (parameters != null) + { + foreach (object obj in parameters) + { + disposableParameter = obj as IDisposable; + if (disposableParameter != null) + { + try + { + disposableParameter.Dispose(); + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + //channelHandler.HandleError(e); + throw; + } + } + } + } + } + + //[MethodImpl(MethodImplOptions.NoInlining)] + //IDisposable ApplyHostingIntegrationContextNoInline() + //{ + // return this.HostingProperty.ApplyIntegrationContext(); + //} + + internal async Task ProcessAsync(bool isOperationContextSet) + { + MessageRpc result = this; + //using (ServiceModelActivity.BoundOperation(this.Activity)) + //{ + // bool completed = true; + + OperationContext originalContext; + OperationContext.Holder contextHolder; + if (!isOperationContextSet) + { + contextHolder = OperationContext.CurrentHolder; + originalContext = contextHolder.Context; + } + else + { + contextHolder = null; + originalContext = null; + } + IncrementBusyCount(); + + try + { + if (!isOperationContextSet) + { + contextHolder.Context = OperationContext; + } + + this = await AsyncProcessor(this); + + OperationContext.SetClientReply(null, false); + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + if (!ProcessError(e) && FaultInfo.Fault == null) + { + Abort(); + } + } + finally + { + try + { + DecrementBusyCount(); + + if (!isOperationContextSet) + { + contextHolder.Context = originalContext; + } + + OperationContext.ClearClientReplyNoThrow(); + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + throw DiagnosticUtility.ExceptionUtility.ThrowHelperFatal(e.Message, e); + } + } + + return this; + //} + } + + // UnPause is called on the original MessageRpc to continue work on the current thread, and the copy is ignored. + // Since the copy is ignored, Decrement the BusyCount + internal void UnPause() + { + paused = false; + DecrementBusyCount(); + + } + + internal bool UnlockInvokeContinueGate(out IAsyncResult result) + { + return invokeContinueGate.Unlock(out result); + } + + internal void PrepareInvokeContinueGate() + { + invokeContinueGate = new SignalGate(); + } + + void IncrementBusyCount() + { + // TODO: Do we want a way to keep track of bust count? I believe this originally drove PerformanceCounters so we might want to re-work this functionality. + // Only increment the counter on the service side. + //if (Host != null) + //{ + //Host.IncrementBusyCount(); + //if (AspNetEnvironment.Current.TraceIncrementBusyCountIsEnabled()) + //{ + // AspNetEnvironment.Current.TraceIncrementBusyCount(SR.Format(SR.ServiceBusyCountTrace, this.Operation.Action)); + //} + //} + } + + void DecrementBusyCount() + { + // See comment on IncrementBusyCount + //if (Host != null) + //{ + // Host.DecrementBusyCount(); + //if (AspNetEnvironment.Current.TraceDecrementBusyCountIsEnabled()) + //{ + // AspNetEnvironment.Current.TraceDecrementBusyCount(SR.Format(SR.ServiceBusyCountTrace, this.Operation.Action)); + //} + //} + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/MultipleFilterMatchesException.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/MultipleFilterMatchesException.cs new file mode 100644 index 000000000..18bc98367 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/MultipleFilterMatchesException.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.ObjectModel; + +namespace CoreWCF.Dispatcher +{ + //[Serializable] + internal class MultipleFilterMatchesException : Exception //SystemException + { + //[NonSerialized] + Collection filters; + + //protected MultipleFilterMatchesException(SerializationInfo info, StreamingContext context) + // : base(info, context) + //{ + // this.filters = null; + //} + + public MultipleFilterMatchesException() + : this(SR.FilterMultipleMatches) + { + } + + public MultipleFilterMatchesException(string message) + : this(message, null, null) + { + } + + public MultipleFilterMatchesException(string message, Exception innerException) + : this(message, innerException, null) + { + } + + public MultipleFilterMatchesException(string message, Collection filters) + : this(message, null, filters) + { + } + + public MultipleFilterMatchesException(string message, Exception innerException, Collection filters) + : base(message, innerException) + { + this.filters = filters; + } + + public Collection Filters + { + get + { + return filters; + } + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/MultipleReceiveBinder.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/MultipleReceiveBinder.cs new file mode 100644 index 000000000..c34f90afa --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/MultipleReceiveBinder.cs @@ -0,0 +1,135 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using CoreWCF.Runtime; +using CoreWCF.Channels; + +namespace CoreWCF.Dispatcher +{ + internal class MultipleReceiveBinder : IChannelBinder + { + internal static class MultipleReceiveDefaults + { + internal const int MaxPendingReceives = 1; + } + + IChannelBinder channelBinder; + bool ordered; + + public MultipleReceiveBinder(IChannelBinder channelBinder, int size, bool ordered) + { + this.ordered = ordered; + this.channelBinder = channelBinder; + } + + public IChannel Channel + { + get { return channelBinder.Channel; } + } + + public bool HasSession + { + get { return channelBinder.HasSession; } + } + + public Uri ListenUri + { + get { return channelBinder.ListenUri; } + } + + public EndpointAddress LocalAddress + { + get { return channelBinder.LocalAddress; } + } + + public EndpointAddress RemoteAddress + { + get { return channelBinder.RemoteAddress; } + } + + public void Abort() + { + channelBinder.Abort(); + } + + public void CloseAfterFault(TimeSpan timeout) + { + channelBinder.CloseAfterFault(timeout); + } + + public Task> TryReceiveAsync(CancellationToken token) + { + return channelBinder.TryReceiveAsync(token); + } + + //public bool TryReceive(TimeSpan timeout, out RequestContext requestContext) + //{ + // return this.channelBinder.TryReceive(timeout, out requestContext); + //} + + //public IAsyncResult BeginTryReceive(TimeSpan timeout, AsyncCallback callback, object state) + //{ + // // At anytime there can be only one thread in BeginTryReceive and the + // // outstanding AsyncResult should have completed before the next one. + // // There should be no pending oustanding result here. + // Fx.AssertAndThrow(this.outstanding == null, "BeginTryReceive should not have a pending result."); + + // MultipleReceiveAsyncResult multipleReceiveResult = new MultipleReceiveAsyncResult(callback, state); + // this.outstanding = multipleReceiveResult; + // EnsurePump(timeout); + // IAsyncResult innerResult; + // if (this.pendingResults.TryDequeueHead(out innerResult)) + // { + // HandleReceiveRequestComplete(innerResult, true); + // } + + // return multipleReceiveResult; + //} + + //void EnsurePump(TimeSpan timeout) + //{ + // // ensure we're running at full throttle, the BeginTryReceive calls we make below on the + // // IChannelBinder will typically complete future calls to BeginTryReceive made by CannelHandler + // // corollary to that is that most times these calls will be completed sycnhronously + // while (!this.pendingResults.IsFull) + // { + // ReceiveScopeSignalGate receiveScope = new ReceiveScopeSignalGate(this); + + // // Enqueue the result without locks since this is the pump. + // // BeginTryReceive can be called only from one thread and + // // the head is not yet unlocked so no items can proceed. + // this.pendingResults.Enqueue(receiveScope); + // IAsyncResult result = this.channelBinder.BeginTryReceive(timeout, onInnerReceiveCompleted, receiveScope); + // if (result.CompletedSynchronously) + // { + // this.SignalReceiveCompleted(result); + // } + // } + //} + + //public bool EndTryReceive(IAsyncResult result, out RequestContext requestContext) + //{ + // return MultipleReceiveAsyncResult.End(result, out requestContext); + //} + + public RequestContext CreateRequestContext(Message message) + { + return channelBinder.CreateRequestContext(message); + } + + public Task SendAsync(Message message, CancellationToken token) + { + return channelBinder.SendAsync(message, token); + } + + public Task RequestAsync(Message message, CancellationToken token) + { + return channelBinder.RequestAsync(message, token); + } + + public Task WaitForMessageAsync(CancellationToken token) + { + return channelBinder.WaitForMessageAsync(token); + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/NetDispatcherFaultException.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/NetDispatcherFaultException.cs new file mode 100644 index 000000000..18da6ea5d --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/NetDispatcherFaultException.cs @@ -0,0 +1,16 @@ +using System; + +namespace CoreWCF.Dispatcher +{ + internal class NetDispatcherFaultException : FaultException + { + public NetDispatcherFaultException(string reason, FaultCode code, Exception innerException) + : base(reason, code, FaultCodeConstants.Actions.NetDispatcher, innerException) + { + } + public NetDispatcherFaultException(FaultReason reason, FaultCode code, Exception innerException) + : base(reason, code, FaultCodeConstants.Actions.NetDispatcher, innerException) + { + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/OperationFormatter.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/OperationFormatter.cs new file mode 100644 index 000000000..42cf120a5 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/OperationFormatter.cs @@ -0,0 +1,821 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Runtime.Serialization; +using System.Threading.Tasks; +using System.Xml; +using CoreWCF.Runtime; +using CoreWCF.Channels; +using CoreWCF.Description; +using CoreWCF.Diagnostics; + +namespace CoreWCF.Dispatcher +{ + internal abstract class OperationFormatter : IClientMessageFormatter, IDispatchMessageFormatter + { + MessageDescription replyDescription; + MessageDescription requestDescription; + XmlDictionaryString action; + XmlDictionaryString replyAction; + protected StreamFormatter requestStreamFormatter, replyStreamFormatter; + XmlDictionary dictionary; + string operationName; + static object[] emptyObjectArray = new object[0]; + + public OperationFormatter(OperationDescription description, bool isRpc, bool isEncoded) + { + Validate(description, isRpc, isEncoded); + requestDescription = description.Messages[0]; + if (description.Messages.Count == 2) + replyDescription = description.Messages[1]; + + int stringCount = 3 + requestDescription.Body.Parts.Count; + if (replyDescription != null) + stringCount += 2 + replyDescription.Body.Parts.Count; + + dictionary = new XmlDictionary(stringCount * 2); + GetActions(description, dictionary, out action, out replyAction); + operationName = description.Name; + requestStreamFormatter = StreamFormatter.Create(requestDescription, operationName, true/*isRequest*/); + if (replyDescription != null) + replyStreamFormatter = StreamFormatter.Create(replyDescription, operationName, false/*isResponse*/); + } + + protected abstract void AddHeadersToMessage(Message message, MessageDescription messageDescription, object[] parameters, bool isRequest); + protected abstract void SerializeBody(XmlDictionaryWriter writer, MessageVersion version, string action, MessageDescription messageDescription, object returnValue, object[] parameters, bool isRequest); + protected abstract void GetHeadersFromMessage(Message message, MessageDescription messageDescription, object[] parameters, bool isRequest); + protected abstract object DeserializeBody(XmlDictionaryReader reader, MessageVersion version, string action, MessageDescription messageDescription, object[] parameters, bool isRequest); + + protected virtual void WriteBodyAttributes(XmlDictionaryWriter writer, MessageVersion messageVersion) + { + } + + internal string RequestAction + { + get + { + if (action != null) + return action.Value; + return null; + } + } + internal string ReplyAction + { + get + { + if (replyAction != null) + return replyAction.Value; + return null; + } + } + + protected XmlDictionary Dictionary + { + get { return dictionary; } + } + + protected string OperationName + { + get { return operationName; } + } + + protected MessageDescription ReplyDescription + { + get { return replyDescription; } + } + + protected MessageDescription RequestDescription + { + get { return requestDescription; } + } + + protected XmlDictionaryString AddToDictionary(string s) + { + return AddToDictionary(dictionary, s); + } + + public object DeserializeReply(Message message, object[] parameters) + { + if (message == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("message"); + + if (parameters == null) + throw TraceUtility.ThrowHelperError(new ArgumentNullException(nameof(parameters)), message); + + try + { + object result = null; + if (replyDescription.IsTypedMessage) + { + object typeMessageInstance = CreateTypedMessageInstance(replyDescription.MessageType); + TypedMessageParts typedMessageParts = new TypedMessageParts(typeMessageInstance, replyDescription); + object[] parts = new object[typedMessageParts.Count]; + + GetPropertiesFromMessage(message, replyDescription, parts); + GetHeadersFromMessage(message, replyDescription, parts, false/*isRequest*/); + DeserializeBodyContents(message, parts, false/*isRequest*/); + + // copy values into the actual field/properties + typedMessageParts.SetTypedMessageParts(parts); + + result = typeMessageInstance; + } + else + { + GetPropertiesFromMessage(message, replyDescription, parameters); + GetHeadersFromMessage(message, replyDescription, parameters, false/*isRequest*/); + result = DeserializeBodyContents(message, parameters, false/*isRequest*/); + } + return result; + } + catch (XmlException xe) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException( + SR.Format(SR.SFxErrorDeserializingReplyBodyMore, operationName, xe.Message), xe)); + } + catch (FormatException fe) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException( + SR.Format(SR.SFxErrorDeserializingReplyBodyMore, operationName, fe.Message), fe)); + } + catch (SerializationException se) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException( + SR.Format(SR.SFxErrorDeserializingReplyBodyMore, operationName, se.Message), se)); + } + } + + private static object CreateTypedMessageInstance(Type messageContractType) + { + try + { + object typeMessageInstance = Activator.CreateInstance(messageContractType); + return typeMessageInstance; + } + catch (MissingMethodException mme) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxMessageContractRequiresDefaultConstructor, messageContractType.FullName), mme)); + } + } + + public void DeserializeRequest(Message message, object[] parameters) + { + if (message == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("message"); + + if (parameters == null) + throw TraceUtility.ThrowHelperError(new ArgumentNullException(nameof(parameters)), message); + + try + { + if (requestDescription.IsTypedMessage) + { + object typeMessageInstance = CreateTypedMessageInstance(requestDescription.MessageType); + TypedMessageParts typedMessageParts = new TypedMessageParts(typeMessageInstance, requestDescription); + object[] parts = new object[typedMessageParts.Count]; + + GetPropertiesFromMessage(message, requestDescription, parts); + GetHeadersFromMessage(message, requestDescription, parts, true/*isRequest*/); + DeserializeBodyContents(message, parts, true/*isRequest*/); + + // copy values into the actual field/properties + typedMessageParts.SetTypedMessageParts(parts); + + parameters[0] = typeMessageInstance; + } + else + { + GetPropertiesFromMessage(message, requestDescription, parameters); + GetHeadersFromMessage(message, requestDescription, parameters, true/*isRequest*/); + DeserializeBodyContents(message, parameters, true/*isRequest*/); + } + } + catch (XmlException xe) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + OperationFormatter.CreateDeserializationFailedFault( + SR.Format(SR.SFxErrorDeserializingRequestBodyMore, operationName, xe.Message), + xe)); + } + catch (FormatException fe) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + OperationFormatter.CreateDeserializationFailedFault( + SR.Format(SR.SFxErrorDeserializingRequestBodyMore, operationName, fe.Message), + fe)); + } + catch (SerializationException se) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException( + SR.Format(SR.SFxErrorDeserializingRequestBodyMore, operationName, se.Message), + se)); + } + } + + object DeserializeBodyContents(Message message, object[] parameters, bool isRequest) + { + MessageDescription messageDescription; + StreamFormatter streamFormatter; + + SetupStreamAndMessageDescription(isRequest, out streamFormatter, out messageDescription); + + if (streamFormatter != null) + { + object retVal = null; + streamFormatter.Deserialize(parameters, ref retVal, message); + return retVal; + } + + if (message.IsEmpty) + { + return null; + } + else + { + XmlDictionaryReader reader = message.GetReaderAtBodyContents(); + using (reader) + { + object body = DeserializeBody(reader, message.Version, RequestAction, messageDescription, parameters, isRequest); + message.ReadFromBodyContentsToEnd(reader); + return body; + } + } + } + + public Message SerializeRequest(MessageVersion messageVersion, object[] parameters) + { + object[] parts = null; + + if (messageVersion == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("messageVersion"); + + if (parameters == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("parameters"); + if (requestDescription.IsTypedMessage) + { + TypedMessageParts typedMessageParts = new TypedMessageParts(parameters[0], requestDescription); + + // copy values from the actual field/properties + parts = new object[typedMessageParts.Count]; + typedMessageParts.GetTypedMessageParts(parts); + } + else + { + parts = parameters; + } + Message msg = new OperationFormatterMessage(this, messageVersion, + action == null ? null : ActionHeader.Create(action, messageVersion.Addressing), + parts, null, true/*isRequest*/); + AddPropertiesToMessage(msg, requestDescription, parts); + AddHeadersToMessage(msg, requestDescription, parts, true /*isRequest*/); + + return msg; + } + + public Message SerializeReply(MessageVersion messageVersion, object[] parameters, object result) + { + object[] parts = null; + object resultPart = null; + + if (messageVersion == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("messageVersion"); + + if (parameters == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("parameters"); + + if (replyDescription.IsTypedMessage) + { + // If the response is a typed message then it must + // me the response (as opposed to an out param). We will + // serialize the response in the exact same way that we + // would serialize a bunch of outs (with no return value). + + TypedMessageParts typedMessageParts = new TypedMessageParts(result, replyDescription); + + // make a copy of the list so that we have the actual values of the field/properties + parts = new object[typedMessageParts.Count]; + typedMessageParts.GetTypedMessageParts(parts); + + resultPart = null; + } + else + { + parts = parameters; + resultPart = result; + } + + Message msg = new OperationFormatterMessage(this, messageVersion, + replyAction == null ? null : ActionHeader.Create(replyAction, messageVersion.Addressing), + parts, resultPart, false/*isRequest*/); + AddPropertiesToMessage(msg, replyDescription, parts); + AddHeadersToMessage(msg, replyDescription, parts, false /*isRequest*/); + return msg; + } + + void SetupStreamAndMessageDescription(bool isRequest, out StreamFormatter streamFormatter, out MessageDescription messageDescription) + { + if (isRequest) + { + streamFormatter = requestStreamFormatter; + messageDescription = requestDescription; + } + else + { + streamFormatter = replyStreamFormatter; + messageDescription = replyDescription; + } + } + + async Task SerializeBodyContentsAsync(XmlDictionaryWriter writer, MessageVersion version, object[] parameters, object returnValue, bool isRequest) + { + MessageDescription messageDescription; + StreamFormatter streamFormatter; + + SetupStreamAndMessageDescription(isRequest, out streamFormatter, out messageDescription); + + if (streamFormatter != null) + { + await streamFormatter.SerializeAsync(writer, parameters, returnValue); + return; + } + + SerializeBody(writer, version, RequestAction, messageDescription, returnValue, parameters, isRequest); + } + + void SerializeBodyContents(XmlDictionaryWriter writer, MessageVersion version, object[] parameters, object returnValue, bool isRequest) + { + MessageDescription messageDescription; + StreamFormatter streamFormatter; + + SetupStreamAndMessageDescription(isRequest, out streamFormatter, out messageDescription); + + if (streamFormatter != null) + { + streamFormatter.Serialize(writer, parameters, returnValue); + return; + } + + SerializeBody(writer, version, RequestAction, messageDescription, returnValue, parameters, isRequest); + } + + void AddPropertiesToMessage(Message message, MessageDescription messageDescription, object[] parameters) + { + if (messageDescription.Properties.Count > 0) + { + AddPropertiesToMessageCore(message, messageDescription, parameters); + } + } + + void AddPropertiesToMessageCore(Message message, MessageDescription messageDescription, object[] parameters) + { + MessageProperties properties = message.Properties; + MessagePropertyDescriptionCollection propertyDescriptions = messageDescription.Properties; + for (int i = 0; i < propertyDescriptions.Count; i++) + { + MessagePropertyDescription propertyDescription = propertyDescriptions[i]; + object parameter = parameters[propertyDescription.Index]; + if (null != parameter) + properties.Add(propertyDescription.Name, parameter); + } + } + + void GetPropertiesFromMessage(Message message, MessageDescription messageDescription, object[] parameters) + { + if (messageDescription.Properties.Count > 0) + { + GetPropertiesFromMessageCore(message, messageDescription, parameters); + } + } + + void GetPropertiesFromMessageCore(Message message, MessageDescription messageDescription, object[] parameters) + { + MessageProperties properties = message.Properties; + MessagePropertyDescriptionCollection propertyDescriptions = messageDescription.Properties; + for (int i = 0; i < propertyDescriptions.Count; i++) + { + MessagePropertyDescription propertyDescription = propertyDescriptions[i]; + if (properties.ContainsKey(propertyDescription.Name)) + { + parameters[propertyDescription.Index] = properties[propertyDescription.Name]; + } + } + } + + internal static object GetContentOfMessageHeaderOfT(MessageHeaderDescription headerDescription, object parameterValue, out bool mustUnderstand, out bool relay, out string actor) + { + actor = headerDescription.Actor; + mustUnderstand = headerDescription.MustUnderstand; + relay = headerDescription.Relay; + + if (headerDescription.TypedHeader && parameterValue != null) + parameterValue = TypedHeaderManager.GetContent(headerDescription.Type, parameterValue, out mustUnderstand, out relay, out actor); + return parameterValue; + } + + internal static bool IsValidReturnValue(MessagePartDescription returnValue) + { + return (returnValue != null) && (returnValue.Type != typeof(void)); + } + + internal static XmlDictionaryString AddToDictionary(XmlDictionary dictionary, string s) + { + XmlDictionaryString dictionaryString; + if (!dictionary.TryLookup(s, out dictionaryString)) + { + dictionaryString = dictionary.Add(s); + } + return dictionaryString; + } + + internal static void Validate(OperationDescription operation, bool isRpc, bool isEncoded) + { + if (isEncoded && !isRpc) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxDocEncodedNotSupported, operation.Name))); + } + + bool hasVoid = false; + bool hasTypedOrUntypedMessage = false; + bool hasParameter = false; + for (int i = 0; i < operation.Messages.Count; i++) + { + MessageDescription message = operation.Messages[i]; + if (message.IsTypedMessage || message.IsUntypedMessage) + { + if (isRpc && operation.IsValidateRpcWrapperName) + { + if (!isEncoded) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxTypedMessageCannotBeRpcLiteral, operation.Name))); + + } + hasTypedOrUntypedMessage = true; + } + else if (message.IsVoid) + hasVoid = true; + else + hasParameter = true; + } + if (hasParameter && hasTypedOrUntypedMessage) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxTypedOrUntypedMessageCannotBeMixedWithParameters, operation.Name))); + if (isRpc && hasTypedOrUntypedMessage && hasVoid) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxTypedOrUntypedMessageCannotBeMixedWithVoidInRpc, operation.Name))); + } + + internal static void GetActions(OperationDescription description, XmlDictionary dictionary, out XmlDictionaryString action, out XmlDictionaryString replyAction) + { + string actionString, replyActionString; + actionString = description.Messages[0].Action; + if (actionString == MessageHeaders.WildcardAction) + actionString = null; + if (!description.IsOneWay) + replyActionString = description.Messages[1].Action; + else + replyActionString = null; + if (replyActionString == MessageHeaders.WildcardAction) + replyActionString = null; + action = replyAction = null; + if (actionString != null) + action = AddToDictionary(dictionary, actionString); + if (replyActionString != null) + replyAction = AddToDictionary(dictionary, replyActionString); + } + + internal static NetDispatcherFaultException CreateDeserializationFailedFault(string reason, Exception innerException) + { + reason = SR.Format(SR.SFxDeserializationFailed1, reason); + FaultCode code = new FaultCode(FaultCodeConstants.Codes.DeserializationFailed, FaultCodeConstants.Namespaces.NetDispatch); + code = FaultCode.CreateSenderFaultCode(code); + return new NetDispatcherFaultException(reason, code, innerException); + } + + internal static void TraceAndSkipElement(XmlReader xmlReader) + { + //if (DiagnosticUtility.ShouldTraceVerbose) + //{ + // TraceUtility.TraceEvent(TraceEventType.Verbose, TraceCode.ElementIgnored, SR.SFxTraceCodeElementIgnored, new StringTraceRecord("Element", xmlReader.NamespaceURI + ":" + xmlReader.LocalName)); + //} + xmlReader.Skip(); + } + + class TypedMessageParts + { + object instance; + MemberInfo[] members; + + public TypedMessageParts(object instance, MessageDescription description) + { + if (description == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException(nameof(description))); + } + + if (instance == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException(SR.Format(SR.SFxTypedMessageCannotBeNull, description.Action))); + } + + members = new MemberInfo[description.Body.Parts.Count + description.Properties.Count + description.Headers.Count]; + + foreach (MessagePartDescription part in description.Headers) + members[part.Index] = part.MemberInfo; + + foreach (MessagePartDescription part in description.Properties) + members[part.Index] = part.MemberInfo; + + foreach (MessagePartDescription part in description.Body.Parts) + members[part.Index] = part.MemberInfo; + + this.instance = instance; + } + + object GetValue(int index) + { + MemberInfo memberInfo = members[index]; + PropertyInfo propertyInfo = memberInfo as PropertyInfo; + if (propertyInfo != null) + { + return propertyInfo.GetValue(instance, null); + } + else + { + return ((FieldInfo)memberInfo).GetValue(instance); + } + } + + void SetValue(object value, int index) + { + MemberInfo memberInfo = members[index]; + PropertyInfo propertyInfo = memberInfo as PropertyInfo; + if (propertyInfo != null) + { + propertyInfo.SetValue(instance, value, null); + } + else + { + ((FieldInfo)memberInfo).SetValue(instance, value); + } + } + + internal void GetTypedMessageParts(object[] values) + { + for (int i = 0; i < members.Length; i++) + { + values[i] = GetValue(i); + } + } + + internal void SetTypedMessageParts(object[] values) + { + for (int i = 0; i < members.Length; i++) + { + SetValue(values[i], i); + } + } + + internal int Count + { + get { return members.Length; } + } + } + + internal class OperationFormatterMessage : BodyWriterMessage + { + OperationFormatter operationFormatter; + public OperationFormatterMessage(OperationFormatter operationFormatter, MessageVersion version, ActionHeader action, + object[] parameters, object returnValue, bool isRequest) + : base(version, action, new OperationFormatterBodyWriter(operationFormatter, version, parameters, returnValue, isRequest)) + { + this.operationFormatter = operationFormatter; + } + + + public OperationFormatterMessage(MessageVersion version, string action, BodyWriter bodyWriter) : base(version, action, bodyWriter) { } + + OperationFormatterMessage(MessageHeaders headers, KeyValuePair[] properties, OperationFormatterBodyWriter bodyWriter) + : base(headers, properties, bodyWriter) + { + operationFormatter = bodyWriter.OperationFormatter; + } + + protected override void OnWriteStartBody(XmlDictionaryWriter writer) + { + base.OnWriteStartBody(writer); + operationFormatter.WriteBodyAttributes(writer, Version); + } + + protected override MessageBuffer OnCreateBufferedCopy(int maxBufferSize) + { + BodyWriter bufferedBodyWriter; + if (BodyWriter.IsBuffered) + { + bufferedBodyWriter = base.BodyWriter; + } + else + { + bufferedBodyWriter = base.BodyWriter.CreateBufferedCopy(maxBufferSize); + } + KeyValuePair[] properties = new KeyValuePair[base.Properties.Count]; + ((ICollection>)base.Properties).CopyTo(properties, 0); + return new OperationFormatterMessageBuffer(base.Headers, properties, bufferedBodyWriter); + } + + class OperationFormatterBodyWriter : BodyWriter + { + bool isRequest; + OperationFormatter operationFormatter; + object[] parameters; + object returnValue; + MessageVersion version; + + public OperationFormatterBodyWriter(OperationFormatter operationFormatter, MessageVersion version, + object[] parameters, object returnValue, bool isRequest) + : base(AreParametersBuffered(isRequest, operationFormatter)) + { + this.parameters = parameters; + this.returnValue = returnValue; + this.isRequest = isRequest; + this.operationFormatter = operationFormatter; + this.version = version; + } + + object ThisLock + { + get { return this; } + } + + static bool AreParametersBuffered(bool isRequest, OperationFormatter operationFormatter) + { + StreamFormatter streamFormatter = isRequest ? operationFormatter.requestStreamFormatter : operationFormatter.replyStreamFormatter; + return streamFormatter == null; + } + + protected override void OnWriteBodyContents(XmlDictionaryWriter writer) + { + lock (ThisLock) + { + operationFormatter.SerializeBodyContents(writer, version, parameters, returnValue, isRequest); + } + } + + protected override Task OnWriteBodyContentsAsync(XmlDictionaryWriter writer) + { + return operationFormatter.SerializeBodyContentsAsync(writer, version, parameters, returnValue, isRequest); + } + + internal OperationFormatter OperationFormatter + { + get { return operationFormatter; } + } + } + + class OperationFormatterMessageBuffer : BodyWriterMessageBuffer + { + public OperationFormatterMessageBuffer(MessageHeaders headers, + KeyValuePair[] properties, BodyWriter bodyWriter) + : base(headers, properties, bodyWriter) + { + } + + public override Message CreateMessage() + { + OperationFormatterBodyWriter operationFormatterBodyWriter = base.BodyWriter as OperationFormatterBodyWriter; + if (operationFormatterBodyWriter == null) + return base.CreateMessage(); + lock (ThisLock) + { + if (base.Closed) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateBufferDisposedException()); + return new OperationFormatterMessage(base.Headers, base.Properties, operationFormatterBodyWriter); + } + } + } + } + + internal abstract class OperationFormatterHeader : MessageHeader + { + protected MessageHeader innerHeader; //use innerHeader to handle versionSupported, actor/role handling etc. + protected OperationFormatter operationFormatter; + protected MessageVersion version; + + public OperationFormatterHeader(OperationFormatter operationFormatter, MessageVersion version, string name, string ns, bool mustUnderstand, string actor, bool relay) + { + this.operationFormatter = operationFormatter; + this.version = version; + if (actor != null) + innerHeader = MessageHeader.CreateHeader(name, ns, null/*headerValue*/, mustUnderstand, actor, relay); + else + innerHeader = MessageHeader.CreateHeader(name, ns, null/*headerValue*/, mustUnderstand, "", relay); + } + + + public override bool IsMessageVersionSupported(MessageVersion messageVersion) + { + return innerHeader.IsMessageVersionSupported(messageVersion); + } + + + public override string Name + { + get { return innerHeader.Name; } + } + + public override string Namespace + { + get { return innerHeader.Namespace; } + } + + public override bool MustUnderstand + { + get { return innerHeader.MustUnderstand; } + } + + public override bool Relay + { + get { return innerHeader.Relay; } + } + + public override string Actor + { + get { return innerHeader.Actor; } + } + + protected override void OnWriteStartHeader(XmlDictionaryWriter writer, MessageVersion messageVersion) + { + //Prefix needed since there may be xsi:type attribute at toplevel with qname value where ns = "" + writer.WriteStartElement((Namespace == null || Namespace.Length == 0) ? string.Empty : "h", Name, Namespace); + OnWriteHeaderAttributes(writer, messageVersion); + } + + protected virtual void OnWriteHeaderAttributes(XmlDictionaryWriter writer, MessageVersion messageVersion) + { + base.WriteHeaderAttributes(writer, messageVersion); + } + } + + internal class XmlElementMessageHeader : OperationFormatterHeader + { + protected XmlElement headerValue; + public XmlElementMessageHeader(OperationFormatter operationFormatter, MessageVersion version, string name, string ns, bool mustUnderstand, string actor, bool relay, XmlElement headerValue) : + base(operationFormatter, version, name, ns, mustUnderstand, actor, relay) + { + this.headerValue = headerValue; + } + + protected override void OnWriteHeaderAttributes(XmlDictionaryWriter writer, MessageVersion messageVersion) + { + throw new PlatformNotSupportedException(); + // Needs Net Standard 1.7 + //base.WriteHeaderAttributes(writer, messageVersion); + //XmlDictionaryReader nodeReader = XmlDictionaryReader.CreateDictionaryReader(new XmlNodeReader(headerValue)); + //nodeReader.MoveToContent(); + //writer.WriteAttributes(nodeReader, false); + } + + protected override void OnWriteHeaderContents(XmlDictionaryWriter writer, MessageVersion messageVersion) + { + headerValue.WriteContentTo(writer); + } + } + internal struct QName + { + internal string Name; + internal string Namespace; + internal QName(string name, string ns) + { + Name = name; + Namespace = ns; + } + } + internal class QNameComparer : IEqualityComparer + { + static internal QNameComparer Singleton = new QNameComparer(); + QNameComparer() { } + + public bool Equals(QName x, QName y) + { + return x.Name == y.Name && x.Namespace == y.Namespace; + } + + public int GetHashCode(QName obj) + { + return obj.Name.GetHashCode(); + } + } + internal class MessageHeaderDescriptionTable : Dictionary + { + internal MessageHeaderDescriptionTable() : base(QNameComparer.Singleton) { } + internal void Add(string name, string ns, MessageHeaderDescription message) + { + base.Add(new QName(name, ns), message); + } + internal MessageHeaderDescription Get(string name, string ns) + { + MessageHeaderDescription message; + if (base.TryGetValue(new QName(name, ns), out message)) + return message; + return null; + } + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/OperationInvokerBehavior.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/OperationInvokerBehavior.cs new file mode 100644 index 000000000..0cf2ab90f --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/OperationInvokerBehavior.cs @@ -0,0 +1,72 @@ +using System; +using CoreWCF.Channels; +using CoreWCF.Description; + +namespace CoreWCF.Dispatcher +{ + public class OperationInvokerBehavior : IOperationBehavior + { + public OperationInvokerBehavior() + { + } + + void IOperationBehavior.Validate(OperationDescription description) + { + } + + void IOperationBehavior.AddBindingParameters(OperationDescription description, BindingParameterCollection parameters) + { + } + + void IOperationBehavior.ApplyDispatchBehavior(OperationDescription description, DispatchOperation dispatch) + { + if (dispatch == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("dispatch"); + } + if (description == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("description"); + } + + if (description.TaskMethod != null) + { + dispatch.Invoker = new TaskMethodInvoker(description.TaskMethod, description.TaskTResult); + } + else if (description.SyncMethod != null) + { + if (description.BeginMethod != null) + { + // both sync and async methods are present on the contract, check the preference + //OperationBehaviorAttribute operationBehaviorAttribue = description.Behaviors.Find(); + //if ((operationBehaviorAttribue != null) && operationBehaviorAttribue.PreferAsyncInvocation) + //{ + // dispatch.Invoker = new AsyncMethodInvoker(description.BeginMethod, description.EndMethod); + //} + //else + //{ + dispatch.Invoker = new SyncMethodInvoker(description.SyncMethod); + //} + } + else + { + // only sync method is present on the contract + dispatch.Invoker = new SyncMethodInvoker(description.SyncMethod); + } + } + else + { + if (description.BeginMethod != null) + { + // only async method is present on the contract + throw new PlatformNotSupportedException(); + //dispatch.Invoker = new AsyncMethodInvoker(description.BeginMethod, description.EndMethod); + } + } + } + + void IOperationBehavior.ApplyClientBehavior(OperationDescription description, ClientOperation proxy) + { + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/OperationSelectorBehavior.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/OperationSelectorBehavior.cs new file mode 100644 index 000000000..b58161537 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/OperationSelectorBehavior.cs @@ -0,0 +1,83 @@ +using System.Collections.Generic; +using System.Reflection; +using CoreWCF.Channels; +using CoreWCF.Description; + +namespace CoreWCF.Dispatcher +{ + internal class OperationSelectorBehavior : IContractBehavior + { + void IContractBehavior.Validate(ContractDescription description, ServiceEndpoint endpoint) + { + } + + void IContractBehavior.AddBindingParameters(ContractDescription description, ServiceEndpoint endpoint, BindingParameterCollection parameters) + { + } + + void IContractBehavior.ApplyDispatchBehavior(ContractDescription description, ServiceEndpoint endpoint, DispatchRuntime dispatch) + { + if (dispatch.ClientRuntime != null) + dispatch.ClientRuntime.OperationSelector = new MethodInfoOperationSelector(description, MessageDirection.Output); + } + + void IContractBehavior.ApplyClientBehavior(ContractDescription description, ServiceEndpoint endpoint, ClientRuntime proxy) + { + proxy.OperationSelector = new MethodInfoOperationSelector(description, MessageDirection.Input); + } + + internal class MethodInfoOperationSelector : IClientOperationSelector + { + Dictionary operationMap; + + internal MethodInfoOperationSelector(ContractDescription description, MessageDirection directionThatRequiresClientOpSelection) + { + operationMap = new Dictionary(); + + for (int i = 0; i < description.Operations.Count; i++) + { + OperationDescription operation = description.Operations[i]; + if (operation.Messages[0].Direction == directionThatRequiresClientOpSelection) + { + if (operation.SyncMethod != null) + { + if (!operationMap.ContainsKey(operation.SyncMethod)) + operationMap.Add(operation.SyncMethod, operation.Name); + } + + if (operation.BeginMethod != null) + { + if (!operationMap.ContainsKey(operation.BeginMethod)) + { + operationMap.Add(operation.BeginMethod, operation.Name); + operationMap.Add(operation.EndMethod, operation.Name); + } + } + + if (operation.TaskMethod != null) + { + if (!operationMap.ContainsKey(operation.TaskMethod)) + { + operationMap.Add(operation.TaskMethod, operation.Name); + } + } + } + } + } + + public bool AreParametersRequiredForSelection + { + get { return false; } + } + + public string SelectOperation(MethodBase method, object[] parameters) + { + if (operationMap.ContainsKey(method)) + return operationMap[method]; + else + return null; + } + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/PerCallInstanceContextProvider.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/PerCallInstanceContextProvider.cs new file mode 100644 index 000000000..013c5a09c --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/PerCallInstanceContextProvider.cs @@ -0,0 +1,40 @@ +using System; +using CoreWCF.Channels; + +namespace CoreWCF.Dispatcher +{ + internal class PerCallInstanceContextProvider : InstanceContextProviderBase + { + internal PerCallInstanceContextProvider(DispatchRuntime dispatchRuntime) + : base(dispatchRuntime) + { + } + + #region IInstanceContextProvider Members + + public override InstanceContext GetExistingInstanceContext(Message message, IContextChannel channel) + { + //Always return null so we will create new InstanceContext for each message + return null; + } + + public override void InitializeInstanceContext(InstanceContext instanceContext, Message message, IContextChannel channel) + { + //no-op + } + + public override bool IsIdle(InstanceContext instanceContext) + { + //By default return true if no channels are bound to this context + return true; + } + + public override void NotifyIdle(Action callback, InstanceContext instanceContext) + { + //no-op + } + + #endregion + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/PerSessionInstanceContextProvider.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/PerSessionInstanceContextProvider.cs new file mode 100644 index 000000000..f12276bdb --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/PerSessionInstanceContextProvider.cs @@ -0,0 +1,52 @@ +using System; +using CoreWCF.Runtime; +using CoreWCF.Channels; + +namespace CoreWCF.Dispatcher +{ + class PerSessionInstanceContextProvider : InstanceContextProviderBase + { + + internal PerSessionInstanceContextProvider(DispatchRuntime dispatchRuntime) + : base(dispatchRuntime) + { + } + + public override InstanceContext GetExistingInstanceContext(Message message, IContextChannel channel) + { + // Here is the flow for a Sessionful channel + // 1. First request comes in on new channel. + // 2. ServiceChannel.InstanceContext is returned which is null. + // 3. InstanceBehavior.EnsureInstanceContext will create a new InstanceContext. + // 4. this.InitializeInstanceContext is called with the newly created InstanceContext and the channel. + // 5. If the channel is sessionful then its bound to the InstanceContext. + // 6. Bind channel to the InstanceContext. + // 7. For all further requests on the same channel, we will return ServiceChannel.InstanceContext which will be non null. + ServiceChannel serviceChannel = GetServiceChannelFromProxy(channel); + Fx.Assert((serviceChannel != null), "CoreWCF.Dispatcher.PerSessionInstanceContextProvider.GetExistingInstanceContext(), serviceChannel != null"); + return (serviceChannel != null) ? serviceChannel.InstanceContext : null; + } + + public override void InitializeInstanceContext(InstanceContext instanceContext, Message message, IContextChannel channel) + { + ServiceChannel serviceChannel = GetServiceChannelFromProxy(channel); + if (serviceChannel != null && serviceChannel.HasSession) + { + instanceContext.BindIncomingChannel(serviceChannel); + } + } + + + public override bool IsIdle(InstanceContext instanceContext) + { + //By default return true + return true; + } + + public override void NotifyIdle(Action callback, InstanceContext instanceContext) + { + //no-op + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/PrefixEndpointAddressMessageFilter.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/PrefixEndpointAddressMessageFilter.cs new file mode 100644 index 000000000..255fb3f91 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/PrefixEndpointAddressMessageFilter.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections.Generic; +using CoreWCF.Channels; + +namespace CoreWCF.Dispatcher +{ + internal class PrefixEndpointAddressMessageFilter : MessageFilter + { + EndpointAddress _address; + EndpointAddressMessageFilterHelper _helper; + UriPrefixTable _addressTable; + HostNameComparisonMode _hostNameComparisonMode; + + public PrefixEndpointAddressMessageFilter(EndpointAddress address) + : this(address, false) + { + } + + public PrefixEndpointAddressMessageFilter(EndpointAddress address, bool includeHostNameInComparison) + { + if (address == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(address)); + } + + _address = address; + _helper = new EndpointAddressMessageFilterHelper(_address); + + _hostNameComparisonMode = includeHostNameInComparison + ? HostNameComparisonMode.Exact + : HostNameComparisonMode.StrongWildcard; + + _addressTable = new UriPrefixTable(); + _addressTable.RegisterUri(_address.Uri, _hostNameComparisonMode, new object()); + } + + public EndpointAddress Address + { + get { return _address; } + } + + public bool IncludeHostNameInComparison + { + get { return (_hostNameComparisonMode == HostNameComparisonMode.Exact); } + } + + protected internal override IMessageFilterTable CreateFilterTable() + { + return new PrefixEndpointAddressMessageFilterTable(); + } + + public override bool Match(MessageBuffer messageBuffer) + { + if (messageBuffer == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(messageBuffer)); + } + + Message msg = messageBuffer.CreateMessage(); + try + { + return Match(msg); + } + finally + { + msg.Close(); + } + } + + public override bool Match(Message message) + { + if (message == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(message)); + } + + // To + Uri to = message.Headers.To; + + object o; + if (to == null || !_addressTable.TryLookupUri(to, _hostNameComparisonMode, out o)) + { + return false; + } + + return _helper.Match(message); + } + + internal Dictionary HeaderLookup + { + get { return _helper.HeaderLookup; } + } + + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/PrefixEndpointAddressMessageFilterTable.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/PrefixEndpointAddressMessageFilterTable.cs new file mode 100644 index 000000000..7e76d4d1f --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/PrefixEndpointAddressMessageFilterTable.cs @@ -0,0 +1,156 @@ +using System; +using CoreWCF.Runtime; +using CoreWCF.Channels; + +namespace CoreWCF.Dispatcher +{ + internal class PrefixEndpointAddressMessageFilterTable : EndpointAddressMessageFilterTable + { + UriPrefixTable toHostTable; + UriPrefixTable toNoHostTable; + + public PrefixEndpointAddressMessageFilterTable() + : base() + { + } + + protected override void InitializeLookupTables() + { + toHostTable = new UriPrefixTable(); + toNoHostTable = new UriPrefixTable(); + } + + public override void Add(MessageFilter filter, TFilterData data) + { + if (filter == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("filter"); + } + + Add((PrefixEndpointAddressMessageFilter)filter, data); + } + + public override void Add(EndpointAddressMessageFilter filter, TFilterData data) + { + if (filter == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("filter"); + } + + Fx.Assert("EndpointAddressMessageFilter cannot be added to PrefixEndpointAddressMessageFilterTable"); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException("EndpointAddressMessageFilter cannot be added to PrefixEndpointAddressMessageFilterTable")); + } + + public void Add(PrefixEndpointAddressMessageFilter filter, TFilterData data) + { + if (filter == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("filter"); + } + + filters.Add(filter, data); + + // Create the candidate + byte[] mask = BuildMask(filter.HeaderLookup); + Candidate can = new Candidate(filter, data, mask, filter.HeaderLookup); + candidates.Add(filter, can); + + Uri soapToAddress = filter.Address.Uri; + + CandidateSet cset; + if (!TryMatchCandidateSet(soapToAddress, filter.IncludeHostNameInComparison, out cset)) + { + cset = new CandidateSet(); + GetAddressTable(filter.IncludeHostNameInComparison).RegisterUri(soapToAddress, GetComparisonMode(filter.IncludeHostNameInComparison), cset); + } + cset.candidates.Add(can); + + IncrementQNameCount(cset, filter.Address); + } + + HostNameComparisonMode GetComparisonMode(bool includeHostNameInComparison) + { + return includeHostNameInComparison ? HostNameComparisonMode.Exact : HostNameComparisonMode.StrongWildcard; + } + + UriPrefixTable GetAddressTable(bool includeHostNameInComparison) + { + return includeHostNameInComparison ? toHostTable : toNoHostTable; + } + + internal override bool TryMatchCandidateSet(Uri to, bool includeHostNameInComparison, out CandidateSet cset) + { + return GetAddressTable(includeHostNameInComparison).TryLookupUri(to, GetComparisonMode(includeHostNameInComparison), out cset); + } + + protected override void ClearLookupTables() + { + toHostTable = new UriPrefixTable.CandidateSet>(); + toNoHostTable = new UriPrefixTable.CandidateSet>(); + } + + public override bool Remove(MessageFilter filter) + { + if (filter == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("filter"); + } + + PrefixEndpointAddressMessageFilter pFilter = filter as PrefixEndpointAddressMessageFilter; + if (pFilter != null) + { + return Remove(pFilter); + } + + return false; + } + + public override bool Remove(EndpointAddressMessageFilter filter) + { + if (filter == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("filter"); + } + + Fx.Assert("EndpointAddressMessageFilter cannot be removed from PrefixEndpointAddressMessageFilterTable"); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException("EndpointAddressMessageFilter cannot be removed from PrefixEndpointAddressMessageFilterTable")); + } + + public bool Remove(PrefixEndpointAddressMessageFilter filter) + { + if (filter == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("filter"); + } + + if (!filters.Remove(filter)) + { + return false; + } + + Candidate can = candidates[filter]; + Uri soapToAddress = filter.Address.Uri; + + CandidateSet cset = null; + if (TryMatchCandidateSet(soapToAddress, filter.IncludeHostNameInComparison, out cset)) + { + if (cset.candidates.Count == 1) + { + GetAddressTable(filter.IncludeHostNameInComparison).UnregisterUri(soapToAddress, GetComparisonMode(filter.IncludeHostNameInComparison)); + } + else + { + DecrementQNameCount(cset, filter.Address); + + // Remove Candidate + cset.candidates.Remove(can); + } + } + candidates.Remove(filter); + + RebuildMasks(); + return true; + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/PrimitiveOperationFormatter.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/PrimitiveOperationFormatter.cs new file mode 100644 index 000000000..ac3f58ea8 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/PrimitiveOperationFormatter.cs @@ -0,0 +1,974 @@ +using System; +using System.Reflection; +using System.Runtime.Serialization; +using System.Xml; +using CoreWCF.Runtime; +using CoreWCF.Channels; +using CoreWCF.Description; +using CoreWCF.Diagnostics; + +namespace CoreWCF.Dispatcher +{ + internal class PrimitiveOperationFormatter : IClientMessageFormatter, IDispatchMessageFormatter + { + internal static readonly string NsXsi = "http://www.w3.org/2001/XMLSchema-instance"; + + OperationDescription operation; + MessageDescription responseMessage; + MessageDescription requestMessage; + XmlDictionaryString action; + XmlDictionaryString replyAction; + ActionHeader actionHeaderNone; + ActionHeader actionHeader10; + ActionHeader replyActionHeaderNone; + ActionHeader replyActionHeader10; + XmlDictionaryString requestWrapperName; + XmlDictionaryString requestWrapperNamespace; + XmlDictionaryString responseWrapperName; + XmlDictionaryString responseWrapperNamespace; + PartInfo[] requestParts; + PartInfo[] responseParts; + PartInfo returnPart; + XmlDictionaryString xsiNilLocalName; + XmlDictionaryString xsiNilNamespace; + + public PrimitiveOperationFormatter(OperationDescription description, bool isRpc) + { + if (description == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("description"); + + OperationFormatter.Validate(description, isRpc, false/*isEncoded*/); + + operation = description; + requestMessage = description.Messages[0]; + if (description.Messages.Count == 2) + responseMessage = description.Messages[1]; + + int stringCount = 3 + requestMessage.Body.Parts.Count; + if (responseMessage != null) + stringCount += 2 + responseMessage.Body.Parts.Count; + + XmlDictionary dictionary = new XmlDictionary(stringCount * 2); + + xsiNilLocalName = dictionary.Add("nil"); + xsiNilNamespace = dictionary.Add(NsXsi); + + OperationFormatter.GetActions(description, dictionary, out action, out replyAction); + + if (requestMessage.Body.WrapperName != null) + { + requestWrapperName = AddToDictionary(dictionary, requestMessage.Body.WrapperName); + requestWrapperNamespace = AddToDictionary(dictionary, requestMessage.Body.WrapperNamespace); + } + + requestParts = AddToDictionary(dictionary, requestMessage.Body.Parts, isRpc); + + if (responseMessage != null) + { + if (responseMessage.Body.WrapperName != null) + { + responseWrapperName = AddToDictionary(dictionary, responseMessage.Body.WrapperName); + responseWrapperNamespace = AddToDictionary(dictionary, responseMessage.Body.WrapperNamespace); + } + + responseParts = AddToDictionary(dictionary, responseMessage.Body.Parts, isRpc); + + if (responseMessage.Body.ReturnValue != null && responseMessage.Body.ReturnValue.Type != typeof(void)) + { + returnPart = AddToDictionary(dictionary, responseMessage.Body.ReturnValue, isRpc); + } + } + } + + ActionHeader ActionHeaderNone + { + get + { + if (actionHeaderNone == null) + { + actionHeaderNone = + ActionHeader.Create(action, AddressingVersion.None); + } + + return actionHeaderNone; + } + } + + ActionHeader ActionHeader10 + { + get + { + if (actionHeader10 == null) + { + actionHeader10 = + ActionHeader.Create(action, AddressingVersion.WSAddressing10); + } + + return actionHeader10; + } + } + + //ActionHeader ActionHeaderAugust2004 + //{ + // get + // { + // if (actionHeaderAugust2004 == null) + // { + // actionHeaderAugust2004 = + // ActionHeader.Create(this.action, AddressingVersion.WSAddressingAugust2004); + // } + + // return actionHeaderAugust2004; + // } + //} + + ActionHeader ReplyActionHeaderNone + { + get + { + if (replyActionHeaderNone == null) + { + replyActionHeaderNone = + ActionHeader.Create(replyAction, AddressingVersion.None); + } + + return replyActionHeaderNone; + } + } + + ActionHeader ReplyActionHeader10 + { + get + { + if (replyActionHeader10 == null) + { + replyActionHeader10 = + ActionHeader.Create(replyAction, AddressingVersion.WSAddressing10); + } + + return replyActionHeader10; + } + } + + //ActionHeader ReplyActionHeaderAugust2004 + //{ + // get + // { + // if (replyActionHeaderAugust2004 == null) + // { + // replyActionHeaderAugust2004 = + // ActionHeader.Create(this.replyAction, AddressingVersion.WSAddressingAugust2004); + // } + + // return replyActionHeaderAugust2004; + // } + //} + + static XmlDictionaryString AddToDictionary(XmlDictionary dictionary, string s) + { + XmlDictionaryString dictionaryString; + if (!dictionary.TryLookup(s, out dictionaryString)) + { + dictionaryString = dictionary.Add(s); + } + return dictionaryString; + } + + static PartInfo[] AddToDictionary(XmlDictionary dictionary, MessagePartDescriptionCollection parts, bool isRpc) + { + PartInfo[] partInfos = new PartInfo[parts.Count]; + for (int i = 0; i < parts.Count; i++) + { + partInfos[i] = AddToDictionary(dictionary, parts[i], isRpc); + } + return partInfos; + } + + ActionHeader GetActionHeader(AddressingVersion addressing) + { + if (action == null) + { + return null; + } + + //if (addressing == AddressingVersion.WSAddressingAugust2004) + //{ + // return ActionHeaderAugust2004; + //} + //else + if (addressing == AddressingVersion.WSAddressing10) + { + return ActionHeader10; + } + else if (addressing == AddressingVersion.None) + { + return ActionHeaderNone; + } + else + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new InvalidOperationException(SR.Format(SR.AddressingVersionNotSupported, addressing))); + } + } + + ActionHeader GetReplyActionHeader(AddressingVersion addressing) + { + if (replyAction == null) + { + return null; + } + + //if (addressing == AddressingVersion.WSAddressingAugust2004) + //{ + // return ReplyActionHeaderAugust2004; + //} + //else + if (addressing == AddressingVersion.WSAddressing10) + { + return ReplyActionHeader10; + } + else if (addressing == AddressingVersion.None) + { + return ReplyActionHeaderNone; + } + else + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new InvalidOperationException(SR.Format(SR.AddressingVersionNotSupported, addressing))); + } + } + + static string GetArrayItemName(Type type) + { + switch (type.GetTypeCode()) + { + case TypeCode.Boolean: + return "boolean"; + case TypeCode.DateTime: + return "dateTime"; + case TypeCode.Decimal: + return "decimal"; + case TypeCode.Int32: + return "int"; + case TypeCode.Int64: + return "long"; + case TypeCode.Single: + return "float"; + case TypeCode.Double: + return "double"; + default: + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.SFxInvalidUseOfPrimitiveOperationFormatter)); + } + } + + static PartInfo AddToDictionary(XmlDictionary dictionary, MessagePartDescription part, bool isRpc) + { + Type type = part.Type; + XmlDictionaryString itemName = null; + XmlDictionaryString itemNamespace = null; + if (type.IsArray && type != typeof(byte[])) + { + const string ns = "http://schemas.microsoft.com/2003/10/Serialization/Arrays"; + string name = GetArrayItemName(type.GetElementType()); + itemName = AddToDictionary(dictionary, name); + itemNamespace = AddToDictionary(dictionary, ns); + } + return new PartInfo(part, + AddToDictionary(dictionary, part.Name), + AddToDictionary(dictionary, isRpc ? string.Empty : part.Namespace), + itemName, itemNamespace); + } + + public static bool IsContractSupported(OperationDescription description) + { + if (description == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("description"); + + OperationDescription operation = description; + MessageDescription requestMessage = description.Messages[0]; + MessageDescription responseMessage = null; + if (description.Messages.Count == 2) + responseMessage = description.Messages[1]; + + if (requestMessage.Headers.Count > 0) + return false; + if (requestMessage.Properties.Count > 0) + return false; + if (requestMessage.IsTypedMessage) + return false; + if (responseMessage != null) + { + if (responseMessage.Headers.Count > 0) + return false; + if (responseMessage.Properties.Count > 0) + return false; + if (responseMessage.IsTypedMessage) + return false; + } + if (!AreTypesSupported(requestMessage.Body.Parts)) + return false; + if (responseMessage != null) + { + if (!AreTypesSupported(responseMessage.Body.Parts)) + return false; + if (responseMessage.Body.ReturnValue != null && !IsTypeSupported(responseMessage.Body.ReturnValue)) + return false; + } + return true; + } + + static bool AreTypesSupported(MessagePartDescriptionCollection bodyDescriptions) + { + for (int i = 0; i < bodyDescriptions.Count; i++) + if (!IsTypeSupported(bodyDescriptions[i])) + return false; + return true; + } + + static bool IsTypeSupported(MessagePartDescription bodyDescription) + { + Fx.Assert(bodyDescription != null, ""); + Type type = bodyDescription.Type; + if (type == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxMessagePartDescriptionMissingType, bodyDescription.Name, bodyDescription.Namespace))); + + if (bodyDescription.Multiple) + return false; + + if (type == typeof(void)) + return true; + if (type.GetTypeInfo().IsEnum) + return false; + switch (type.GetTypeCode()) + { + case TypeCode.Boolean: + case TypeCode.DateTime: + case TypeCode.Decimal: + case TypeCode.Double: + case TypeCode.Int32: + case TypeCode.Int64: + case TypeCode.Single: + case TypeCode.String: + return true; + case TypeCode.Object: + if (type.IsArray && type.GetArrayRank() == 1 && IsArrayTypeSupported(type.GetElementType())) + return true; + break; + default: + break; + } + return false; + } + + static bool IsArrayTypeSupported(Type type) + { + if (type.GetTypeInfo().IsEnum) + return false; + switch (type.GetTypeCode()) + { + case TypeCode.Byte: + case TypeCode.Boolean: + case TypeCode.DateTime: + case TypeCode.Decimal: + case TypeCode.Int32: + case TypeCode.Int64: + case TypeCode.Single: + case TypeCode.Double: + return true; + default: + return false; + } + } + + public Message SerializeRequest(MessageVersion messageVersion, object[] parameters) + { + if (messageVersion == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("messageVersion"); + + if (parameters == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("parameters"); + + return Message.CreateMessage(messageVersion, GetActionHeader(messageVersion.Addressing), new PrimitiveRequestBodyWriter(parameters, this)); + } + + public Message SerializeReply(MessageVersion messageVersion, object[] parameters, object result) + { + if (messageVersion == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("messageVersion"); + + if (parameters == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("parameters"); + + return Message.CreateMessage(messageVersion, GetReplyActionHeader(messageVersion.Addressing), new PrimitiveResponseBodyWriter(parameters, result, this)); + } + + public object DeserializeReply(Message message, object[] parameters) + { + if (message == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException(nameof(message))); + if (parameters == null) + throw TraceUtility.ThrowHelperError(new ArgumentNullException(nameof(parameters)), message); + try + { + if (message.IsEmpty) + { + if (responseWrapperName == null) + return null; + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SerializationException(SR.SFxInvalidMessageBodyEmptyMessage)); + } + + XmlDictionaryReader bodyReader = message.GetReaderAtBodyContents(); + using (bodyReader) + { + object returnValue = DeserializeResponse(bodyReader, parameters); + message.ReadFromBodyContentsToEnd(bodyReader); + return returnValue; + } + } + catch (XmlException xe) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException( + SR.Format(SR.SFxErrorDeserializingReplyBodyMore, operation.Name, xe.Message), xe)); + } + catch (FormatException fe) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException( + SR.Format(SR.SFxErrorDeserializingReplyBodyMore, operation.Name, fe.Message), fe)); + } + catch (SerializationException se) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException( + SR.Format(SR.SFxErrorDeserializingReplyBodyMore, operation.Name, se.Message), se)); + } + } + + public void DeserializeRequest(Message message, object[] parameters) + { + if (message == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException(nameof(message))); + if (parameters == null) + throw TraceUtility.ThrowHelperError(new ArgumentNullException(nameof(parameters)), message); + try + { + if (message.IsEmpty) + { + if (requestWrapperName == null) + return; + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SerializationException(SR.SFxInvalidMessageBodyEmptyMessage)); + } + + XmlDictionaryReader bodyReader = message.GetReaderAtBodyContents(); + using (bodyReader) + { + DeserializeRequest(bodyReader, parameters); + message.ReadFromBodyContentsToEnd(bodyReader); + } + } + catch (XmlException xe) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + OperationFormatter.CreateDeserializationFailedFault( + SR.Format(SR.SFxErrorDeserializingRequestBodyMore, operation.Name, xe.Message), + xe)); + } + catch (FormatException fe) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + OperationFormatter.CreateDeserializationFailedFault( + SR.Format(SR.SFxErrorDeserializingRequestBodyMore, operation.Name, fe.Message), + fe)); + } + catch (SerializationException se) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException( + SR.Format(SR.SFxErrorDeserializingRequestBodyMore, operation.Name, se.Message), + se)); + } + } + + void DeserializeRequest(XmlDictionaryReader reader, object[] parameters) + { + if (requestWrapperName != null) + { + if (!reader.IsStartElement(requestWrapperName, requestWrapperNamespace)) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SerializationException(SR.Format(SR.SFxInvalidMessageBody, requestWrapperName, requestWrapperNamespace, reader.NodeType, reader.Name, reader.NamespaceURI))); + bool isEmptyElement = reader.IsEmptyElement; + reader.Read(); + if (isEmptyElement) + { + return; + } + } + + DeserializeParameters(reader, requestParts, parameters); + + if (requestWrapperName != null) + { + reader.ReadEndElement(); + } + } + + object DeserializeResponse(XmlDictionaryReader reader, object[] parameters) + { + if (responseWrapperName != null) + { + if (!reader.IsStartElement(responseWrapperName, responseWrapperNamespace)) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SerializationException(SR.Format(SR.SFxInvalidMessageBody, responseWrapperName, responseWrapperNamespace, reader.NodeType, reader.Name, reader.NamespaceURI))); + bool isEmptyElement = reader.IsEmptyElement; + reader.Read(); + if (isEmptyElement) + { + return null; + } + } + + object returnValue = null; + if (returnPart != null) + { + while (true) + { + if (IsPartElement(reader, returnPart)) + { + returnValue = DeserializeParameter(reader, returnPart); + break; + } + if (!reader.IsStartElement()) + break; + if (IsPartElements(reader, responseParts)) + break; + OperationFormatter.TraceAndSkipElement(reader); + } + } + DeserializeParameters(reader, responseParts, parameters); + + if (responseWrapperName != null) + { + reader.ReadEndElement(); + } + + return returnValue; + } + + + void DeserializeParameters(XmlDictionaryReader reader, PartInfo[] parts, object[] parameters) + { + if (parts.Length != parameters.Length) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new ArgumentException(SR.Format(SR.SFxParameterCountMismatch, "parts", parts.Length, "parameters", parameters.Length), nameof(parameters))); + + int nextPartIndex = 0; + while (reader.IsStartElement()) + { + for (int i = nextPartIndex; i < parts.Length; i++) + { + PartInfo part = parts[i]; + if (IsPartElement(reader, part)) + { + parameters[part.Description.Index] = DeserializeParameter(reader, parts[i]); + nextPartIndex = i + 1; + } + else + parameters[part.Description.Index] = null; + } + + if (reader.IsStartElement()) + OperationFormatter.TraceAndSkipElement(reader); + } + } + + private bool IsPartElements(XmlDictionaryReader reader, PartInfo[] parts) + { + foreach (PartInfo part in parts) + if (IsPartElement(reader, part)) + return true; + return false; + } + + bool IsPartElement(XmlDictionaryReader reader, PartInfo part) + { + return reader.IsStartElement(part.DictionaryName, part.DictionaryNamespace); + } + + object DeserializeParameter(XmlDictionaryReader reader, PartInfo part) + { + if (reader.AttributeCount > 0 && + reader.MoveToAttribute(xsiNilLocalName.Value, xsiNilNamespace.Value) && + reader.ReadContentAsBoolean()) + { + reader.Skip(); + return null; + } + return part.ReadValue(reader); + } + + void SerializeParameter(XmlDictionaryWriter writer, PartInfo part, object graph) + { + + writer.WriteStartElement(part.DictionaryName, part.DictionaryNamespace); + if (graph == null) + { + writer.WriteStartAttribute(xsiNilLocalName, xsiNilNamespace); + writer.WriteValue(true); + writer.WriteEndAttribute(); + } + else + part.WriteValue(writer, graph); + writer.WriteEndElement(); + } + + void SerializeParameters(XmlDictionaryWriter writer, PartInfo[] parts, object[] parameters) + { + if (parts.Length != parameters.Length) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new ArgumentException(SR.Format(SR.SFxParameterCountMismatch, "parts", parts.Length, "parameters", parameters.Length), nameof(parameters))); + + + for (int i = 0; i < parts.Length; i++) + { + PartInfo part = parts[i]; + SerializeParameter(writer, part, parameters[part.Description.Index]); + } + } + + void SerializeRequest(XmlDictionaryWriter writer, object[] parameters) + { + if (requestWrapperName != null) + writer.WriteStartElement(requestWrapperName, requestWrapperNamespace); + + SerializeParameters(writer, requestParts, parameters); + + if (requestWrapperName != null) + writer.WriteEndElement(); + } + + void SerializeResponse(XmlDictionaryWriter writer, object returnValue, object[] parameters) + { + if (responseWrapperName != null) + writer.WriteStartElement(responseWrapperName, responseWrapperNamespace); + + if (returnPart != null) + SerializeParameter(writer, returnPart, returnValue); + + SerializeParameters(writer, responseParts, parameters); + + if (responseWrapperName != null) + writer.WriteEndElement(); + } + + class PartInfo + { + XmlDictionaryString dictionaryName; + XmlDictionaryString dictionaryNamespace; + XmlDictionaryString itemName; + XmlDictionaryString itemNamespace; + MessagePartDescription description; + TypeCode typeCode; + bool isArray; + + public PartInfo(MessagePartDescription description, XmlDictionaryString dictionaryName, XmlDictionaryString dictionaryNamespace, XmlDictionaryString itemName, XmlDictionaryString itemNamespace) + { + this.dictionaryName = dictionaryName; + this.dictionaryNamespace = dictionaryNamespace; + this.itemName = itemName; + this.itemNamespace = itemNamespace; + this.description = description; + if (description.Type.IsArray) + { + isArray = true; + typeCode = description.Type.GetElementType().GetTypeCode(); + } + else + { + isArray = false; + typeCode = description.Type.GetTypeCode(); + } + } + + public MessagePartDescription Description + { + get { return description; } + } + + public XmlDictionaryString DictionaryName + { + get { return dictionaryName; } + } + + public XmlDictionaryString DictionaryNamespace + { + get { return dictionaryNamespace; } + } + + public object ReadValue(XmlDictionaryReader reader) + { + object value; + if (isArray) + { + switch (typeCode) + { + case TypeCode.Byte: + value = reader.ReadElementContentAsBase64(); + break; + case TypeCode.Boolean: + if (!reader.IsEmptyElement) + { + reader.ReadStartElement(); + value = reader.ReadBooleanArray(itemName, itemNamespace); + reader.ReadEndElement(); + } + else + { + reader.Read(); + value = new bool[0]; + } + break; + case TypeCode.DateTime: + if (!reader.IsEmptyElement) + { + reader.ReadStartElement(); + value = reader.ReadDateTimeArray(itemName, itemNamespace); + reader.ReadEndElement(); + } + else + { + reader.Read(); + value = new DateTime[0]; + } + break; + case TypeCode.Decimal: + if (!reader.IsEmptyElement) + { + reader.ReadStartElement(); + value = reader.ReadDecimalArray(itemName, itemNamespace); + reader.ReadEndElement(); + } + else + { + reader.Read(); + value = new decimal[0]; + } + break; + case TypeCode.Int32: + if (!reader.IsEmptyElement) + { + reader.ReadStartElement(); + value = reader.ReadInt32Array(itemName, itemNamespace); + reader.ReadEndElement(); + } + else + { + reader.Read(); + value = new int[0]; + } + break; + case TypeCode.Int64: + if (!reader.IsEmptyElement) + { + reader.ReadStartElement(); + value = reader.ReadInt64Array(itemName, itemNamespace); + reader.ReadEndElement(); + } + else + { + reader.Read(); + value = new long[0]; + } + break; + case TypeCode.Single: + if (!reader.IsEmptyElement) + { + reader.ReadStartElement(); + value = reader.ReadSingleArray(itemName, itemNamespace); + reader.ReadEndElement(); + } + else + { + reader.Read(); + value = new float[0]; + } + break; + case TypeCode.Double: + if (!reader.IsEmptyElement) + { + reader.ReadStartElement(); + value = reader.ReadDoubleArray(itemName, itemNamespace); + reader.ReadEndElement(); + } + else + { + reader.Read(); + value = new double[0]; + } + break; + default: + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.SFxInvalidUseOfPrimitiveOperationFormatter)); + } + } + else + { + switch (typeCode) + { + case TypeCode.Boolean: + value = reader.ReadElementContentAsBoolean(); + break; + case TypeCode.DateTime: + value = reader.ReadElementContentAsDateTime(); + break; + case TypeCode.Decimal: + value = reader.ReadElementContentAsDecimal(); + break; + case TypeCode.Double: + value = reader.ReadElementContentAsDouble(); + break; + case TypeCode.Int32: + value = reader.ReadElementContentAsInt(); + break; + case TypeCode.Int64: + value = reader.ReadElementContentAsLong(); + break; + case TypeCode.Single: + value = reader.ReadElementContentAsFloat(); + break; + case TypeCode.String: + return reader.ReadElementContentAsString(); + default: + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.SFxInvalidUseOfPrimitiveOperationFormatter)); + } + } + return value; + } + + public void WriteValue(XmlDictionaryWriter writer, object value) + { + if (isArray) + { + switch (typeCode) + { + case TypeCode.Byte: + { + byte[] arrayValue = (byte[])value; + writer.WriteBase64(arrayValue, 0, arrayValue.Length); + } + break; + case TypeCode.Boolean: + { + bool[] arrayValue = (bool[])value; + writer.WriteArray(null, itemName, itemNamespace, arrayValue, 0, arrayValue.Length); + } + break; + case TypeCode.DateTime: + { + DateTime[] arrayValue = (DateTime[])value; + writer.WriteArray(null, itemName, itemNamespace, arrayValue, 0, arrayValue.Length); + } + break; + case TypeCode.Decimal: + { + decimal[] arrayValue = (decimal[])value; + writer.WriteArray(null, itemName, itemNamespace, arrayValue, 0, arrayValue.Length); + } + break; + case TypeCode.Int32: + { + int[] arrayValue = (int[])value; + writer.WriteArray(null, itemName, itemNamespace, arrayValue, 0, arrayValue.Length); + } + break; + case TypeCode.Int64: + { + long[] arrayValue = (long[])value; + writer.WriteArray(null, itemName, itemNamespace, arrayValue, 0, arrayValue.Length); + } + break; + case TypeCode.Single: + { + float[] arrayValue = (float[])value; + writer.WriteArray(null, itemName, itemNamespace, arrayValue, 0, arrayValue.Length); + } + break; + case TypeCode.Double: + { + double[] arrayValue = (double[])value; + writer.WriteArray(null, itemName, itemNamespace, arrayValue, 0, arrayValue.Length); + } + break; + default: + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.SFxInvalidUseOfPrimitiveOperationFormatter)); + } + } + else + { + switch (typeCode) + { + case TypeCode.Boolean: + writer.WriteValue((bool)value); + break; + case TypeCode.DateTime: + writer.WriteValue((DateTime)value); + break; + case TypeCode.Decimal: + writer.WriteValue((decimal)value); + break; + case TypeCode.Double: + writer.WriteValue((double)value); + break; + case TypeCode.Int32: + writer.WriteValue((int)value); + break; + case TypeCode.Int64: + writer.WriteValue((long)value); + break; + case TypeCode.Single: + writer.WriteValue((float)value); + break; + case TypeCode.String: + writer.WriteString((string)value); + break; + default: + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.SFxInvalidUseOfPrimitiveOperationFormatter)); + } + } + } + } + + class PrimitiveRequestBodyWriter : BodyWriter + { + object[] parameters; + PrimitiveOperationFormatter primitiveOperationFormatter; + + public PrimitiveRequestBodyWriter(object[] parameters, PrimitiveOperationFormatter primitiveOperationFormatter) + : base(true) + { + this.parameters = parameters; + this.primitiveOperationFormatter = primitiveOperationFormatter; + } + + protected override void OnWriteBodyContents(XmlDictionaryWriter writer) + { + primitiveOperationFormatter.SerializeRequest(writer, parameters); + } + } + + class PrimitiveResponseBodyWriter : BodyWriter + { + object[] parameters; + object returnValue; + PrimitiveOperationFormatter primitiveOperationFormatter; + + public PrimitiveResponseBodyWriter(object[] parameters, object returnValue, + PrimitiveOperationFormatter primitiveOperationFormatter) + : base(true) + { + this.parameters = parameters; + this.returnValue = returnValue; + this.primitiveOperationFormatter = primitiveOperationFormatter; + } + + protected override void OnWriteBodyContents(XmlDictionaryWriter writer) + { + primitiveOperationFormatter.SerializeResponse(writer, returnValue, parameters); + } + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ProxyOperationRuntime.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ProxyOperationRuntime.cs new file mode 100644 index 000000000..b2ad8c0cc --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ProxyOperationRuntime.cs @@ -0,0 +1,418 @@ +using System; +using System.Collections.ObjectModel; +using System.Diagnostics.Contracts; +using System.Reflection; +using System.Security; +using CoreWCF.Runtime; +using CoreWCF.Channels; +using CoreWCF.Description; + +namespace CoreWCF.Dispatcher +{ + internal class ProxyOperationRuntime + { + static internal readonly ParameterInfo[] NoParams = new ParameterInfo[0]; + static internal readonly object[] EmptyArray = new object[0]; + + readonly IClientMessageFormatter _formatter; + readonly bool _isInitiating; + readonly bool _isOneWay; + readonly bool _isTerminating; + readonly bool _isSessionOpenNotificationEnabled; + readonly string _name; + readonly IParameterInspector[] _parameterInspectors; + readonly IClientFaultFormatter _faultFormatter; + readonly ImmutableClientRuntime _parent; + bool _serializeRequest; + bool _deserializeReply; + string _action; + string _replyAction; + + MethodInfo _beginMethod; + MethodInfo _syncMethod; + MethodInfo _taskMethod; + ParameterInfo[] _inParams; + ParameterInfo[] _outParams; + ParameterInfo[] _endOutParams; + ParameterInfo _returnParam; + + internal ProxyOperationRuntime(ClientOperation operation, ImmutableClientRuntime parent) + { + if (operation == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("operation"); + if (parent == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("parent"); + + _parent = parent; + _formatter = operation.Formatter; + _isInitiating = operation.IsInitiating; + _isOneWay = operation.IsOneWay; + _isTerminating = operation.IsTerminating; + _isSessionOpenNotificationEnabled = operation.IsSessionOpenNotificationEnabled; + _name = operation.Name; + _parameterInspectors = EmptyArray.ToArray(operation.ParameterInspectors); + _faultFormatter = operation.FaultFormatter; + _serializeRequest = operation.SerializeRequest; + _deserializeReply = operation.DeserializeReply; + _action = operation.Action; + _replyAction = operation.ReplyAction; + _beginMethod = operation.BeginMethod; + _syncMethod = operation.SyncMethod; + _taskMethod = operation.TaskMethod; + TaskTResult = operation.TaskTResult; + + if (_beginMethod != null) + { + _inParams = ServiceReflector.GetInputParameters(_beginMethod, true); + if (_syncMethod != null) + { + _outParams = ServiceReflector.GetOutputParameters(_syncMethod, false); + } + else + { + _outParams = NoParams; + } + _endOutParams = ServiceReflector.GetOutputParameters(operation.EndMethod, true); + _returnParam = operation.EndMethod.ReturnParameter; + } + else if (_syncMethod != null) + { + _inParams = ServiceReflector.GetInputParameters(_syncMethod, false); + _outParams = ServiceReflector.GetOutputParameters(_syncMethod, false); + _returnParam = _syncMethod.ReturnParameter; + } + + if (_formatter == null && (_serializeRequest || _deserializeReply)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.ClientRuntimeRequiresFormatter0, _name))); + } + } + + internal string Action + { + get { return _action; } + } + + internal IClientFaultFormatter FaultFormatter + { + get { return _faultFormatter; } + } + + internal bool IsInitiating + { + get { return _isInitiating; } + } + + internal bool IsOneWay + { + get { return _isOneWay; } + } + + internal bool IsTerminating + { + get { return _isTerminating; } + } + + internal bool IsSessionOpenNotificationEnabled + { + get { return _isSessionOpenNotificationEnabled; } + } + + internal string Name + { + get { return _name; } + } + + internal ImmutableClientRuntime Parent + { + get { return _parent; } + } + + internal string ReplyAction + { + get { return _replyAction; } + } + + internal bool DeserializeReply + { + get { return _deserializeReply; } + } + + internal bool SerializeRequest + { + get { return _serializeRequest; } + } + + internal Type TaskTResult + { + get; + set; + } + + internal void AfterReply(ref ProxyRpc rpc) + { + if (!_isOneWay) + { + Message reply = rpc.Reply; + + if (_deserializeReply) + { + //if (TD.ClientFormatterDeserializeReplyStartIsEnabled()) + //{ + // TD.ClientFormatterDeserializeReplyStart(rpc.EventTraceActivity); + //} + + rpc.ReturnValue = _formatter.DeserializeReply(reply, rpc.OutputParameters); + + //if (TD.ClientFormatterDeserializeReplyStopIsEnabled()) + //{ + // TD.ClientFormatterDeserializeReplyStop(rpc.EventTraceActivity); + //} + + } + else + { + rpc.ReturnValue = reply; + } + + int offset = _parent.ParameterInspectorCorrelationOffset; + try + { + for (int i = _parameterInspectors.Length - 1; i >= 0; i--) + { + _parameterInspectors[i].AfterCall(_name, + rpc.OutputParameters, + rpc.ReturnValue, + rpc.Correlation[offset + i]); + //if (TD.ClientParameterInspectorAfterCallInvokedIsEnabled()) + //{ + // TD.ClientParameterInspectorAfterCallInvoked(rpc.EventTraceActivity, this._parameterInspectors[i].GetType().FullName); + //} + } + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + if (ErrorBehavior.ShouldRethrowClientSideExceptionAsIs(e)) + { + throw; + } + throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(e); + } + + if (_parent.ValidateMustUnderstand) + { + Collection headersNotUnderstood = reply.Headers.GetHeadersNotUnderstood(); + if (headersNotUnderstood != null && headersNotUnderstood.Count > 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ProtocolException(SR.Format(SR.SFxHeaderNotUnderstood, headersNotUnderstood[0].Name, headersNotUnderstood[0].Namespace))); + } + } + } + } + + internal void BeforeRequest(ref ProxyRpc rpc) + { + int offset = _parent.ParameterInspectorCorrelationOffset; + try + { + for (int i = 0; i < _parameterInspectors.Length; i++) + { + rpc.Correlation[offset + i] = _parameterInspectors[i].BeforeCall(_name, rpc.InputParameters); + //if (TD.ClientParameterInspectorBeforeCallInvokedIsEnabled()) + //{ + // TD.ClientParameterInspectorBeforeCallInvoked(rpc.EventTraceActivity, this._parameterInspectors[i].GetType().FullName); + //} + } + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + if (ErrorBehavior.ShouldRethrowClientSideExceptionAsIs(e)) + { + throw; + } + throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(e); + } + + if (_serializeRequest) + { + //if (TD.ClientFormatterSerializeRequestStartIsEnabled()) + //{ + // TD.ClientFormatterSerializeRequestStart(rpc.EventTraceActivity); + //} + + rpc.Request = _formatter.SerializeRequest(rpc.MessageVersion, rpc.InputParameters); + + + + //if (TD.ClientFormatterSerializeRequestStopIsEnabled()) + //{ + // TD.ClientFormatterSerializeRequestStop(rpc.EventTraceActivity); + //} + } + else + { + if (rpc.InputParameters[0] == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxProxyRuntimeMessageCannotBeNull, _name))); + } + + rpc.Request = (Message)rpc.InputParameters[0]; + if (!IsValidAction(rpc.Request, Action)) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxInvalidRequestAction, Name, rpc.Request.Headers.Action ?? "{NULL}", Action))); + } + } + + internal static object GetDefaultParameterValue(Type parameterType) + { + return (parameterType.GetTypeInfo().IsValueType && parameterType != typeof(void)) ? Activator.CreateInstance(parameterType) : null; + } + + internal bool IsSyncCall(MethodCall methodCall) + { + if (_syncMethod == null) + { + return false; + } + + Contract.Assert(methodCall != null); + Contract.Assert(methodCall.MethodBase != null); + return methodCall.MethodBase.Equals(_syncMethod); + } + + internal bool IsBeginCall(MethodCall methodCall) + { + if (_beginMethod == null) + { + return false; + } + + Contract.Assert(methodCall != null); + Contract.Assert(methodCall.MethodBase != null); + return methodCall.MethodBase.Equals(_beginMethod); + } + + internal bool IsTaskCall(MethodCall methodCall) + { + if (_taskMethod == null) + { + return false; + } + + Contract.Assert(methodCall != null); + Contract.Assert(methodCall.MethodBase != null); + return methodCall.MethodBase.Equals(_taskMethod); + } + + internal object[] MapSyncInputs(MethodCall methodCall, out object[] outs) + { + if (_outParams.Length == 0) + { + outs = Array.Empty(); + } + else + { + outs = new object[_outParams.Length]; + } + if (_inParams.Length == 0) + return Array.Empty(); + return methodCall.Args; + } + + internal object[] MapAsyncBeginInputs(MethodCall methodCall, out AsyncCallback callback, out object asyncState) + { + object[] ins; + if (_inParams.Length == 0) + { + ins = Array.Empty(); + } + else + { + ins = new object[_inParams.Length]; + } + + object[] args = methodCall.Args; + for (int i = 0; i < ins.Length; i++) + { + ins[i] = args[_inParams[i].Position]; + } + + callback = args[methodCall.Args.Length - 2] as AsyncCallback; + asyncState = args[methodCall.Args.Length - 1]; + return ins; + } + + internal void MapAsyncEndInputs(MethodCall methodCall, out IAsyncResult result, out object[] outs) + { + outs = new object[_endOutParams.Length]; + result = methodCall.Args[methodCall.Args.Length - 1] as IAsyncResult; + } + + internal object[] MapSyncOutputs(MethodCall methodCall, object[] outs, ref object ret) + { + return MapOutputs(_outParams, methodCall, outs, ref ret); + } + + internal object[] MapAsyncOutputs(MethodCall methodCall, object[] outs, ref object ret) + { + return MapOutputs(_endOutParams, methodCall, outs, ref ret); + } + + private object[] MapOutputs(ParameterInfo[] parameters, MethodCall methodCall, object[] outs, ref object ret) + { + if (ret == null && _returnParam != null) + { + ret = GetDefaultParameterValue(TypeLoader.GetParameterType(_returnParam)); + } + + if (parameters.Length == 0) + { + return null; + } + + object[] args = methodCall.Args; + for (int i = 0; i < parameters.Length; i++) + { + if (outs[i] == null) + { + // the RealProxy infrastructure requires a default value for value types + args[parameters[i].Position] = GetDefaultParameterValue(TypeLoader.GetParameterType(parameters[i])); + } + else + { + args[parameters[i].Position] = outs[i]; + } + } + + return args; + } + + static internal bool IsValidAction(Message message, string action) + { + if (message == null) + { + return false; + } + + if (message.IsFault) + { + return true; + } + + if (action == MessageHeaders.WildcardAction) + { + return true; + } + + return (string.CompareOrdinal(message.Headers.Action, action) == 0); + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ProxyRpc.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ProxyRpc.cs new file mode 100644 index 000000000..d237dd85b --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ProxyRpc.cs @@ -0,0 +1,62 @@ +using System; +using System.Threading; +using CoreWCF.Runtime; +using CoreWCF.Channels; + +namespace CoreWCF.Dispatcher +{ + struct ProxyRpc + { + internal readonly string Action; + //internal ServiceModelActivity Activity; + internal Guid ActivityId; + internal readonly ServiceChannel Channel; + internal object[] Correlation; + internal readonly object[] InputParameters; + internal readonly ProxyOperationRuntime Operation; + internal object[] OutputParameters; + internal Message Request; + internal Message Reply; + internal object ReturnValue; + internal MessageVersion MessageVersion; + //internal readonly TimeoutHelper TimeoutHelper; + internal CancellationToken CancellationToken; + //EventTraceActivity eventTraceActivity; + + internal ProxyRpc(ServiceChannel channel, ProxyOperationRuntime operation, string action, object[] inputs, CancellationToken token) + { + Action = action; + //this.Activity = null; + //this.eventTraceActivity = null; + Channel = channel; + Correlation = EmptyArray.Allocate(operation.Parent.CorrelationCount); + InputParameters = inputs; + Operation = operation; + OutputParameters = null; + Request = null; + Reply = null; + ActivityId = Guid.Empty; + ReturnValue = null; + MessageVersion = channel.MessageVersion; + CancellationToken = token; + } + + //internal EventTraceActivity EventTraceActivity + //{ + // get + // { + // if (this.eventTraceActivity == null) + // { + // this.eventTraceActivity = new EventTraceActivity(); + // } + // return this.eventTraceActivity; + // } + + // set + // { + // this.eventTraceActivity = value; + // } + //} + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/QueryUtil.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/QueryUtil.cs new file mode 100644 index 000000000..db8feaa47 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/QueryUtil.cs @@ -0,0 +1,599 @@ +using System; +using System.Collections.Generic; +using CoreWCF.Runtime; + +namespace CoreWCF.Dispatcher +{ + + // + // Generic struct representing ranges within buffers + // + internal struct QueryRange + { + internal int end; // INCLUSIVE - the end of the range + internal int start; // INCLUSIVE - the start of the range + internal QueryRange(int start, int end) + { + this.start = start; + this.end = end; + } + + internal int Count + { + get + { + return end - start + 1; + } + } + internal bool IsInRange(int point) + { + return (start <= point && point <= end); + } + internal void Shift(int offset) + { + start += offset; + end += offset; + } + } + + /// + /// Our own buffer management + /// There are a few reasons why we don't reuse something in System.Collections.Generic + /// 1. We want Clear() to NOT reallocate the internal array. We want it to simply set the Count = 0 + /// This allows us to reuse buffers with impunity. + /// 2. We want to be able to replace the internal buffer in a collection with a different one. Again, + /// this is to help with pooling + /// 3. We want to be able to control how fast buffers grow. + /// 4. Does absolutely no bounds or null checking. As fast as we can make it. All checking should be done + /// by whoever wraps this. Checking is unnecessary for many internal uses where we need optimal perf. + /// 5. Does more precise trimming + /// 6. AND this is a struct + /// + /// + internal struct QueryBuffer + { + internal T[] buffer; // buffer of T. Frequently larger than count + internal int count; // Actual # of items + internal static T[] EmptyBuffer = new T[0]; + + /// + /// Construct a new buffer + /// + /// + internal QueryBuffer(int capacity) + { + if (0 == capacity) + { + buffer = QueryBuffer.EmptyBuffer; + } + else + { + buffer = new T[capacity]; + } + count = 0; + } + /// + /// # of items + /// + internal int Count + { + get + { + return count; + } + } + + + internal T this[int index] + { + get + { + return buffer[index]; + } + set + { + buffer[index] = value; + } + } + + + /// + /// Add an element to the buffer + /// + internal void Add(T t) + { + if (count == buffer.Length) + { + Array.Resize(ref buffer, count > 0 ? count * 2 : 16); + } + buffer[count++] = t; + } + + + /// + /// Add all the elements in the given buffer to this one + /// We can do this very efficiently using an Array Copy + /// + internal void Add(ref QueryBuffer addBuffer) + { + if (1 == addBuffer.count) + { + Add(addBuffer.buffer[0]); + return; + } + + int newCount = count + addBuffer.count; + if (newCount >= buffer.Length) + { + Grow(newCount); + } + // Copy all the new elements in + Array.Copy(addBuffer.buffer, 0, buffer, count, addBuffer.count); + count = newCount; + } + + + /// + /// Set the count to zero but do NOT get rid of the actual buffer + /// + internal void Clear() + { + count = 0; + } + + + internal void CopyFrom(ref QueryBuffer addBuffer) + { + int addCount = addBuffer.count; + switch (addCount) + { + default: + if (addCount > buffer.Length) + { + buffer = new T[addCount]; + } + // Copy all the new elements in + Array.Copy(addBuffer.buffer, 0, buffer, 0, addCount); + count = addCount; + break; + + case 0: + count = 0; + break; + + case 1: + if (buffer.Length == 0) + { + buffer = new T[1]; + } + buffer[0] = addBuffer.buffer[0]; + count = 1; + break; + } + } + + internal void CopyTo(T[] dest) + { + Array.Copy(buffer, dest, count); + } + + void Grow(int capacity) + { + int newCapacity = buffer.Length * 2; + Array.Resize(ref buffer, capacity > newCapacity ? capacity : newCapacity); + } + + internal int IndexOf(T t) + { + for (int i = 0; i < count; ++i) + { + if (t.Equals(buffer[i])) + { + return i; + } + } + return -1; + } + + internal int IndexOf(T t, int startAt) + { + for (int i = startAt; i < count; ++i) + { + if (t.Equals(buffer[i])) + { + return i; + } + } + return -1; + } + internal bool IsValidIndex(int index) + { + return (index >= 0 && index < count); + } + + /// + /// Reserve enough space for count elements + /// + internal void Reserve(int reserveCount) + { + int newCount = count + reserveCount; + if (newCount >= buffer.Length) + { + Grow(newCount); + } + count = newCount; + } + + internal void ReserveAt(int index, int reserveCount) + { + if (index == count) + { + Reserve(reserveCount); + return; + } + + int newCount; + if (index > count) + { + // We want to reserve starting at a location past what is current committed. + // No shifting needed + newCount = index + reserveCount + 1; + if (newCount >= buffer.Length) + { + Grow(newCount); + } + } + else + { + // reserving space within an already allocated portion of the buffer + // we'll ensure that the buffer can fit 'newCount' items, then shift by reserveCount starting at index + newCount = count + reserveCount; + if (newCount >= buffer.Length) + { + Grow(newCount); + } + // Move to make room + Array.Copy(buffer, index, buffer, index + reserveCount, count - index); + } + count = newCount; + } + + internal void Remove(T t) + { + int index = IndexOf(t); + if (index >= 0) + { + RemoveAt(index); + } + } + + internal void RemoveAt(int index) + { + if (index < count - 1) + { + Array.Copy(buffer, index + 1, buffer, index, count - index - 1); + } + count--; + } + + internal void Sort(IComparer comparer) + { + Array.Sort(buffer, 0, count, comparer); + } + + /// + /// Reduce the buffer capacity so that its size is exactly == to the element count + /// + internal void TrimToCount() + { + if (count < buffer.Length) + { + if (0 == count) + { + buffer = QueryBuffer.EmptyBuffer; + } + else + { + T[] newBuffer = new T[count]; + Array.Copy(buffer, newBuffer, count); + } + } + } + } + + internal struct SortedBuffer + where C : IComparer + { + int size; + T[] buffer; + static DefaultComparer Comparer; + + internal SortedBuffer(C comparerInstance) + { + size = 0; + buffer = null; + + if (Comparer == null) + { + Comparer = new DefaultComparer(comparerInstance); + } + else + { + Fx.Assert(object.ReferenceEquals(DefaultComparer.Comparer, comparerInstance), "The SortedBuffer type has already been initialized with a different comparer instance."); + } + } + + internal T this[int index] + { + get + { + return GetAt(index); + } + } + + internal int Capacity + { + set + { + if (buffer != null) + { + if (value != buffer.Length) + { + Fx.Assert(value >= size, "New capacity must be >= size"); + if (value > 0) + { + Array.Resize(ref buffer, value); + } + else + { + buffer = null; + } + } + } + else + { + buffer = new T[value]; + } + } + } + + internal int Count + { + get + { + return size; + } + } + + internal int Add(T item) + { + int i = Search(item); + + if (i < 0) + { + i = ~i; + InsertAt(i, item); + } + + return i; + } + + internal void Clear() + { + size = 0; + } + internal void Exchange(T old, T replace) + { + if (Comparer.Compare(old, replace) == 0) + { + int i = IndexOf(old); + if (i >= 0) + { + buffer[i] = replace; + } + else + { + Insert(replace); + } + } + else + { + // PERF, astern, can this be made more efficient? Does it need to be? + Remove(old); + Insert(replace); + } + } + + internal T GetAt(int index) + { + Fx.Assert(index < size, "Index is greater than size"); + return buffer[index]; + } + + internal int IndexOf(T item) + { + return Search(item); + } + + internal int IndexOfKey(K key, IItemComparer itemComp) + { + return Search(key, itemComp); + } + + internal int Insert(T item) + { + int i = Search(item); + + if (i >= 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperCritical(new ArgumentException(SR.QueryItemAlreadyExists)); + } + + // If an item is not found, Search returns the bitwise negation of + // the index an item should inserted at; + InsertAt(~i, item); + return ~i; + } + + void InsertAt(int index, T item) + { + Fx.Assert(index >= 0 && index <= size, ""); + + if (buffer == null) + { + buffer = new T[1]; + } + else if (buffer.Length == size) + { + // PERF, astern, how should we choose a new size? + T[] tmp = new T[size + 1]; + + if (index == 0) + { + Array.Copy(buffer, 0, tmp, 1, size); + } + else if (index == size) + { + Array.Copy(buffer, 0, tmp, 0, size); + } + else + { + Array.Copy(buffer, 0, tmp, 0, index); + Array.Copy(buffer, index, tmp, index + 1, size - index); + } + + buffer = tmp; + } + else + { + Array.Copy(buffer, index, buffer, index + 1, size - index); + } + + buffer[index] = item; + ++size; + } + + internal bool Remove(T item) + { + int i = IndexOf(item); + + if (i >= 0) + { + RemoveAt(i); + return true; + } + + return false; + } + + internal void RemoveAt(int index) + { + Fx.Assert(index >= 0 && index < size, ""); + + if (index < size - 1) + { + Array.Copy(buffer, index + 1, buffer, index, size - index - 1); + } + + buffer[--size] = default(T); + } + + int Search(T item) + { + if (size == 0) + return ~0; + return Search(item, Comparer); + } + + int Search(K key, IItemComparer comparer) + { + if (size <= 8) + { + return LinearSearch(key, comparer, 0, size); + } + else + { + return BinarySearch(key, comparer); + } + } + + int BinarySearch(K key, IItemComparer comparer) + { + // [low, high) + int low = 0; + int high = size; + int mid, result; + + // Binary search is implemented here so we could look for a type that is different from the + // buffer type. Also, the search switches to linear for 8 or fewer elements. + while (high - low > 8) + { + mid = (high + low) / 2; + result = comparer.Compare(key, buffer[mid]); + if (result < 0) + { + high = mid; + } + else if (result > 0) + { + low = mid + 1; + } + else + { + return mid; + } + } + + return LinearSearch(key, comparer, low, high); + } + + // [start, bound) + int LinearSearch(K key, IItemComparer comparer, int start, int bound) + { + int result; + + for (int i = start; i < bound; ++i) + { + result = comparer.Compare(key, buffer[i]); + if (result == 0) + { + return i; + } + + if (result < 0) + { + // Return the bitwise negation of the insertion index + return ~i; + } + } + + // Return the bitwise negation of the insertion index + return ~bound; + } + internal void Trim() + { + Capacity = size; + } + + internal class DefaultComparer : IItemComparer + { + public static IComparer Comparer; + + public DefaultComparer(C comparer) + { + Comparer = comparer; + } + + public int Compare(T item1, T item2) + { + return Comparer.Compare(item1, item2); + } + } + } + + internal interface IItemComparer + { + int Compare(K key, V value); + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ReceiveContextAcknowledgementMode.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ReceiveContextAcknowledgementMode.cs new file mode 100644 index 000000000..745491d55 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ReceiveContextAcknowledgementMode.cs @@ -0,0 +1,9 @@ +namespace CoreWCF.Dispatcher +{ + internal enum ReceiveContextAcknowledgementMode + { + AutoAcknowledgeOnReceive = 0, + AutoAcknowledgeOnRPCComplete = 1, + ManualAcknowledgement = 2 + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ReplyChannelBinder.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ReplyChannelBinder.cs new file mode 100644 index 000000000..4095c7723 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ReplyChannelBinder.cs @@ -0,0 +1,102 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using CoreWCF.Runtime; +using CoreWCF.Channels; +using CoreWCF.Diagnostics; + +namespace CoreWCF.Dispatcher +{ + internal class ReplyChannelBinder : IChannelBinder + { + IReplyChannel channel; + Uri listenUri; + bool initialized = false; + + public ReplyChannelBinder() { } + + internal void Init(IReplyChannel channel, Uri listenUri) + { + if (initialized) + { + Fx.Assert(this.channel == channel, "Wrong channel when calling Init"); + Fx.Assert(this.listenUri == listenUri, "Wrong listenUri when calling Init"); + return; + } + + if (channel == null) + { + Fx.Assert("ReplyChannelBinder.ReplyChannelBinder: (channel != null)"); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("channel"); + } + this.channel = channel; + this.listenUri = listenUri; + initialized = true; + } + + public IChannel Channel + { + get { return channel; } + } + + public bool HasSession + { + get { return channel is ISessionChannel; } + } + + public Uri ListenUri + { + get { return listenUri; } + } + + public EndpointAddress LocalAddress + { + get { return channel.LocalAddress; } + } + + public EndpointAddress RemoteAddress + { + get + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException()); + } + } + + public void Abort() + { + channel.Abort(); + } + + public void CloseAfterFault(TimeSpan timeout) + { + var helper = new TimeoutHelper(timeout); + channel.CloseAsync(helper.GetCancellationToken()).GetAwaiter().GetResult(); + } + + public RequestContext CreateRequestContext(Message message) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException()); + } + + public Task SendAsync(Message message, CancellationToken token) + { + throw TraceUtility.ThrowHelperError(new NotImplementedException(), message); + } + + public Task> TryReceiveAsync(CancellationToken token) + { + return channel.TryReceiveRequestAsync(token); + } + + public Task RequestAsync(Message message, CancellationToken token) + { + throw TraceUtility.ThrowHelperError(new NotImplementedException(), message); + } + + public Task WaitForMessageAsync(CancellationToken token) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException()); + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ServiceDispatcher.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ServiceDispatcher.cs new file mode 100644 index 000000000..0be52fa44 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ServiceDispatcher.cs @@ -0,0 +1,107 @@ +using CoreWCF.Channels; +using CoreWCF.Configuration; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace CoreWCF.Dispatcher +{ + internal class ServiceDispatcher : IServiceDispatcher + { + private EndpointDispatcherTable _endpointDispatcherTable; + private IRequestReplyCorrelator _requestReplyCorrelator; + + public ServiceDispatcher(Uri baseAddress, Binding binding, EndpointDispatcherTable endpointDispatcherTable) + { + BaseAddress = baseAddress; + Binding = binding; + _endpointDispatcherTable = endpointDispatcherTable; + // TODO: Maybe make lazy + _requestReplyCorrelator = new RequestReplyCorrelator(); + } + + public Uri BaseAddress { get; } + + public Binding Binding { get; } + + public Task DispatchAsync(RequestContext request, IChannel channel, CancellationToken token) + { + bool dummy; + var endpointDispatcher = _endpointDispatcherTable.Lookup(request.RequestMessage, out dummy); + return DispatchAsyncCore(request, channel, endpointDispatcher, token); + } + + private Task DispatchAsyncCore(RequestContext request, IChannel channel, EndpointDispatcher endpointDispatcher, CancellationToken token) + { + var dispatchRuntime = endpointDispatcher.DispatchRuntime; + //EndpointDispatcher endpoint = dispatchRuntime.EndpointDispatcher; + //bool releasedPump = false; + + ServiceChannel serviceChannel = null; + var sessionIdleManager = channel.GetProperty(); + IChannelBinder binder = null; + if (channel is IReplyChannel) + { + var rcbinder = channel.GetProperty(); + rcbinder.Init(channel as IReplyChannel, BaseAddress); + binder = rcbinder; + } + else if (channel is IDuplexSessionChannel) + { + var dcbinder = channel.GetProperty(); + dcbinder.Init(channel as IDuplexSessionChannel, _requestReplyCorrelator, BaseAddress); + binder = dcbinder; + } + + serviceChannel = new ServiceChannel( + binder, + endpointDispatcher, + Binding, + sessionIdleManager.UseIfNeeded(binder, Binding.ReceiveTimeout)); + + Message message = request.RequestMessage; + DispatchOperationRuntime operation = dispatchRuntime.GetOperation(ref message); + if (operation == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "No DispatchOperationRuntime found to process message."))); + } + + // TODO: Wire in session open notification + //if (shouldRejectMessageWithOnOpenActionHeader && message.Headers.Action == OperationDescription.SessionOpenedAction) + //{ + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxNoEndpointMatchingAddressForConnectionOpeningMessage, message.Headers.Action, "Open"))); + //} + + // TODO: Session lifetime + //if (operation.IsTerminating && requestInfo.HasSession) + //{ + // isChannelTerminated = true; + //} + + // TODO: Fix up whatever semantics OperationContext places on a host being passed + var currentOperationContext = new OperationContext(request, message, serviceChannel, /*host*/ null); + + currentOperationContext.EndpointDispatcher = endpointDispatcher; + + var existingInstanceContext = dispatchRuntime.InstanceContextProvider.GetExistingInstanceContext(request.RequestMessage, serviceChannel.Proxy as IContextChannel); + // TODO: Investigate consequences of cleanThread parameter + MessageRpc rpc = new MessageRpc(request, message, operation, serviceChannel, /*host*/ null, + /*cleanThread*/ true, currentOperationContext, existingInstanceContext /*, eventTraceActivity*/); + + return operation.Parent.DispatchAsync(ref rpc, /*hasOperationContextBeenSet*/ false); + // TODO : Fix error handling + //catch (Exception e) + //{ + // if (Fx.IsFatal(e)) + // { + // throw; + // } + // return HandleError(e, request, channel); + //} + } + + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/SharedRuntimeState.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/SharedRuntimeState.cs new file mode 100644 index 000000000..eedde6373 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/SharedRuntimeState.cs @@ -0,0 +1,41 @@ +using System; + +namespace CoreWCF.Dispatcher +{ + internal class SharedRuntimeState + { + bool _isImmutable; + + internal SharedRuntimeState(bool isOnServer) + { + IsOnServer = isOnServer; + } + + internal bool EnableFaults { get; set; } = true; + + internal bool IsOnServer { get; } + + internal bool ManualAddressing { get; set; } + + internal bool ValidateMustUnderstand { get; set; } = true; + + internal void LockDownProperties() + { + _isImmutable = true; + } + + internal void ThrowIfImmutable() + { + if (_isImmutable) + { + if (IsOnServer) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.SFxImmutableServiceHostBehavior0)); + } + + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.SFxImmutableChannelFactoryBehavior0)); + } + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/SingletonInstanceContextProvider.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/SingletonInstanceContextProvider.cs new file mode 100644 index 000000000..958cb3231 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/SingletonInstanceContextProvider.cs @@ -0,0 +1,92 @@ +using System; +using CoreWCF.Channels; + +namespace CoreWCF.Dispatcher +{ + internal class SingletonInstanceContextProvider : InstanceContextProviderBase + { + InstanceContext singleton; + object thisLock; + + internal SingletonInstanceContextProvider(DispatchRuntime dispatchRuntime) + : base(dispatchRuntime) + { + thisLock = new object(); + } + + internal InstanceContext SingletonInstance + { + get + { + if (singleton == null) + { + lock (thisLock) + { + if (singleton == null) + { + InstanceContext instanceContext = DispatchRuntime.SingletonInstanceContext; + + if (instanceContext == null) + { + instanceContext = new InstanceContext(DispatchRuntime.ChannelDispatcher.Host, false); + instanceContext.OpenAsync().GetAwaiter().GetResult(); + } + else if (instanceContext.State != CommunicationState.Opened) + { + // we need to lock against the instance context for open since two different endpoints could + // share the same instance context, but different providers. So the provider lock does not guard + // the open process + lock (instanceContext.ThisLock) + { + if (instanceContext.State != CommunicationState.Opened) + { + instanceContext.OpenAsync().GetAwaiter().GetResult(); + } + } + } + + //Set the IsUsercreated flag to false for singleton mode even in cases when users create their own runtime. + instanceContext.IsUserCreated = false; + + //Delay assigning the potentially newly created InstanceContext (till after its opened) to this.Singleton + //to ensure that it is opened only once. + singleton = instanceContext; + } + } + } + return singleton; + } + } + + #region IInstanceContextProvider Members + + public override InstanceContext GetExistingInstanceContext(Message message, IContextChannel channel) + { + ServiceChannel serviceChannel = GetServiceChannelFromProxy(channel); + if (serviceChannel != null && serviceChannel.HasSession) + { + SingletonInstance.BindIncomingChannel(serviceChannel); + } + return SingletonInstance; + } + + public override void InitializeInstanceContext(InstanceContext instanceContext, Message message, IContextChannel channel) + { + //no-op + } + + public override bool IsIdle(InstanceContext instanceContext) + { + //By default return false + return false; + } + + public override void NotifyIdle(Action callback, InstanceContext instanceContext) + { + //no-op + } + + #endregion + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/StreamFormatter.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/StreamFormatter.cs new file mode 100644 index 000000000..5c1fbbd3d --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/StreamFormatter.cs @@ -0,0 +1,423 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using System.Xml; +using CoreWCF.Runtime; +using CoreWCF.Channels; +using CoreWCF.Description; +using CoreWCF.Diagnostics; + +namespace CoreWCF.Dispatcher +{ + internal class StreamFormatter + { + string wrapperName; + string wrapperNS; + string partName; + string partNS; + int streamIndex; + bool isRequest; + string operationName; + const int returnValueIndex = -1; + + internal static StreamFormatter Create(MessageDescription messageDescription, string operationName, bool isRequest) + { + MessagePartDescription streamPart = ValidateAndGetStreamPart(messageDescription, isRequest, operationName); + if (streamPart == null) + return null; + return new StreamFormatter(messageDescription, streamPart, operationName, isRequest); + } + + StreamFormatter(MessageDescription messageDescription, MessagePartDescription streamPart, string operationName, bool isRequest) + { + if ((object)streamPart == (object)messageDescription.Body.ReturnValue) + streamIndex = returnValueIndex; + else + streamIndex = streamPart.Index; + wrapperName = messageDescription.Body.WrapperName; + wrapperNS = messageDescription.Body.WrapperNamespace; + partName = streamPart.Name; + partNS = streamPart.Namespace; + this.isRequest = isRequest; + this.operationName = operationName; + } + + internal void Serialize(XmlDictionaryWriter writer, object[] parameters, object returnValue) + { + Stream streamValue = GetStreamAndWriteStartWrapperIfNecessary(writer, parameters, returnValue); + var streamProvider = new OperationStreamProvider(streamValue); + StreamFormatterHelper.WriteValue(writer, streamProvider); + WriteEndWrapperIfNecessary(writer); + } + + internal async Task SerializeAsync(XmlDictionaryWriter writer, object[] parameters, object returnValue) + { + using (TaskHelpers.RunTaskContinuationsOnOurThreads()) // If inner stream doesn't have sync implementation, don't continue on thread pool. + { + // TODO: For NetStandard 2.0, use async methods on writer + Stream streamValue = await GetStreamAndWriteStartWrapperIfNecessaryAsync(writer, parameters, returnValue); + var streamProvider = new OperationStreamProvider(streamValue); + await StreamFormatterHelper.WriteValueAsync(writer, streamProvider); + await WriteEndWrapperIfNecessaryAsync(writer); + } + } + + Stream GetStreamAndWriteStartWrapperIfNecessary(XmlDictionaryWriter writer, object[] parameters, object returnValue) + { + Stream streamValue = GetStreamValue(parameters, returnValue); + if (streamValue == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(partName); + if (WrapperName != null) + writer.WriteStartElement(WrapperName, WrapperNamespace); + writer.WriteStartElement(PartName, PartNamespace); + return streamValue; + } + + private async Task GetStreamAndWriteStartWrapperIfNecessaryAsync(XmlDictionaryWriter writer, object[] parameters, object returnValue) + { + Stream streamValue = GetStreamValue(parameters, returnValue); + if (streamValue == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(partName); + if (WrapperName != null) + await writer.WriteStartElementAsync(null, WrapperName, WrapperNamespace); + await writer.WriteStartElementAsync(null, PartName, PartNamespace); + return streamValue; + } + + void WriteEndWrapperIfNecessary(XmlDictionaryWriter writer) + { + writer.WriteEndElement(); + if (wrapperName != null) + writer.WriteEndElement(); + } + + private Task WriteEndWrapperIfNecessaryAsync(XmlDictionaryWriter writer) + { + writer.WriteEndElement(); + if (wrapperName != null) + writer.WriteEndElement(); + return Task.CompletedTask; + } + + internal void Deserialize(object[] parameters, ref object retVal, Message message) + { + SetStreamValue(parameters, ref retVal, new MessageBodyStream(message, WrapperName, WrapperNamespace, PartName, PartNamespace, isRequest)); + } + + internal string WrapperName + { + get { return wrapperName; } + set { wrapperName = value; } + } + + internal string WrapperNamespace + { + get { return wrapperNS; } + set { wrapperNS = value; } + } + + internal string PartName + { + get { return partName; } + } + + internal string PartNamespace + { + get { return partNS; } + } + + + Stream GetStreamValue(object[] parameters, object returnValue) + { + if (streamIndex == returnValueIndex) + return (Stream)returnValue; + return (Stream)parameters[streamIndex]; + } + + void SetStreamValue(object[] parameters, ref object returnValue, Stream streamValue) + { + if (streamIndex == returnValueIndex) + returnValue = streamValue; + else + parameters[streamIndex] = streamValue; + } + + static MessagePartDescription ValidateAndGetStreamPart(MessageDescription messageDescription, bool isRequest, string operationName) + { + MessagePartDescription part = GetStreamPart(messageDescription); + if (part != null) + return part; + if (HasStream(messageDescription)) + { + if (messageDescription.IsTypedMessage) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxInvalidStreamInTypedMessage, messageDescription.MessageName))); + else if (isRequest) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxInvalidStreamInRequest, operationName))); + else + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxInvalidStreamInResponse, operationName))); + } + return null; + } + + private static bool HasStream(MessageDescription messageDescription) + { + if (messageDescription.Body.ReturnValue != null && messageDescription.Body.ReturnValue.Type == typeof(Stream)) + return true; + foreach (MessagePartDescription part in messageDescription.Body.Parts) + { + if (part.Type == typeof(Stream)) + return true; + } + return false; + } + + static MessagePartDescription GetStreamPart(MessageDescription messageDescription) + { + if (OperationFormatter.IsValidReturnValue(messageDescription.Body.ReturnValue)) + { + if (messageDescription.Body.Parts.Count == 0) + if (messageDescription.Body.ReturnValue.Type == typeof(Stream)) + return messageDescription.Body.ReturnValue; + } + else + { + if (messageDescription.Body.Parts.Count == 1) + if (messageDescription.Body.Parts[0].Type == typeof(Stream)) + return messageDescription.Body.Parts[0]; + } + return null; + } + + internal static bool IsStream(MessageDescription messageDescription) + { + return GetStreamPart(messageDescription) != null; + } + + internal class MessageBodyStream : Stream + { + Message message; + XmlDictionaryReader reader; + long position; + string wrapperName, wrapperNs; + string elementName, elementNs; + bool isRequest; + internal MessageBodyStream(Message message, string wrapperName, string wrapperNs, string elementName, string elementNs, bool isRequest) + { + this.message = message; + position = 0; + this.wrapperName = wrapperName; + this.wrapperNs = wrapperNs; + this.elementName = elementName; + this.elementNs = elementNs; + this.isRequest = isRequest; + } + + public override int Read(byte[] buffer, int offset, int count) + { + EnsureStreamIsOpen(); + if (buffer == null) + throw TraceUtility.ThrowHelperError(new ArgumentNullException(nameof(buffer)), message); + if (offset < 0) + throw TraceUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(offset), offset, + SR.ValueMustBeNonNegative), message); + if (count < 0) + throw TraceUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(count), count, + SR.ValueMustBeNonNegative), message); + if (buffer.Length - offset < count) + throw TraceUtility.ThrowHelperError(new ArgumentException(SR.Format(SR.SFxInvalidStreamOffsetLength, offset + count)), message); + + try + { + + if (reader == null) + { + reader = message.GetReaderAtBodyContents(); + if (wrapperName != null) + { + reader.MoveToContent(); + reader.ReadStartElement(wrapperName, wrapperNs); + } + reader.MoveToContent(); + if (reader.NodeType == XmlNodeType.EndElement) + { + return 0; + } + + reader.ReadStartElement(elementName, elementNs); + } + if (reader.MoveToContent() != XmlNodeType.Text) + { + Exhaust(reader); + return 0; + } + int bytesRead = reader.ReadContentAsBase64(buffer, offset, count); + position += bytesRead; + if (bytesRead == 0) + { + Exhaust(reader); + } + return bytesRead; + } + catch (Exception ex) + { + if (Fx.IsFatal(ex)) + throw; + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new IOException(SR.SFxStreamIOException, ex)); + } + } + + private void EnsureStreamIsOpen() + { + if (message.State == MessageState.Closed) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ObjectDisposedException( + isRequest ? SR.SFxStreamRequestMessageClosed : SR.SFxStreamResponseMessageClosed)); + } + + static void Exhaust(XmlDictionaryReader reader) + { + if (reader != null) + { + while (reader.Read()) + { + // drain + } + } + } + + public override long Position + { + get + { + EnsureStreamIsOpen(); + return position; + } + set { throw TraceUtility.ThrowHelperError(new NotSupportedException(), message); } + } + + protected override void Dispose(bool isDisposing) + { + message.Close(); + if (reader != null) + { + reader.Dispose(); + reader = null; + } + base.Dispose(isDisposing); + } + + public override bool CanRead { get { return message.State != MessageState.Closed; } } + public override bool CanSeek { get { return false; } } + public override bool CanWrite { get { return false; } } + public override long Length + { + get + { + throw TraceUtility.ThrowHelperError(new NotSupportedException(), message); + } + } + public override void Flush() { throw TraceUtility.ThrowHelperError(new NotSupportedException(), message); } + public override long Seek(long offset, SeekOrigin origin) { throw TraceUtility.ThrowHelperError(new NotSupportedException(), message); } + public override void SetLength(long value) { throw TraceUtility.ThrowHelperError(new NotSupportedException(), message); } + public override void Write(byte[] buffer, int offset, int count) { throw TraceUtility.ThrowHelperError(new NotSupportedException(), message); } + } + + internal class OperationStreamProvider //: IStreamProvider + { + Stream stream; + + internal OperationStreamProvider(Stream stream) + { + this.stream = stream; + } + + public Stream GetStream() + { + return stream; + } + public void ReleaseStream(Stream stream) + { + //Noop + } + } + + internal class StreamFormatterHelper + { + // The method was duplicated from the desktop implementation of + // System.Xml.XmlDictionaryWriter.WriteValue(IStreamProvider) + public static void WriteValue(XmlDictionaryWriter writer, OperationStreamProvider value) + { + if (value == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(value)); + + Stream stream = value.GetStream(); + if (stream == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.Format(SR.XmlInvalidStream))); + } + + int blockSize = 256; + int bytesRead = 0; + byte[] block = new byte[blockSize]; + while (true) + { + bytesRead = stream.Read(block, 0, blockSize); + if (bytesRead > 0) + { + writer.WriteBase64(block, 0, bytesRead); + } + else + { + break; + } + + if (blockSize < 65536 && bytesRead == blockSize) + { + blockSize = blockSize * 16; + block = new byte[blockSize]; + } + } + + value.ReleaseStream(stream); + } + + internal static async Task WriteValueAsync(XmlDictionaryWriter writer, OperationStreamProvider value) + { + if (value == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException(nameof(value))); + + Stream stream = value.GetStream(); + if (stream == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.XmlInvalidStream)); + } + + int blockSize = 256; + int bytesRead = 0; + byte[] block = new byte[blockSize]; + while (true) + { + bytesRead = await stream.ReadAsync(block, 0, blockSize); + if (bytesRead > 0) + { + // XmlDictionaryWriter has not implemented WriteBase64Async() yet. + writer.WriteBase64(block, 0, bytesRead); + } + else + { + break; + } + + if (blockSize < 65536 && bytesRead == blockSize) + { + blockSize = blockSize * 16; + block = new byte[blockSize]; + } + } + + value.ReleaseStream(stream); + } + } + + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/SyncMethodInvoker.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/SyncMethodInvoker.cs new file mode 100644 index 000000000..544a589e8 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/SyncMethodInvoker.cs @@ -0,0 +1,212 @@ +using System; +using System.Reflection; +using System.Threading.Tasks; +using CoreWCF.Runtime; +using CoreWCF.Diagnostics; + +namespace CoreWCF.Dispatcher +{ + internal class SyncMethodInvoker : IOperationInvoker + { + private readonly MethodInfo _method; + private InvokeDelegate _invokeDelegate; + private int _inputParameterCount; + private int _outputParameterCount; + private string _methodName; + + public SyncMethodInvoker(MethodInfo method) + { + if (method == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(method)); + } + + _method = method; + } + + public MethodInfo Method + { + get + { + return _method; + } + } + + public string MethodName + { + get + { + if (_methodName == null) + _methodName = _method.Name; + return _methodName; + } + } + + public bool IsSynchronous => false; + + public object[] AllocateInputs() + { + EnsureIsInitialized(); + + return EmptyArray.Allocate(_inputParameterCount); + } + + public object Invoke(object instance, object[] inputs, out object[] outputs) + { + throw new NotImplementedException(); + } + + public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state) + { + return InvokeAsync(instance, inputs).ToApm(callback, state); + } + + public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result) + { + var task = result as Task>; + if (task == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.SFxInvalidCallbackIAsyncResult)); + } + + var tuple = task.Result; + outputs = tuple.Item2; + return tuple.Item1; + } + + private Task> InvokeAsync(object instance, object[] inputs) + { + EnsureIsInitialized(); + + if (instance == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.SFxNoServiceObject)); + if (inputs == null) + { + if (_inputParameterCount > 0) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxInputParametersToServiceNull, _inputParameterCount))); + } + else if (inputs.Length != _inputParameterCount) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxInputParametersToServiceInvalid, _inputParameterCount, inputs.Length))); + + var outputs = EmptyArray.Allocate(_outputParameterCount); + + //long beginOperation = 0; + //bool callSucceeded = false; + //bool callFaulted = false; + + //EventTraceActivity eventTraceActivity = null; + //if (WcfEventSource.Instance.OperationCompletedIsEnabled() || + // WcfEventSource.Instance.OperationFaultedIsEnabled() || + // WcfEventSource.Instance.OperationFailedIsEnabled()) + //{ + // beginOperation = DateTime.UtcNow.Ticks; + // OperationContext context = OperationContext.Current; + // if (context != null && context.IncomingMessage != null) + // { + // eventTraceActivity = EventTraceActivityHelper.TryExtractActivity(context.IncomingMessage); + // } + //} + + object returnValue; + try + { + //ServiceModelActivity activity = null; + //IDisposable boundActivity = null; + //if (DiagnosticUtility.ShouldUseActivity) + //{ + // activity = ServiceModelActivity.CreateBoundedActivity(true); + // boundActivity = activity; + //} + //else if (TraceUtility.MessageFlowTracingOnly) + //{ + // Guid activityId = TraceUtility.GetReceivedActivityId(OperationContext.Current); + // if (activityId != Guid.Empty) + // { + // DiagnosticTraceBase.ActivityId = activityId; + // } + //} + //else if (TraceUtility.ShouldPropagateActivity) + //{ + // //Message flow tracing only scenarios use a light-weight ActivityID management logic + // Guid activityId = ActivityIdHeader.ExtractActivityId(OperationContext.Current.IncomingMessage); + // if (activityId != Guid.Empty) + // { + // boundActivity = Activity.CreateActivity(activityId); + // } + //} + + //using (boundActivity) + //{ + // if (DiagnosticUtility.ShouldUseActivity) + // { + // ServiceModelActivity.Start(activity, SR.Format(SR.ActivityExecuteMethod, _method.DeclaringType.FullName, _method.Name), ActivityType.ExecuteUserCode); + // } + // if (WcfEventSource.Instance.OperationInvokedIsEnabled()) + // { + // WcfEventSource.Instance.OperationInvoked(eventTraceActivity, MethodName, TraceUtility.GetCallerInfo(OperationContext.Current)); + // } + returnValue = _invokeDelegate(instance, inputs, outputs); + //callSucceeded = true; + //} + } + catch (FaultException) + { + //callFaulted = true; + throw; + } + finally + { + //if (beginOperation != 0) + //{ + // if (callSucceeded) + // { + // if (WcfEventSource.Instance.OperationCompletedIsEnabled()) + // { + // WcfEventSource.Instance.OperationCompleted(eventTraceActivity, _methodName, + // TraceUtility.GetUtcBasedDurationForTrace(beginOperation)); + // } + // } + // else if (callFaulted) + // { + // if (WcfEventSource.Instance.OperationFaultedIsEnabled()) + // { + // WcfEventSource.Instance.OperationFaulted(eventTraceActivity, _methodName, + // TraceUtility.GetUtcBasedDurationForTrace(beginOperation)); + // } + // } + // else + // { + // if (WcfEventSource.Instance.OperationFailedIsEnabled()) + // { + // WcfEventSource.Instance.OperationFailed(eventTraceActivity, _methodName, + // TraceUtility.GetUtcBasedDurationForTrace(beginOperation)); + // } + // } + //} + } + + return Task.FromResult(Tuple.Create(returnValue, outputs)); + } + + private void EnsureIsInitialized() + { + if (_invokeDelegate == null) + { + EnsureIsInitializedCore(); + } + } + + private void EnsureIsInitializedCore() + { + // Only pass locals byref because InvokerUtil may store temporary results in the byref. + // If two threads both reference this.count, temporary results may interact. + int inputParameterCount; + int outputParameterCount; + var invokeDelegate = new InvokerUtil().GenerateInvokeDelegate(Method, out inputParameterCount, out outputParameterCount); + _outputParameterCount = outputParameterCount; + _inputParameterCount = inputParameterCount; + _invokeDelegate = invokeDelegate; // must set this last due to race + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/SynchronizedChannelCollection.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/SynchronizedChannelCollection.cs new file mode 100644 index 000000000..daca1bd17 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/SynchronizedChannelCollection.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections.Generic; +using CoreWCF.Collections.Generic; +using CoreWCF.Channels; + +namespace CoreWCF.Dispatcher +{ + class SynchronizedChannelCollection : SynchronizedCollection + where TChannel : IChannel + { + EventHandler onChannelClosed; + EventHandler onChannelFaulted; + + internal SynchronizedChannelCollection(object syncRoot) + : base(syncRoot) + { + onChannelClosed = new EventHandler(OnChannelClosed); + onChannelFaulted = new EventHandler(OnChannelFaulted); + } + + void AddingChannel(TChannel channel) + { + channel.Faulted += onChannelFaulted; + channel.Closed += onChannelClosed; + } + + void RemovingChannel(TChannel channel) + { + channel.Faulted -= onChannelFaulted; + channel.Closed -= onChannelClosed; + } + + void OnChannelClosed(object sender, EventArgs args) + { + TChannel channel = (TChannel)sender; + Remove(channel); + } + + void OnChannelFaulted(object sender, EventArgs args) + { + TChannel channel = (TChannel)sender; + Remove(channel); + } + + protected override void ClearItems() + { + List items = Items; + + for (int i = 0; i < items.Count; i++) + { + RemovingChannel(items[i]); + } + + base.ClearItems(); + } + + protected override void InsertItem(int index, TChannel item) + { + AddingChannel(item); + base.InsertItem(index, item); + } + + protected override void RemoveItem(int index) + { + TChannel oldItem = Items[index]; + + base.RemoveItem(index); + RemovingChannel(oldItem); + } + + protected override void SetItem(int index, TChannel item) + { + TChannel oldItem = Items[index]; + + AddingChannel(item); + base.SetItem(index, item); + RemovingChannel(oldItem); + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/TaskMethodInvoker.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/TaskMethodInvoker.cs new file mode 100644 index 000000000..a6dc8334c --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/TaskMethodInvoker.cs @@ -0,0 +1,335 @@ +using System; +using System.Reflection; +using System.Security; +using System.Threading; +using System.Threading.Tasks; +using CoreWCF.Runtime; +using CoreWCF.Description; +using CoreWCF.Diagnostics; +using System.Diagnostics; + +namespace CoreWCF.Dispatcher +{ + internal class TaskMethodInvoker : IOperationInvoker + { + private const string ResultMethodName = "Result"; + private readonly MethodInfo taskMethod; + private InvokeDelegate invokeDelegate; + private int inputParameterCount; + private int outputParameterCount; + private MethodInfo taskTResultGetMethod; + private bool isGenericTask; + + public TaskMethodInvoker(MethodInfo taskMethod, Type taskType) + { + if (taskMethod == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(taskMethod)); + } + + this.taskMethod = taskMethod; + + if (taskType != ServiceReflector.VoidType) + { + taskTResultGetMethod = ((PropertyInfo)taskMethod.ReturnType.GetMember(ResultMethodName)[0]).GetGetMethod(); + isGenericTask = true; + } + } + + public bool IsSynchronous + { + get { return false; } + } + + public MethodInfo TaskMethod + { + get { return taskMethod; } + } + + public object[] AllocateInputs() + { + EnsureIsInitialized(); + + return EmptyArray.Allocate(inputParameterCount); + } + + public object Invoke(object instance, object[] inputs, out object[] outputs) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException()); + } + + public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state) + { + return ToApm(InvokeAsync(instance, inputs), callback, state); + } + + public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result) + { + if (instance == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.SFxNoServiceObject)); + } + + object returnVal = null; + //bool callFailed = true; + //bool callFaulted = false; + //ServiceModelActivity activity = null; + //Activity boundOperation = null; + + try + { + //AsyncMethodInvoker.GetActivityInfo(ref activity, ref boundOperation); + + Task> invokeTask = result as Task>; + + if (invokeTask == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.SFxInvalidCallbackIAsyncResult); + } + + AggregateException ae = null; + Tuple tuple = null; + Task task = null; + + if (invokeTask.IsFaulted) + { + Fx.Assert(invokeTask.Exception != null, "Task.IsFaulted guarantees non-null exception."); + ae = invokeTask.Exception; + } + else + { + Fx.Assert(invokeTask.IsCompleted, "Task.Result is expected to be completed"); + + tuple = invokeTask.Result; + task = tuple.Item1 as Task; + + if (task == null) + { + outputs = tuple.Item2; + return null; + } + + if (task.IsFaulted) + { + Fx.Assert(task.Exception != null, "Task.IsFaulted guarantees non-null exception."); + ae = task.Exception; + } + } + + if (ae != null && ae.InnerException != null) + { + if (ae.InnerException is FaultException) + { + // If invokeTask.IsFaulted we produce the 'callFaulted' behavior below. + // Any other exception will retain 'callFailed' behavior. + //callFaulted = true; + //callFailed = false; + } + + if (ae.InnerException is SecurityException) + { + DiagnosticUtility.TraceHandledException(ae.InnerException, TraceEventType.Warning); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(AuthorizationBehavior.CreateAccessDeniedFaultException()); + } + + invokeTask.GetAwaiter().GetResult(); + } + + // Task cancellation without an exception indicates failure but we have no + // additional information to provide. Accessing Task.Result will throw a + // TaskCanceledException. For consistency between void Tasks and Task, + // we detect and throw here. + if (task.IsCanceled) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new TaskCanceledException(task)); + } + + outputs = tuple.Item2; + + returnVal = isGenericTask ? taskTResultGetMethod.Invoke(task, Type.EmptyTypes) : null; + //callFailed = false; + + return returnVal; + } + finally + { + //if (boundOperation != null) + //{ + // ((IDisposable)boundOperation).Dispose(); + //} + + //ServiceModelActivity.Stop(activity); + //AsyncMethodInvoker.StopOperationInvokeTrace(callFailed, callFaulted, TaskMethod.Name); + //AsyncMethodInvoker.StopOperationInvokePerformanceCounters(callFailed, callFaulted, TaskMethod.Name); + } + } + + private async Task> InvokeAsync(object instance, object[] inputs) + { + EnsureIsInitialized(); + + if (instance == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.SFxNoServiceObject)); + } + + if (inputs == null) + { + if (inputParameterCount > 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxInputParametersToServiceNull, inputParameterCount))); + } + } + else if (inputs.Length != inputParameterCount) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxInputParametersToServiceInvalid, inputParameterCount, inputs.Length))); + } + + object[] outputs = EmptyArray.Allocate(outputParameterCount); + + //AsyncMethodInvoker.StartOperationInvokePerformanceCounters(taskMethod.Name); + + object returnValue; + bool callFailed = true; + bool callFaulted = false; + //ServiceModelActivity activity = null; + //Activity boundActivity = null; + + try + { + //AsyncMethodInvoker.CreateActivityInfo(ref activity, ref boundActivity); + //AsyncMethodInvoker.StartOperationInvokeTrace(taskMethod.Name); + + //if (DiagnosticUtility.ShouldUseActivity) + //{ + // string activityName = SR.Format(SR.ActivityExecuteMethod, taskMethod.DeclaringType.FullName, taskMethod.Name); + // ServiceModelActivity.Start(activity, activityName, ActivityType.ExecuteUserCode); + //} + + returnValue = invokeDelegate(instance, inputs, outputs); + + if (returnValue == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("task"); + } + + var returnValueTask = returnValue as Task; + + if (returnValueTask != null) + { + // Only return once the task has completed + await returnValueTask; + } + + callFailed = false; + + return Tuple.Create(returnValue, outputs); + } + catch (SecurityException e) + { + DiagnosticUtility.TraceHandledException(e, TraceEventType.Warning); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(AuthorizationBehavior.CreateAccessDeniedFaultException()); + } + catch (FaultException) + { + callFaulted = true; + throw; + } + catch (Exception e) + { + TraceUtility.TraceUserCodeException(e, taskMethod); + throw; + } + finally + { + //if (boundActivity != null) + //{ + // ((IDisposable)boundActivity).Dispose(); + //} + + //ServiceModelActivity.Stop(activity); + + // Any exception above means InvokeEnd will not be called, so complete it here. + if (callFailed || callFaulted) + { + //AsyncMethodInvoker.StopOperationInvokeTrace(callFailed, callFaulted, TaskMethod.Name); + //AsyncMethodInvoker.StopOperationInvokePerformanceCounters(callFailed, callFaulted, TaskMethod.Name); + } + } + } + + // Helper method when implementing an APM wrapper around a Task based async method which returns a result. + // In the BeginMethod method, you would call use ToApm to wrap a call to MethodAsync: + // return MethodAsync(params).ToApm(callback, state); + // In the EndMethod, you would use ToApmEnd to ensure the correct exception handling + // This will handle throwing exceptions in the correct place and ensure the IAsyncResult contains the provided + // state object + private static Task ToApm(Task task, AsyncCallback callback, object state) + { + // When using APM, the returned IAsyncResult must have the passed in state object stored in AsyncState. This + // is so the callback can regain state. If the incoming task already holds the state object, there's no need + // to create a TaskCompletionSource to ensure the returned (IAsyncResult)Task has the right state object. + // This is a performance optimization for this special case. + if (task.AsyncState == state) + { + if (callback != null) + { + task.ContinueWith((antecedent, obj) => + { + AsyncCallback callbackObj = (AsyncCallback)obj; + callbackObj(antecedent); + }, callback, CancellationToken.None, TaskContinuationOptions.HideScheduler, TaskScheduler.Default); + } + + return task; + } + + // Need to create a TaskCompletionSource so that the returned Task object has the correct AsyncState value. + var tcs = new TaskCompletionSource(state); + var continuationState = Tuple.Create(tcs, callback); + + task.ContinueWith((antecedent, obj) => + { + Tuple, AsyncCallback> tuple = (Tuple, AsyncCallback>)obj; + TaskCompletionSource tcsObj = tuple.Item1; + AsyncCallback callbackObj = tuple.Item2; + + if (antecedent.IsFaulted) + { + tcsObj.TrySetException(antecedent.Exception.InnerException); + } + else if (antecedent.IsCanceled) + { + tcsObj.TrySetCanceled(); + } + else + { + tcsObj.TrySetResult(antecedent.Result); + } + + if (callbackObj != null) + { + callbackObj(tcsObj.Task); + } + }, continuationState, CancellationToken.None, TaskContinuationOptions.HideScheduler, TaskScheduler.Default); + + return tcs.Task; + } + + private void EnsureIsInitialized() + { + if (this.invokeDelegate == null) + { + // Only pass locals byref because InvokerUtil may store temporary results in the byref. + // If two threads both reference this.count, temporary results may interact. + int inputParameterCount; + int outputParameterCount; + InvokeDelegate invokeDelegate = new InvokerUtil().GenerateInvokeDelegate(taskMethod, out inputParameterCount, out outputParameterCount); + this.inputParameterCount = inputParameterCount; + this.outputParameterCount = outputParameterCount; + this.invokeDelegate = invokeDelegate; // must set this last due to race + } + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/TerminatingOperationBehavior.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/TerminatingOperationBehavior.cs new file mode 100644 index 000000000..8b685ea90 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/TerminatingOperationBehavior.cs @@ -0,0 +1,59 @@ +using System; +using System.Threading; +using CoreWCF.Channels; + +namespace CoreWCF.Dispatcher +{ + class TerminatingOperationBehavior + { + static void AbortChannel(object state) + { + ((IChannel)state).Abort(); + } + + public static TerminatingOperationBehavior CreateIfNecessary(DispatchRuntime dispatch) + { + if (IsTerminatingOperationBehaviorNeeded(dispatch)) + { + return new TerminatingOperationBehavior(); + } + else + { + return null; + } + } + + static bool IsTerminatingOperationBehaviorNeeded(DispatchRuntime dispatch) + { + for (int i = 0; i < dispatch.Operations.Count; i++) + { + DispatchOperation operation = dispatch.Operations[i]; + + if (operation.IsTerminating) + { + return true; + } + } + + return false; + } + + internal void AfterReply(ref MessageRpc rpc) + { + if (rpc.Operation.IsTerminating && rpc.Channel.HasSession) + { + Timer timer = new Timer(new TimerCallback(TerminatingOperationBehavior.AbortChannel), rpc.Channel.Binder.Channel, rpc.Channel.CloseTimeout, TimeSpan.FromMilliseconds(-1)); + } + } + + internal static void AfterReply(ref ProxyRpc rpc) + { + if (rpc.Operation.IsTerminating && rpc.Channel.HasSession) + { + IChannel sessionChannel = rpc.Channel.Binder.Channel; + rpc.Channel.CloseAsync(rpc.CancellationToken).GetAwaiter().GetResult(); + } + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ThreadBehavior.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ThreadBehavior.cs new file mode 100644 index 000000000..96d60a998 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ThreadBehavior.cs @@ -0,0 +1,33 @@ +using System; +using System.Threading; +using CoreWCF.Runtime; + +namespace CoreWCF.Dispatcher +{ + internal class ThreadBehavior + { + readonly SynchronizationContext context; + + internal ThreadBehavior(DispatchRuntime dispatch) + { + context = dispatch.SynchronizationContext; + } + + internal SynchronizationContext GetSyncContext(MessageRpc rpc) + { + Fx.Assert(rpc.InstanceContext != null, "instanceContext is null !"); + SynchronizationContext syncContext = rpc.InstanceContext.SynchronizationContext ?? context; + return syncContext; + } + + internal static SynchronizationContext GetCurrentSynchronizationContext() + { + //if (AspNetEnvironment.IsApplicationDomainHosted()) + //{ + // return null; + //} + return SynchronizationContext.Current; + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ThreadSafeMessageFilterTable.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ThreadSafeMessageFilterTable.cs new file mode 100644 index 000000000..8ec210d2f --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ThreadSafeMessageFilterTable.cs @@ -0,0 +1,275 @@ +using System.Collections; +using System.Collections.Generic; +using CoreWCF.Channels; + +namespace CoreWCF.Dispatcher +{ + internal class ThreadSafeMessageFilterTable : IMessageFilterTable + { + MessageFilterTable table; + object syncRoot; + + internal ThreadSafeMessageFilterTable() + { + table = new MessageFilterTable(); + syncRoot = new object(); + } + + internal object SyncRoot + { + get { return syncRoot; } + } + + public int DefaultPriority + { + get + { + lock (syncRoot) + { + return table.DefaultPriority; + } + } + set + { + lock (syncRoot) + { + table.DefaultPriority = value; + } + } + } + + internal void Add(MessageFilter filter, FilterData data, int priority) + { + lock (syncRoot) + { + table.Add(filter, data, priority); + } + } + + // + // IMessageFilterTable methods + // + + public int Count + { + get + { + lock (syncRoot) + { + return table.Count; + } + } + } + + public void Clear() + { + lock (syncRoot) + { + table.Clear(); + } + } + + public bool GetMatchingValue(Message message, out FilterData data) + { + lock (syncRoot) + { + return table.GetMatchingValue(message, out data); + } + } + + public bool GetMatchingValue(MessageBuffer buffer, out FilterData data) + { + lock (syncRoot) + { + return table.GetMatchingValue(buffer, out data); + } + } + + public bool GetMatchingValues(Message message, ICollection results) + { + lock (syncRoot) + { + return table.GetMatchingValues(message, results); + } + } + + public bool GetMatchingValues(MessageBuffer buffer, ICollection results) + { + lock (syncRoot) + { + return table.GetMatchingValues(buffer, results); + } + } + + public bool GetMatchingFilter(Message message, out MessageFilter filter) + { + lock (syncRoot) + { + return table.GetMatchingFilter(message, out filter); + } + } + + public bool GetMatchingFilter(MessageBuffer buffer, out MessageFilter filter) + { + lock (syncRoot) + { + return table.GetMatchingFilter(buffer, out filter); + } + } + + public bool GetMatchingFilters(Message message, ICollection results) + { + lock (syncRoot) + { + return table.GetMatchingFilters(message, results); + } + } + + public bool GetMatchingFilters(MessageBuffer buffer, ICollection results) + { + lock (syncRoot) + { + return table.GetMatchingFilters(buffer, results); + } + } + + // + // IDictionary methods + // + + public FilterData this[MessageFilter key] + { + get + { + lock (syncRoot) + { + return table[key]; + } + } + set + { + lock (syncRoot) + { + table[key] = value; + } + } + } + + public ICollection Keys + { + get + { + lock (syncRoot) + { + return table.Keys; + } + } + } + + public ICollection Values + { + get + { + lock (syncRoot) + { + return table.Values; + } + } + } + + public bool ContainsKey(MessageFilter key) + { + lock (syncRoot) + { + return table.ContainsKey(key); + } + } + + public void Add(MessageFilter key, FilterData value) + { + lock (syncRoot) + { + table.Add(key, value); + } + } + + public bool Remove(MessageFilter key) + { + lock (syncRoot) + { + return table.Remove(key); + } + } + + // + // ICollection> methods + // + + bool ICollection>.IsReadOnly + { + get + { + lock (syncRoot) + { + return ((ICollection>)table).IsReadOnly; + } + } + } + + void ICollection>.Add(KeyValuePair item) + { + lock (syncRoot) + { + ((ICollection>)table).Add(item); + } + } + + bool ICollection>.Contains(KeyValuePair item) + { + lock (syncRoot) + { + return ((ICollection>)table).Contains(item); + } + } + + void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex) + { + lock (syncRoot) + { + ((ICollection>)table).CopyTo(array, arrayIndex); + } + } + + bool ICollection>.Remove(KeyValuePair item) + { + lock (syncRoot) + { + return ((ICollection>)table).Remove(item); + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + lock (syncRoot) + { + return ((IEnumerable>)this).GetEnumerator(); + } + } + + IEnumerator> IEnumerable>.GetEnumerator() + { + lock (syncRoot) + { + return ((ICollection>)table).GetEnumerator(); + } + } + + public bool TryGetValue(MessageFilter filter, out FilterData data) + { + lock (syncRoot) + { + return table.TryGetValue(filter, out data); + } + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/UniqueContractNameValidationBehavior.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/UniqueContractNameValidationBehavior.cs new file mode 100644 index 000000000..097ea062f --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/UniqueContractNameValidationBehavior.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Xml; +using CoreWCF.Channels; +using CoreWCF.Description; + +namespace CoreWCF.Dispatcher +{ + internal class UniqueContractNameValidationBehavior : IServiceBehavior + { + Dictionary contracts = new Dictionary(); + + public UniqueContractNameValidationBehavior() { } + + public void Validate(ServiceDescription description, ServiceHostBase serviceHostBase) + { + if (description == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(description)); + if (serviceHostBase == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(serviceHostBase)); + + + foreach (ServiceEndpoint endpoint in description.Endpoints) + { + XmlQualifiedName qname = new XmlQualifiedName(endpoint.Contract.Name, endpoint.Contract.Namespace); + + if (!contracts.ContainsKey(qname)) + { + contracts.Add(qname, endpoint.Contract); + } + else if (contracts[qname] != endpoint.Contract) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException( + SR.Format(SR.SFxMultipleContractsWithSameName, qname.Name, qname.Namespace))); + } + } + } + + public void AddBindingParameters(ServiceDescription description, ServiceHostBase serviceHostBase, Collection endpoints, BindingParameterCollection parameters) + { + } + + public void ApplyDispatchBehavior(ServiceDescription description, ServiceHostBase serviceHostBase) + { + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/XmlSerializerFaultFormatter.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/XmlSerializerFaultFormatter.cs new file mode 100644 index 000000000..999b72e53 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/XmlSerializerFaultFormatter.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; +using System.Xml; +using CoreWCF.Collections.Generic; +using CoreWCF.Channels; +using CoreWCF.Description; + +namespace CoreWCF.Dispatcher +{ + internal class XmlSerializerFaultFormatter : FaultFormatter + { + SynchronizedCollection xmlSerializerFaultContractInfos; + + internal XmlSerializerFaultFormatter(Type[] detailTypes, + SynchronizedCollection xmlSerializerFaultContractInfos) + : base(detailTypes) + { + Initialize(xmlSerializerFaultContractInfos); + } + + internal XmlSerializerFaultFormatter(SynchronizedCollection faultContractInfoCollection, + SynchronizedCollection xmlSerializerFaultContractInfos) + : base(faultContractInfoCollection) + { + Initialize(xmlSerializerFaultContractInfos); + } + + void Initialize(SynchronizedCollection xmlSerializerFaultContractInfos) + { + if (xmlSerializerFaultContractInfos == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("xmlSerializerFaultContractInfos"); + } + this.xmlSerializerFaultContractInfos = xmlSerializerFaultContractInfos; + } + + protected override XmlObjectSerializer GetSerializer(Type detailType, string faultExceptionAction, out string action) + { + action = faultExceptionAction; + + XmlSerializerOperationBehavior.Reflector.XmlSerializerFaultContractInfo faultInfo = null; + for (int i = 0; i < xmlSerializerFaultContractInfos.Count; i++) + { + if (xmlSerializerFaultContractInfos[i].FaultContractInfo.Detail == detailType) + { + faultInfo = xmlSerializerFaultContractInfos[i]; + break; + } + } + if (faultInfo != null) + { + if (action == null) + action = faultInfo.FaultContractInfo.Action; + + return faultInfo.Serializer; + } + else + return new XmlSerializerObjectSerializer(detailType); + } + + protected override FaultException CreateFaultException(MessageFault messageFault, string action) + { + IList faultInfos; + if (action != null) + { + faultInfos = new List(); + for (int i = 0; i < xmlSerializerFaultContractInfos.Count; i++) + { + if (xmlSerializerFaultContractInfos[i].FaultContractInfo.Action == action + || xmlSerializerFaultContractInfos[i].FaultContractInfo.Action == MessageHeaders.WildcardAction) + { + faultInfos.Add(xmlSerializerFaultContractInfos[i]); + } + } + } + else + { + faultInfos = xmlSerializerFaultContractInfos; + } + + Type detailType = null; + object detailObj = null; + for (int i = 0; i < faultInfos.Count; i++) + { + XmlSerializerOperationBehavior.Reflector.XmlSerializerFaultContractInfo faultInfo = faultInfos[i]; + XmlDictionaryReader detailReader = messageFault.GetReaderAtDetailContents(); + XmlObjectSerializer serializer = faultInfo.Serializer; + + if (serializer.IsStartObject(detailReader)) + { + detailType = faultInfo.FaultContractInfo.Detail; + try + { + detailObj = serializer.ReadObject(detailReader); + FaultException faultException = CreateFaultException(messageFault, action, + detailObj, detailType, detailReader); + if (faultException != null) + return faultException; + } + catch (SerializationException) + { + } + } + } + return new FaultException(messageFault, action); + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/XmlSerializerObjectSerializer.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/XmlSerializerObjectSerializer.cs new file mode 100644 index 000000000..22cf99c00 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/XmlSerializerObjectSerializer.cs @@ -0,0 +1,121 @@ +using System; +using System.Runtime.Serialization; +using System.Xml; +using System.Xml.Serialization; +using CoreWCF.Runtime.Serialization; + +namespace CoreWCF.Dispatcher +{ + internal class XmlSerializerObjectSerializer : XmlObjectSerializer + { + XmlSerializer serializer; + Type rootType; + string rootName; + string rootNamespace; + bool isSerializerSetExplicit = false; + + internal XmlSerializerObjectSerializer(Type type) + { + Initialize(type, null /*rootName*/, null /*rootNamespace*/, null /*xmlSerializer*/); + } + + internal XmlSerializerObjectSerializer(Type type, XmlQualifiedName qualifiedName, XmlSerializer xmlSerializer) + { + if (qualifiedName == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("qualifiedName"); + } + Initialize(type, qualifiedName.Name, qualifiedName.Namespace, xmlSerializer); + } + + void Initialize(Type type, string rootName, string rootNamespace, XmlSerializer xmlSerializer) + { + if (type == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("type"); + } + rootType = type; + this.rootName = rootName; + this.rootNamespace = rootNamespace == null ? string.Empty : rootNamespace; + serializer = xmlSerializer; + + if (serializer == null) + { + if (this.rootName == null) + serializer = new XmlSerializer(type); + else + { + XmlRootAttribute xmlRoot = new XmlRootAttribute(); + xmlRoot.ElementName = this.rootName; + xmlRoot.Namespace = this.rootNamespace; + serializer = new XmlSerializer(type, xmlRoot); + } + } + else + isSerializerSetExplicit = true; + + //try to get rootName and rootNamespace from type since root name not set explicitly + if (this.rootName == null) + { + XmlTypeMapping mapping = new XmlReflectionImporter().ImportTypeMapping(rootType); + this.rootName = mapping.ElementName; + this.rootNamespace = mapping.Namespace; + } + } + + public override void WriteObject(XmlDictionaryWriter writer, object graph) + { + if (isSerializerSetExplicit) + serializer.Serialize(writer, new object[] { graph }); + else + serializer.Serialize(writer, graph); + } + + public override void WriteStartObject(XmlDictionaryWriter writer, object graph) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException()); + } + + public override void WriteObjectContent(XmlDictionaryWriter writer, object graph) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException()); + } + + public override void WriteEndObject(XmlDictionaryWriter writer) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException()); + } + + public override object ReadObject(XmlDictionaryReader reader, bool verifyObjectName) + { + if (isSerializerSetExplicit) + { + object[] deserializedObjects = (object[])serializer.Deserialize(reader); + if (deserializedObjects != null && deserializedObjects.Length > 0) + return deserializedObjects[0]; + else + return null; + } + else + return serializer.Deserialize(reader); + } + + public override bool IsStartObject(XmlDictionaryReader reader) + { + if (reader == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(reader)); + + reader.MoveToElement(); + + if (rootName != null) + { + return reader.IsStartElement(rootName, rootNamespace); + } + else + { + return reader.IsStartElement(); + } + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/XmlSerializerOperationFormatter.cs b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/XmlSerializerOperationFormatter.cs new file mode 100644 index 000000000..9542f590c --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/XmlSerializerOperationFormatter.cs @@ -0,0 +1,528 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Xml; +using System.Xml.Schema; +using System.Xml.Serialization; +using CoreWCF.Channels; +using CoreWCF.Description; + +namespace CoreWCF.Dispatcher +{ + internal class XmlSerializerOperationFormatter : OperationFormatter + { + const string soap11Encoding = "http://schemas.xmlsoap.org/soap/encoding/"; + const string soap12Encoding = "http://www.w3.org/2003/05/soap-encoding"; + + bool isEncoded; + + MessageInfo requestMessageInfo; + MessageInfo replyMessageInfo; + + public XmlSerializerOperationFormatter(OperationDescription description, XmlSerializerFormatAttribute xmlSerializerFormatAttribute, + MessageInfo requestMessageInfo, MessageInfo replyMessageInfo) : + base(description, xmlSerializerFormatAttribute.Style == OperationFormatStyle.Rpc, xmlSerializerFormatAttribute.IsEncoded) + { + if (xmlSerializerFormatAttribute.IsEncoded && xmlSerializerFormatAttribute.Style != OperationFormatStyle.Rpc) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxDocEncodedNotSupported, description.Name))); + isEncoded = xmlSerializerFormatAttribute.IsEncoded; + + this.requestMessageInfo = requestMessageInfo; + this.replyMessageInfo = replyMessageInfo; + } + + protected override void AddHeadersToMessage(Message message, MessageDescription messageDescription, object[] parameters, bool isRequest) + { + XmlSerializer serializer; + MessageHeaderDescriptionTable headerDescriptionTable; + MessageHeaderDescription unknownHeaderDescription; + bool mustUnderstand; + bool relay; + string actor; + try + { + if (isRequest) + { + serializer = requestMessageInfo.HeaderSerializer; + headerDescriptionTable = requestMessageInfo.HeaderDescriptionTable; + unknownHeaderDescription = requestMessageInfo.UnknownHeaderDescription; + } + else + { + serializer = replyMessageInfo.HeaderSerializer; + headerDescriptionTable = replyMessageInfo.HeaderDescriptionTable; + unknownHeaderDescription = replyMessageInfo.UnknownHeaderDescription; + } + if (serializer != null) + { + object[] headerValues = new object[headerDescriptionTable.Count]; + MessageHeaderOfTHelper messageHeaderOfTHelper = null; + int headerIndex = 0; + + foreach (MessageHeaderDescription headerDescription in messageDescription.Headers) + { + object parameterValue = parameters[headerDescription.Index]; + if (!headerDescription.IsUnknownHeaderCollection) + { + if (headerDescription.TypedHeader) + { + if (messageHeaderOfTHelper == null) + messageHeaderOfTHelper = new MessageHeaderOfTHelper(parameters.Length); + headerValues[headerIndex++] = messageHeaderOfTHelper.GetContentAndSaveHeaderAttributes(parameters[headerDescription.Index], headerDescription); + } + else + headerValues[headerIndex++] = parameterValue; + } + } + + MemoryStream memoryStream = new MemoryStream(); + XmlDictionaryWriter bufferWriter = XmlDictionaryWriter.CreateTextWriter(memoryStream); + bufferWriter.WriteStartElement("root"); + // TODO: Use overload which takes encoding once available + serializer.Serialize(bufferWriter, headerValues, null); //, isEncoded ? GetEncoding(message.Version.Envelope) : null); + bufferWriter.WriteEndElement(); + bufferWriter.Flush(); + XmlDocument doc = new XmlDocument(); + memoryStream.Position = 0; + doc.Load(memoryStream); + // TODO: XmlTextReader supported in .Net Standard 1.7, prohibiting DtdProcessing is important + //doc.Load(new XmlTextReader(memoryStream) { DtdProcessing = DtdProcessing.Prohibit }); + //doc.Save(Console.Out); + foreach (XmlElement element in doc.DocumentElement.ChildNodes) + { + MessageHeaderDescription matchingHeaderDescription = headerDescriptionTable.Get(element.LocalName, element.NamespaceURI); + if (matchingHeaderDescription == null) + message.Headers.Add(new XmlElementMessageHeader(this, message.Version, element.LocalName, element.NamespaceURI, + false/*mustUnderstand*/, null/*actor*/, false/*relay*/, element)); + else + { + if (matchingHeaderDescription.TypedHeader) + messageHeaderOfTHelper.GetHeaderAttributes(matchingHeaderDescription, out mustUnderstand, out relay, out actor); + else + { + mustUnderstand = matchingHeaderDescription.MustUnderstand; + relay = matchingHeaderDescription.Relay; + actor = matchingHeaderDescription.Actor; + } + message.Headers.Add(new XmlElementMessageHeader(this, message.Version, element.LocalName, element.NamespaceURI, + mustUnderstand, actor, relay, element)); + } + } + } + if (unknownHeaderDescription != null && parameters[unknownHeaderDescription.Index] != null) + { + foreach (object unknownHeader in (IEnumerable)parameters[unknownHeaderDescription.Index]) + { + + XmlElement element = (XmlElement)GetContentOfMessageHeaderOfT(unknownHeaderDescription, unknownHeader, out mustUnderstand, out relay, out actor); + if (element != null) + message.Headers.Add(new XmlElementMessageHeader(this, message.Version, element.LocalName, element.NamespaceURI, + mustUnderstand, actor, relay, element)); + } + } + } + catch (InvalidOperationException e) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException( + SR.Format(SR.SFxErrorSerializingHeader, messageDescription.MessageName, e.Message), e)); + } + } + + protected override void GetHeadersFromMessage(Message message, MessageDescription messageDescription, object[] parameters, bool isRequest) + { + try + { + XmlSerializer serializer; + MessageHeaderDescriptionTable headerDescriptionTable; + MessageHeaderDescription unknownHeaderDescription; + if (isRequest) + { + serializer = requestMessageInfo.HeaderSerializer; + headerDescriptionTable = requestMessageInfo.HeaderDescriptionTable; + unknownHeaderDescription = requestMessageInfo.UnknownHeaderDescription; + } + else + { + serializer = replyMessageInfo.HeaderSerializer; + headerDescriptionTable = replyMessageInfo.HeaderDescriptionTable; + unknownHeaderDescription = replyMessageInfo.UnknownHeaderDescription; + } + MessageHeaders headers = message.Headers; + ArrayList unknownHeaders = null; + XmlDocument xmlDoc = null; + if (unknownHeaderDescription != null) + { + unknownHeaders = new ArrayList(); + xmlDoc = new XmlDocument(); + } + if (serializer == null) + { + if (unknownHeaderDescription != null) + { + for (int headerIndex = 0; headerIndex < headers.Count; headerIndex++) + AddUnknownHeader(unknownHeaderDescription, unknownHeaders, xmlDoc, null/*bufferWriter*/, headers[headerIndex], headers.GetReaderAtHeader(headerIndex)); + parameters[unknownHeaderDescription.Index] = unknownHeaders.ToArray(unknownHeaderDescription.TypedHeader ? typeof(MessageHeader) : typeof(XmlElement)); + } + return; + } + + + MemoryStream memoryStream = new MemoryStream(); + XmlDictionaryWriter bufferWriter = XmlDictionaryWriter.CreateTextWriter(memoryStream); + message.WriteStartEnvelope(bufferWriter); + message.WriteStartHeaders(bufferWriter); + MessageHeaderOfTHelper messageHeaderOfTHelper = null; + for (int headerIndex = 0; headerIndex < headers.Count; headerIndex++) + { + MessageHeaderInfo header = headers[headerIndex]; + XmlDictionaryReader headerReader = headers.GetReaderAtHeader(headerIndex); + MessageHeaderDescription matchingHeaderDescription = headerDescriptionTable.Get(header.Name, header.Namespace); + if (matchingHeaderDescription != null) + { + if (header.MustUnderstand) + headers.UnderstoodHeaders.Add(header); + if (matchingHeaderDescription.TypedHeader) + { + if (messageHeaderOfTHelper == null) + messageHeaderOfTHelper = new MessageHeaderOfTHelper(parameters.Length); + messageHeaderOfTHelper.SetHeaderAttributes(matchingHeaderDescription, header.MustUnderstand, header.Relay, header.Actor); + } + + } + if (matchingHeaderDescription == null && unknownHeaderDescription != null) + AddUnknownHeader(unknownHeaderDescription, unknownHeaders, xmlDoc, bufferWriter, header, headerReader); + else + bufferWriter.WriteNode(headerReader, false); + headerReader.Dispose(); + } + bufferWriter.WriteEndElement(); + bufferWriter.WriteEndElement(); + bufferWriter.Flush(); + + /* + XmlDocument doc = new XmlDocument(); + memoryStream.Position = 0; + doc.Load(memoryStream); + doc.Save(Console.Out); + */ + + memoryStream.Position = 0; + ArraySegment memoryBuffer; + memoryStream.TryGetBuffer(out memoryBuffer); + XmlDictionaryReader bufferReader = XmlDictionaryReader.CreateTextReader(memoryBuffer.Array, 0, (int)memoryStream.Length, XmlDictionaryReaderQuotas.Max); + + bufferReader.ReadStartElement(); + bufferReader.MoveToContent(); + if (!bufferReader.IsEmptyElement) + { + bufferReader.ReadStartElement(); + // TODO: Use overload with encoding once available + object[] headerValues = (object[]) serializer.Deserialize(bufferReader); //, isEncoded ? GetEncoding(message.Version.Envelope) : null); + int headerIndex = 0; + foreach (MessageHeaderDescription headerDescription in messageDescription.Headers) + { + if (!headerDescription.IsUnknownHeaderCollection) + { + object parameterValue = headerValues[headerIndex++]; + if (headerDescription.TypedHeader && parameterValue != null) + parameterValue = messageHeaderOfTHelper.CreateMessageHeader(headerDescription, parameterValue); + parameters[headerDescription.Index] = parameterValue; + } + } + bufferReader.Dispose(); + } + if (unknownHeaderDescription != null) + parameters[unknownHeaderDescription.Index] = unknownHeaders.ToArray(unknownHeaderDescription.TypedHeader ? typeof(MessageHeader) : typeof(XmlElement)); + } + catch (InvalidOperationException e) + { + // all exceptions from XmlSerializer get wrapped in InvalidOperationException, + // so we must be conservative and never turn this into a fault + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException( + SR.Format(SR.SFxErrorDeserializingHeader, messageDescription.MessageName), e)); + } + } + + private static void AddUnknownHeader(MessageHeaderDescription unknownHeaderDescription, ArrayList unknownHeaders, XmlDocument xmlDoc, XmlDictionaryWriter bufferWriter, MessageHeaderInfo header, XmlDictionaryReader headerReader) + { + object unknownHeader = xmlDoc.ReadNode(headerReader); + if (bufferWriter != null) + ((XmlElement)unknownHeader).WriteTo(bufferWriter); + if (unknownHeader != null && unknownHeaderDescription.TypedHeader) + unknownHeader = TypedHeaderManager.Create(unknownHeaderDescription.Type, unknownHeader, header.MustUnderstand, header.Relay, header.Actor); + + unknownHeaders.Add(unknownHeader); + } + + protected override void WriteBodyAttributes(XmlDictionaryWriter writer, MessageVersion version) + { + if (isEncoded && version.Envelope == EnvelopeVersion.Soap11) + { + string encoding = GetEncoding(version.Envelope); + writer.WriteAttributeString("encodingStyle", version.Envelope.Namespace, encoding); + } + writer.WriteAttributeString("xmlns", "xsi", null, XmlUtil.XmlSerializerSchemaInstanceNamespace); + writer.WriteAttributeString("xmlns", "xsd", null, XmlUtil.XmlSerializerSchemaNamespace); + } + + protected override void SerializeBody(XmlDictionaryWriter writer, MessageVersion version, string action, MessageDescription messageDescription, object returnValue, object[] parameters, bool isRequest) + { + if (writer == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException(nameof(writer))); + if (parameters == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException(nameof(parameters))); + try + { + MessageInfo messageInfo; + if (isRequest) + messageInfo = requestMessageInfo; + else + messageInfo = replyMessageInfo; + if (messageInfo.RpcEncodedTypedMessageBodyParts == null) + { + SerializeBody(writer, version, messageInfo.BodySerializer, messageDescription.Body.ReturnValue, messageDescription.Body.Parts, returnValue, parameters); + return; + } + + object[] bodyPartValues = new object[messageInfo.RpcEncodedTypedMessageBodyParts.Count]; + object bodyObject = parameters[messageDescription.Body.Parts[0].Index]; + if (bodyObject == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxBodyCannotBeNull, messageDescription.MessageName))); + int i = 0; + foreach (MessagePartDescription bodyPart in messageInfo.RpcEncodedTypedMessageBodyParts) + { + MemberInfo member = bodyPart.MemberInfo; + FieldInfo field = member as FieldInfo; + if (field != null) + bodyPartValues[i++] = field.GetValue(bodyObject); + else + { + PropertyInfo property = member as PropertyInfo; + if (property != null) + bodyPartValues[i++] = property.GetValue(bodyObject, null); + } + } + SerializeBody(writer, version, messageInfo.BodySerializer, null/*returnPart*/, messageInfo.RpcEncodedTypedMessageBodyParts, null/*returnValue*/, bodyPartValues); + } + catch (InvalidOperationException e) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException( + SR.Format(SR.SFxErrorSerializingBody, messageDescription.MessageName, e.Message), e)); + } + } + + void SerializeBody(XmlDictionaryWriter writer, MessageVersion version, XmlSerializer serializer, MessagePartDescription returnPart, MessagePartDescriptionCollection bodyParts, object returnValue, object[] parameters) + { + if (serializer == null) + { + return; + } + + bool hasReturnValue = IsValidReturnValue(returnPart); + object[] bodyParameters = new object[bodyParts.Count + (hasReturnValue ? 1 : 0)]; + int paramIndex = 0; + + if (hasReturnValue) + bodyParameters[paramIndex++] = returnValue; + + for (int i = 0; i < bodyParts.Count; i++) + bodyParameters[paramIndex++] = parameters[bodyParts[i].Index]; + + // TODO: Switch to using encoding once available + //string encoding = isEncoded ? GetEncoding(version.Envelope) : null; + serializer.Serialize(writer, bodyParameters, null); //, encoding); + } + + + protected override object DeserializeBody(XmlDictionaryReader reader, MessageVersion version, string action, MessageDescription messageDescription, object[] parameters, bool isRequest) + { + MessageInfo messageInfo; + if (isRequest) + messageInfo = requestMessageInfo; + else + messageInfo = replyMessageInfo; + if (messageInfo.RpcEncodedTypedMessageBodyParts == null) + return DeserializeBody(reader, version, messageInfo.BodySerializer, messageDescription.Body.ReturnValue, messageDescription.Body.Parts, parameters, isRequest); + + object[] bodyPartValues = new object[messageInfo.RpcEncodedTypedMessageBodyParts.Count]; + DeserializeBody(reader, version, messageInfo.BodySerializer, null/*returnPart*/, messageInfo.RpcEncodedTypedMessageBodyParts, bodyPartValues, isRequest); + object bodyObject = Activator.CreateInstance(messageDescription.Body.Parts[0].Type); + int i = 0; + foreach (MessagePartDescription bodyPart in messageInfo.RpcEncodedTypedMessageBodyParts) + { + MemberInfo member = bodyPart.MemberInfo; + FieldInfo field = member as FieldInfo; + if (field != null) + field.SetValue(bodyObject, bodyPartValues[i++]); + else + { + PropertyInfo property = member as PropertyInfo; + if (property != null) + property.SetValue(bodyObject, bodyPartValues[i++], null); + } + } + parameters[messageDescription.Body.Parts[0].Index] = bodyObject; + return null; + } + + object DeserializeBody(XmlDictionaryReader reader, MessageVersion version, XmlSerializer serializer, MessagePartDescription returnPart, MessagePartDescriptionCollection bodyParts, object[] parameters, bool isRequest) + { + try + { + if (reader == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException(nameof(reader))); + if (parameters == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException(nameof(parameters))); + object returnValue = null; + if (serializer == null) + { + return null; + } + if (reader.NodeType == XmlNodeType.EndElement) + return null; + + // TODO: Use overload which takes encoding once available + object[] bodyParameters = (object[])serializer.Deserialize(reader); //, isEncoded ? GetEncoding(version.Envelope) : null); + + int paramIndex = 0; + if (IsValidReturnValue(returnPart)) + returnValue = bodyParameters[paramIndex++]; + + for (int i = 0; i < bodyParts.Count; i++) + parameters[bodyParts[i].Index] = bodyParameters[paramIndex++]; + return returnValue; + } + catch (InvalidOperationException e) + { + // all exceptions from XmlSerializer get wrapped in InvalidOperationException, + // so we must be conservative and never turn this into a fault + string resourceKey = isRequest ? SR.SFxErrorDeserializingRequestBody : SR.SFxErrorDeserializingReplyBody; + + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException( + SR.Format(resourceKey, OperationName), e)); + } + } + + internal static string GetEncoding(EnvelopeVersion version) + { + if (version == EnvelopeVersion.Soap11) + { + return soap11Encoding; + } + else if (version == EnvelopeVersion.Soap12) + { + return soap12Encoding; + } + else + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("version", + SR.Format(SR.EnvelopeVersionNotSupported, version)); + } + } + + internal abstract class MessageInfo + { + internal abstract XmlSerializer BodySerializer { get; } + internal abstract XmlSerializer HeaderSerializer { get; } + internal abstract MessageHeaderDescriptionTable HeaderDescriptionTable { get; } + internal abstract MessageHeaderDescription UnknownHeaderDescription { get; } + internal abstract MessagePartDescriptionCollection RpcEncodedTypedMessageBodyParts { get; } + } + + class MessageHeaderOfTHelper + { + object[] attributes; + internal MessageHeaderOfTHelper(int parameterCount) + { + attributes = new object[parameterCount]; + } + internal object GetContentAndSaveHeaderAttributes(object parameterValue, MessageHeaderDescription headerDescription) + { + if (parameterValue == null) + return null; + bool mustUnderstand; + bool relay; + string actor; + if (headerDescription.Multiple) + { + object[] messageHeaderOfTArray = (object[])parameterValue; + MessageHeader[] messageHeaderOfTAttributes = new MessageHeader[messageHeaderOfTArray.Length]; + Array tArray = Array.CreateInstance(headerDescription.Type, messageHeaderOfTArray.Length); + for (int i = 0; i < tArray.Length; i++) + { + tArray.SetValue(GetContentOfMessageHeaderOfT(headerDescription, messageHeaderOfTArray[i], out mustUnderstand, out relay, out actor), i); + messageHeaderOfTAttributes[i] = new MessageHeader(null, mustUnderstand, actor, relay); + } + attributes[headerDescription.Index] = messageHeaderOfTAttributes; + return tArray; + } + else + { + object content = GetContentOfMessageHeaderOfT(headerDescription, parameterValue, out mustUnderstand, out relay, out actor); + attributes[headerDescription.Index] = new MessageHeader(null, mustUnderstand, actor, relay); + return content; + } + + } + + internal void GetHeaderAttributes(MessageHeaderDescription headerDescription, out bool mustUnderstand, out bool relay, out string actor) + { + MessageHeader matchingMessageHeaderOfTAttribute = null; + if (headerDescription.Multiple) + { + MessageHeader[] messageHeaderOfTAttributes = (MessageHeader[])attributes[headerDescription.Index]; + for (int i = 0; i < messageHeaderOfTAttributes.Length; i++) + { + if (messageHeaderOfTAttributes[i] != null) + { + matchingMessageHeaderOfTAttribute = messageHeaderOfTAttributes[i]; + messageHeaderOfTAttributes[i] = null; + break; + } + } + //assert(matchingMessageHeaderOfTAttribute != null); + + } + else + matchingMessageHeaderOfTAttribute = (MessageHeader)attributes[headerDescription.Index]; + mustUnderstand = matchingMessageHeaderOfTAttribute.MustUnderstand; + relay = matchingMessageHeaderOfTAttribute.Relay; + actor = matchingMessageHeaderOfTAttribute.Actor; + } + + internal void SetHeaderAttributes(MessageHeaderDescription headerDescription, bool mustUnderstand, bool relay, string actor) + { + if (headerDescription.Multiple) + { + if (attributes[headerDescription.Index] == null) + attributes[headerDescription.Index] = new List>(); + ((List>)attributes[headerDescription.Index]).Add(new MessageHeader(null, mustUnderstand, actor, relay)); + } + else + attributes[headerDescription.Index] = new MessageHeader(null, mustUnderstand, actor, relay); + + } + internal object CreateMessageHeader(MessageHeaderDescription headerDescription, object headerValue) + { + if (headerDescription.Multiple) + { + IList> messageHeaderOfTAttributes = (IList>)attributes[headerDescription.Index]; + object[] messageHeaderOfTArray = (object[])Array.CreateInstance(TypedHeaderManager.GetMessageHeaderType(headerDescription.Type), messageHeaderOfTAttributes.Count); + Array headerValues = (Array)headerValue; + for (int i = 0; i < messageHeaderOfTArray.Length; i++) + { + MessageHeader messageHeaderOfTAttribute = messageHeaderOfTAttributes[i]; + messageHeaderOfTArray[i] = TypedHeaderManager.Create(headerDescription.Type, headerValues.GetValue(i), + messageHeaderOfTAttribute.MustUnderstand, messageHeaderOfTAttribute.Relay, messageHeaderOfTAttribute.Actor); + } + return messageHeaderOfTArray; + } + else + { + MessageHeader messageHeaderOfTAttribute = (MessageHeader)attributes[headerDescription.Index]; + return TypedHeaderManager.Create(headerDescription.Type, headerValue, + messageHeaderOfTAttribute.MustUnderstand, messageHeaderOfTAttribute.Relay, messageHeaderOfTAttribute.Actor); + + } + } + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/DnsEndpointIdentity.cs b/src/CoreWCF.Primitives/src/CoreWCF/DnsEndpointIdentity.cs new file mode 100644 index 000000000..1e3720b8c --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/DnsEndpointIdentity.cs @@ -0,0 +1,36 @@ +using System; +using System.Xml; +using CoreWCF.IdentityModel.Claims; + +namespace CoreWCF +{ + internal class DnsEndpointIdentity : EndpointIdentity + { + public DnsEndpointIdentity(string dnsName) + { + if (dnsName == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("dnsName"); + + base.Initialize(Claim.CreateDnsClaim(dnsName)); + } + + public DnsEndpointIdentity(Claim identity) + { + if (identity == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("identity"); + + if (!identity.ClaimType.Equals(ClaimTypes.Dns)) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.Format(SR.UnrecognizedClaimTypeForIdentity, identity.ClaimType, ClaimTypes.Dns)); + + base.Initialize(identity); + } + + internal override void WriteContentsTo(XmlDictionaryWriter writer) + { + if (writer == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer"); + + writer.WriteElementString(XD.AddressingDictionary.Dns, XD.AddressingDictionary.IdentityExtensionNamespace, (string)IdentityClaim.Resource); + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/EmptyArray.cs b/src/CoreWCF.Primitives/src/CoreWCF/EmptyArray.cs new file mode 100644 index 000000000..834f9135f --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/EmptyArray.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using CoreWCF.Collections.Generic; +using CoreWCF.Dispatcher; + +namespace CoreWCF +{ + internal class EmptyArray + { + internal static T[] Allocate(int n) + { + if (n == 0) + { + return Array.Empty(); + } + + return new T[n]; + } + internal static T[] ToArray(IList collection) + { + if (collection.Count == 0) + { + return Array.Empty(); + } + + T[] array = new T[collection.Count]; + collection.CopyTo(array, 0); + return array; + } + + internal static T[] ToArray(SynchronizedCollection collection) + { + lock (collection.SyncRoot) + { + return ToArray((IList)collection); + } + } + } + + internal class EmptyArray + { + internal static object[] Allocate(int n) + { + if (n == 0) + { + return Array.Empty(); + } + + return new object[n]; + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/EndpointAddress.cs b/src/CoreWCF.Primitives/src/CoreWCF/EndpointAddress.cs new file mode 100644 index 000000000..e541d322a --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/EndpointAddress.cs @@ -0,0 +1,1079 @@ +using System; +using System.Collections.ObjectModel; +using System.Xml; +using CoreWCF.Runtime; +using CoreWCF.Channels; + +namespace CoreWCF +{ + public class EndpointAddress + { + static Uri anonymousUri; + static Uri noneUri; + static EndpointAddress anonymousAddress; + + /* + Conceptually, the agnostic EndpointAddress class represents all of UNION(v200408,v10) data thusly: + - Address Uri (both versions - the Address) + - AddressHeaderCollection (both versions - RefProp&RefParam both project into here) + - PSP blob (200408 - this is PortType, ServiceName, Policy, it is not surfaced in OM) + - metadata (both versions, but weird semantics in 200408) + - identity (both versions, this is the one 'extension' that we know about) + - extensions (both versions, the "any*" stuff at the end) + + When reading from 200408: + - Address is projected into Uri + - both RefProps and RefParams are projected into AddressHeaderCollection, + they (internally) remember 'which kind' they are + - PortType, ServiceName, Policy are projected into the (internal) PSP blob + - if we see a wsx:metadata element next, we project that element and that element only into the metadata reader + - we read the rest, recognizing and fishing out identity if there, projecting rest to extensions reader + When reading from 10: + - Address is projected into Uri + - RefParams are projected into AddressHeaderCollection; they (internally) remember 'which kind' they are + - nothing is projected into the (internal) PSP blob (it's empty) + - if there's a wsa10:metadata element, everything inside it projects into metadatareader + - we read the rest, recognizing and fishing out identity if there, projecting rest to extensions reader + + When writing to 200408: + - Uri is written as Address + - AddressHeaderCollection is written as RefProps & RefParams, based on what they internally remember selves to be + - PSP blob is written out verbatim (will have: PortType?, ServiceName?, Policy?) + - metadata reader is written out verbatim + - identity is written out as extension + - extension reader is written out verbatim + When writing to 10: + - Uri is written as Address + - AddressHeaderCollection is all written as RefParams, regardless of what they internally remember selves to be + - PSP blob is ignored + - if metadata reader is non-empty, we write its value out verbatim inside a wsa10:metadata element + - identity is written out as extension + - extension reader is written out verbatim + + EndpointAddressBuilder: + - you can set metadata to any value you like; we don't (cannot) validate because 10 allows anything + - you can set any extensions you like + + Known Weirdnesses: + - PSP blob does not surface in OM - it can only roundtrip 200408wire->OM->200408wire + - RefProperty distinction does not surface in OM - it can only roundtrip 200408wire->OM->200408wire + - regardless of what metadata in reader, when you roundtrip OM->200408wire->OM, only wsx:metadata + as first element after PSP will stay in metadata, anything else gets dumped in extensions + - PSP blob is lost when doing OM->10wire->OM + - RefProps turn into RefParams when doing OM->10wire->OM + - Identity is always shuffled to front of extensions when doing anyWire->OM->anyWire + */ + + AddressingVersion addressingVersion; + AddressHeaderCollection headers; + EndpointIdentity identity; + Uri uri; + XmlBuffer buffer; // invariant: each section in the buffer will start with a dummy wrapper element + int extensionSection; + int metadataSection; + int pspSection; + bool isAnonymous; + bool isNone; + // these are the element name/namespace for the dummy wrapper element that wraps each buffer section + internal const string DummyName = "Dummy"; + internal const string DummyNamespace = "http://Dummy"; + + EndpointAddress(AddressingVersion version, Uri uri, EndpointIdentity identity, AddressHeaderCollection headers, XmlBuffer buffer, int metadataSection, int extensionSection, int pspSection) + { + Init(version, uri, identity, headers, buffer, metadataSection, extensionSection, pspSection); + } + + public EndpointAddress(string uri) + { + if (uri == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("uri"); + } + + Uri u = new Uri(uri); + + Init(u, (EndpointIdentity)null, (AddressHeaderCollection)null, null, -1, -1, -1); + } + + public EndpointAddress(Uri uri, params AddressHeader[] addressHeaders) + : this(uri, (EndpointIdentity)null, addressHeaders) + { + } + + public EndpointAddress(Uri uri, EndpointIdentity identity, params AddressHeader[] addressHeaders) + { + if (uri == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("uri"); + } + + Init(uri, identity, addressHeaders); + } + + internal EndpointAddress(Uri newUri, EndpointAddress oldEndpointAddress) + { + Init(oldEndpointAddress.addressingVersion, newUri, oldEndpointAddress.identity, oldEndpointAddress.headers, oldEndpointAddress.buffer, oldEndpointAddress.metadataSection, oldEndpointAddress.extensionSection, oldEndpointAddress.pspSection); + } + + //public EndpointAddress(Uri uri, EndpointIdentity identity, AddressHeaderCollection headers) + //{ + // if (uri == null) + // { + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("uri"); + // } + + // Init(uri, identity, headers, null, -1, -1, -1); + //} + + internal EndpointAddress(Uri uri, EndpointIdentity identity, AddressHeaderCollection headers, XmlDictionaryReader metadataReader, XmlDictionaryReader extensionReader, XmlDictionaryReader pspReader) + { + if (uri == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("uri"); + } + + XmlBuffer buffer = null; + PossiblyPopulateBuffer(metadataReader, ref buffer, out metadataSection); + + EndpointIdentity ident2; + int extSection; + buffer = ReadExtensions(extensionReader, null, buffer, out ident2, out extSection); + + if (identity != null && ident2 != null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.MultipleIdentities, nameof(extensionReader))); + } + + PossiblyPopulateBuffer(pspReader, ref buffer, out pspSection); + + if (buffer != null) + { + buffer.Close(); + } + + Init(uri, identity ?? ident2, headers, buffer, metadataSection, extSection, pspSection); + } + + //// metadataReader and extensionReader are assumed to not have a starting dummy wrapper element + //public EndpointAddress(Uri uri, EndpointIdentity identity, AddressHeaderCollection headers, XmlDictionaryReader metadataReader, XmlDictionaryReader extensionReader) + // : this(uri, identity, headers, metadataReader, extensionReader, null) + //{ + //} + + void Init(Uri uri, EndpointIdentity identity, AddressHeader[] headers) + { + if (headers == null || headers.Length == 0) + { + Init(uri, identity, (AddressHeaderCollection)null, null, -1, -1, -1); + } + else + { + Init(uri, identity, new AddressHeaderCollection(headers), null, -1, -1, -1); + } + } + + void Init(Uri uri, EndpointIdentity identity, AddressHeaderCollection headers, XmlBuffer buffer, int metadataSection, int extensionSection, int pspSection) + { + Init(null, uri, identity, headers, buffer, metadataSection, extensionSection, pspSection); + } + + void Init(AddressingVersion version, Uri uri, EndpointIdentity identity, AddressHeaderCollection headers, XmlBuffer buffer, int metadataSection, int extensionSection, int pspSection) + { + if (!uri.IsAbsoluteUri) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("uri", SR.UriMustBeAbsolute); + + addressingVersion = version; + this.uri = uri; + this.identity = identity; + this.headers = headers; + this.buffer = buffer; + this.metadataSection = metadataSection; + this.extensionSection = extensionSection; + this.pspSection = pspSection; + + if (version != null) + { + isAnonymous = uri == version.AnonymousUri; + isNone = uri == version.NoneUri; + } + else + { + isAnonymous = object.ReferenceEquals(uri, AnonymousUri) || uri == AnonymousUri; + isNone = object.ReferenceEquals(uri, NoneUri) || uri == NoneUri; + } + if (isAnonymous) + { + this.uri = AnonymousUri; + } + if (isNone) + { + this.uri = NoneUri; + } + } + + // TODO: This was internal, I needed to make it public. Is this a problem? + public static EndpointAddress AnonymousAddress + { + get + { + if (anonymousAddress == null) + anonymousAddress = new EndpointAddress(AnonymousUri); + return anonymousAddress; + } + } + + public static Uri AnonymousUri + { + get + { + if (anonymousUri == null) + anonymousUri = new Uri(AddressingStrings.AnonymousUri); + return anonymousUri; + } + } + + public static Uri NoneUri + { + get + { + if (noneUri == null) + noneUri = new Uri(AddressingStrings.NoneUri); + return noneUri; + } + } + + internal XmlBuffer Buffer + { + get + { + return buffer; + } + } + + public AddressHeaderCollection Headers + { + get + { + if (headers == null) + { + headers = new AddressHeaderCollection(); + } + + return headers; + } + } + + public EndpointIdentity Identity + { + get + { + return identity; + } + } + + public bool IsAnonymous + { + get + { + return isAnonymous; + } + } + + public bool IsNone + { + get + { + return isNone; + } + } + + public Uri Uri + { + get + { + return uri; + } + } + + public void ApplyTo(Message message) + { + if (message == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("message"); + + Uri uri = Uri; + if (IsAnonymous) + { + if (message.Version.Addressing == AddressingVersion.WSAddressing10) + { + message.Headers.To = null; + } + else + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new ProtocolException(SR.Format(SR.AddressingVersionNotSupported, message.Version.Addressing))); + } + } + else if (IsNone) + { + message.Headers.To = message.Version.Addressing.NoneUri; + } + else + { + message.Headers.To = uri; + } + message.Properties.Via = message.Headers.To; + if (headers != null) + { + headers.AddHeadersTo(message); + } + } + + // NOTE: UserInfo, Query, and Fragment are ignored when comparing Uris as addresses + // this is the WCF logic for comparing Uris that represent addresses + // this method must be kept in sync with UriGetHashCode + internal static bool UriEquals(Uri u1, Uri u2, bool ignoreCase, bool includeHostInComparison) + { + return UriEquals(u1, u2, ignoreCase, includeHostInComparison, true); + } + + internal static bool UriEquals(Uri u1, Uri u2, bool ignoreCase, bool includeHostInComparison, bool includePortInComparison) + { + // PERF: Equals compares everything but UserInfo and Fragments. It's more strict than + // we are, and faster, so it is done first. + if (u1.Equals(u2)) + { + return true; + } + + if (u1.Scheme != u2.Scheme) // Uri.Scheme is always lowercase + { + return false; + } + if (includePortInComparison) + { + if (u1.Port != u2.Port) + { + return false; + } + } + if (includeHostInComparison) + { + if (string.Compare(u1.Host, u2.Host, StringComparison.OrdinalIgnoreCase) != 0) + { + return false; + } + } + + if (string.Compare(u1.AbsolutePath, u2.AbsolutePath, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal) == 0) + { + return true; + } + + // Normalize for trailing slashes + string u1Path = u1.GetComponents(UriComponents.Path, UriFormat.Unescaped); + string u2Path = u2.GetComponents(UriComponents.Path, UriFormat.Unescaped); + int u1Len = (u1Path.Length > 0 && u1Path[u1Path.Length - 1] == '/') ? u1Path.Length - 1 : u1Path.Length; + int u2Len = (u2Path.Length > 0 && u2Path[u2Path.Length - 1] == '/') ? u2Path.Length - 1 : u2Path.Length; + if (u2Len != u1Len) + { + return false; + } + return string.Compare(u1Path, 0, u2Path, 0, u1Len, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal) == 0; + } + + // this method must be kept in sync with UriEquals + internal static int UriGetHashCode(Uri uri, bool includeHostInComparison) + { + return UriGetHashCode(uri, includeHostInComparison, true); + } + + internal static int UriGetHashCode(Uri uri, bool includeHostInComparison, bool includePortInComparison) + { + UriComponents components = UriComponents.Scheme | UriComponents.Path; + + if (includePortInComparison) + { + components = components | UriComponents.Port; + } + if (includeHostInComparison) + { + components = components | UriComponents.Host; + } + + // Normalize for trailing slashes + string uriString = uri.GetComponents(components, UriFormat.Unescaped); + if (uriString.Length > 0 && uriString[uriString.Length - 1] != '/') + uriString = string.Concat(uriString, "/"); + + return StringComparer.OrdinalIgnoreCase.GetHashCode(uriString); + } + + internal bool EndpointEquals(EndpointAddress endpointAddress) + { + if (endpointAddress == null) + { + return false; + } + + if (object.ReferenceEquals(this, endpointAddress)) + { + return true; + } + + Uri thisTo = Uri; + Uri otherTo = endpointAddress.Uri; + + if (!UriEquals(thisTo, otherTo, false /* ignoreCase */, true /* includeHostInComparison */)) + { + return false; + } + + if (Identity == null) + { + if (endpointAddress.Identity != null) + { + return false; + } + } + else if (!Identity.Equals(endpointAddress.Identity)) + { + return false; + } + + if (!Headers.IsEquivalent(endpointAddress.Headers)) + { + return false; + } + + return true; + } + + public override bool Equals(object obj) + { + if (object.ReferenceEquals(obj, this)) + { + return true; + } + + if (obj == null) + { + return false; + } + + EndpointAddress address = obj as EndpointAddress; + if (address == null) + { + return false; + } + + return EndpointEquals(address); + } + + public override int GetHashCode() + { + return UriGetHashCode(uri, true /* includeHostInComparison */); + } + + // returns reader without starting dummy wrapper element + internal XmlDictionaryReader GetReaderAtPsp() + { + return GetReaderAtSection(buffer, pspSection); + } + + //// returns reader without starting dummy wrapper element + internal XmlDictionaryReader GetReaderAtMetadata() + { + return GetReaderAtSection(buffer, metadataSection); + } + + //// returns reader without starting dummy wrapper element + internal XmlDictionaryReader GetReaderAtExtensions() + { + return GetReaderAtSection(buffer, extensionSection); + } + + static XmlDictionaryReader GetReaderAtSection(XmlBuffer buffer, int section) + { + if (buffer == null || section < 0) + return null; + + XmlDictionaryReader reader = buffer.GetReader(section); + reader.MoveToContent(); + Fx.Assert(reader.Name == DummyName, "EndpointAddress: Expected dummy element not found"); + reader.Read(); // consume the dummy wrapper element + return reader; + } + + void PossiblyPopulateBuffer(XmlDictionaryReader reader, ref XmlBuffer buffer, out int section) + { + if (reader == null) + { + section = -1; + } + else + { + if (buffer == null) + { + buffer = new XmlBuffer(short.MaxValue); + } + section = buffer.SectionCount; + XmlDictionaryWriter writer = buffer.OpenSection(reader.Quotas); + writer.WriteStartElement(DummyName, DummyNamespace); + Copy(writer, reader); + buffer.CloseSection(); + } + } + + //public static EndpointAddress ReadFrom(XmlDictionaryReader reader) + //{ + // AddressingVersion dummyVersion; + // return ReadFrom(reader, out dummyVersion); + //} + + internal static EndpointAddress ReadFrom(XmlDictionaryReader reader, out AddressingVersion version) + { + if (reader == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader"); + + reader.ReadFullStartElement(); + reader.MoveToContent(); + + if (reader.IsNamespaceUri(AddressingVersion.WSAddressing10.DictionaryNamespace)) + { + version = AddressingVersion.WSAddressing10; + } + //else if (reader.IsNamespaceUri(AddressingVersion.WSAddressingAugust2004.DictionaryNamespace)) + //{ + // version = AddressingVersion.WSAddressingAugust2004; + //} + else if (reader.NodeType != XmlNodeType.Element) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument( + "reader", SR.CannotDetectAddressingVersion); + } + else + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument( + "reader", SR.Format(SR.AddressingVersionNotSupported, reader.NamespaceURI)); + } + + EndpointAddress ea = ReadFromDriver(version, reader); + reader.ReadEndElement(); + return ea; + } + + //public static EndpointAddress ReadFrom(XmlDictionaryReader reader, XmlDictionaryString localName, XmlDictionaryString ns) + //{ + // AddressingVersion version; + // return ReadFrom(reader, localName, ns, out version); + //} + + internal static EndpointAddress ReadFrom(XmlDictionaryReader reader, XmlDictionaryString localName, XmlDictionaryString ns, out AddressingVersion version) + { + if (reader == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader"); + + reader.ReadFullStartElement(localName, ns); + reader.MoveToContent(); + + if (reader.IsNamespaceUri(AddressingVersion.WSAddressing10.DictionaryNamespace)) + { + version = AddressingVersion.WSAddressing10; + } + //else if (reader.IsNamespaceUri(AddressingVersion.WSAddressingAugust2004.DictionaryNamespace)) + //{ + // version = AddressingVersion.WSAddressingAugust2004; + //} + else if (reader.NodeType != XmlNodeType.Element) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument( + "reader", SR.CannotDetectAddressingVersion); + } + else + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument( + "reader", SR.Format(SR.AddressingVersionNotSupported, reader.NamespaceURI)); + } + + EndpointAddress ea = ReadFromDriver(version, reader); + reader.ReadEndElement(); + return ea; + } + + //public static EndpointAddress ReadFrom(AddressingVersion addressingVersion, XmlReader reader) + //{ + // return ReadFrom(addressingVersion, XmlDictionaryReader.CreateDictionaryReader(reader)); + //} + + //public static EndpointAddress ReadFrom(AddressingVersion addressingVersion, XmlReader reader, string localName, string ns) + //{ + // if (reader == null) + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader"); + // if (addressingVersion == null) + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("addressingVersion"); + + // XmlDictionaryReader dictReader = XmlDictionaryReader.CreateDictionaryReader(reader); + // dictReader.ReadFullStartElement(localName, ns); + // EndpointAddress ea = ReadFromDriver(addressingVersion, dictReader); + // reader.ReadEndElement(); + // return ea; + //} + + public static EndpointAddress ReadFrom(AddressingVersion addressingVersion, XmlDictionaryReader reader) + { + if (reader == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader"); + if (addressingVersion == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("addressingVersion"); + + reader.ReadFullStartElement(); + EndpointAddress ea = ReadFromDriver(addressingVersion, reader); + reader.ReadEndElement(); + return ea; + } + + //public static EndpointAddress ReadFrom(AddressingVersion addressingVersion, XmlDictionaryReader reader, XmlDictionaryString localName, XmlDictionaryString ns) + //{ + // if (reader == null) + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader"); + // if (addressingVersion == null) + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("addressingVersion"); + + // reader.ReadFullStartElement(localName, ns); + // EndpointAddress ea = ReadFromDriver(addressingVersion, reader); + // reader.ReadEndElement(); + // return ea; + //} + + static EndpointAddress ReadFromDriver(AddressingVersion addressingVersion, XmlDictionaryReader reader) + { + AddressHeaderCollection headers; + EndpointIdentity identity; + Uri uri; + XmlBuffer buffer; + bool isAnonymous; + int extensionSection; + int metadataSection; + int pspSection = -1; + + if (addressingVersion == AddressingVersion.WSAddressing10) + { + isAnonymous = ReadContentsFrom10(reader, out uri, out headers, out identity, out buffer, out metadataSection, out extensionSection); + } + //else if (addressingVersion == AddressingVersion.WSAddressingAugust2004) + //{ + // isAnonymous = ReadContentsFrom200408(reader, out uri, out headers, out identity, out buffer, out metadataSection, out extensionSection, out pspSection); + //} + else + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("addressingVersion", + SR.Format(SR.AddressingVersionNotSupported, addressingVersion)); + } + + if (isAnonymous && headers == null && identity == null && buffer == null) + { + return AnonymousAddress; + } + else + { + return new EndpointAddress(addressingVersion, uri, identity, headers, buffer, metadataSection, extensionSection, pspSection); + } + } + + internal static XmlBuffer ReadExtensions(XmlDictionaryReader reader, AddressingVersion version, XmlBuffer buffer, out EndpointIdentity identity, out int section) + { + if (reader == null) + { + identity = null; + section = -1; + return buffer; + } + + // EndpointIdentity and extensions + identity = null; + XmlDictionaryWriter bufferWriter = null; + reader.MoveToContent(); + while (reader.IsStartElement()) + { + if (reader.IsStartElement(XD.AddressingDictionary.Identity, XD.AddressingDictionary.IdentityExtensionNamespace)) + { + if (identity != null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateXmlException(reader, SR.Format(SR.UnexpectedDuplicateElement, XD.AddressingDictionary.Identity.Value, XD.AddressingDictionary.IdentityExtensionNamespace.Value))); + identity = EndpointIdentity.ReadIdentity(reader); + } + else if (version != null && reader.NamespaceURI == version.Namespace) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateXmlException(reader, SR.Format(SR.AddressingExtensionInBadNS, reader.LocalName, reader.NamespaceURI))); + } + else + { + if (bufferWriter == null) + { + if (buffer == null) + buffer = new XmlBuffer(short.MaxValue); + bufferWriter = buffer.OpenSection(reader.Quotas); + bufferWriter.WriteStartElement(DummyName, DummyNamespace); + } + + bufferWriter.WriteNode(reader, true); + } + reader.MoveToContent(); + } + + if (bufferWriter != null) + { + bufferWriter.WriteEndElement(); + buffer.CloseSection(); + section = buffer.SectionCount - 1; + } + else + { + section = -1; + } + + return buffer; + } + + static bool ReadContentsFrom10(XmlDictionaryReader reader, out Uri uri, out AddressHeaderCollection headers, out EndpointIdentity identity, out XmlBuffer buffer, out int metadataSection, out int extensionSection) + { + buffer = null; + extensionSection = -1; + metadataSection = -1; + + // Cache address string + if (!reader.IsStartElement(XD.AddressingDictionary.Address, XD.Addressing10Dictionary.Namespace)) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateXmlException(reader, SR.Format(SR.UnexpectedElementExpectingElement, reader.LocalName, reader.NamespaceURI, XD.AddressingDictionary.Address.Value, XD.Addressing10Dictionary.Namespace.Value))); + string address = reader.ReadElementContentAsString(); + + // Headers + if (reader.IsStartElement(XD.AddressingDictionary.ReferenceParameters, XD.Addressing10Dictionary.Namespace)) + { + headers = AddressHeaderCollection.ReadServiceParameters(reader); + } + else + { + headers = null; + } + + // Metadata + if (reader.IsStartElement(XD.Addressing10Dictionary.Metadata, XD.Addressing10Dictionary.Namespace)) + { + reader.ReadFullStartElement(); // the wsa10:Metadata element is never stored in the buffer + buffer = new XmlBuffer(short.MaxValue); + metadataSection = 0; + XmlDictionaryWriter writer = buffer.OpenSection(reader.Quotas); + writer.WriteStartElement(DummyName, DummyNamespace); + while (reader.NodeType != XmlNodeType.EndElement && !reader.EOF) + { + writer.WriteNode(reader, true); + } + writer.Flush(); + buffer.CloseSection(); + reader.ReadEndElement(); + } + + // Extensions + buffer = ReadExtensions(reader, AddressingVersion.WSAddressing10, buffer, out identity, out extensionSection); + if (buffer != null) + { + buffer.Close(); + } + + // Process Address + if (address == Addressing10Strings.Anonymous) + { + uri = AddressingVersion.WSAddressing10.AnonymousUri; + if (headers == null && identity == null) + { + return true; + } + } + else if (address == Addressing10Strings.NoneAddress) + { + uri = AddressingVersion.WSAddressing10.NoneUri; + return false; + } + else + { + if (!Uri.TryCreate(address, UriKind.Absolute, out uri)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.Format(SR.InvalidUriValue, address, XD.AddressingDictionary.Address.Value, XD.Addressing10Dictionary.Namespace.Value))); + } + } + return false; + } + + static XmlException CreateXmlException(XmlDictionaryReader reader, string message) + { + IXmlLineInfo lineInfo = reader as IXmlLineInfo; + if (lineInfo != null) + { + return new XmlException(message, null, lineInfo.LineNumber, lineInfo.LinePosition); + } + + return new XmlException(message); + } + + // this function has a side-effect on the reader (MoveToContent) + static bool Done(XmlDictionaryReader reader) + { + reader.MoveToContent(); + return (reader.NodeType == XmlNodeType.EndElement || reader.EOF); + } + + // copy all of reader to writer + static internal void Copy(XmlDictionaryWriter writer, XmlDictionaryReader reader) + { + while (!Done(reader)) + { + writer.WriteNode(reader, true); + } + } + + public override string ToString() + { + return uri.ToString(); + } + + public void WriteContentsTo(AddressingVersion addressingVersion, XmlDictionaryWriter writer) + { + if (writer == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer"); + } + + if (addressingVersion == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("addressingVersion"); + } + + if (addressingVersion == AddressingVersion.WSAddressing10) + { + WriteContentsTo10(writer); + } + else if (addressingVersion == AddressingVersion.None) + { + WriteContentsToNone(writer); + } + else + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("addressingVersion", + SR.Format(SR.AddressingVersionNotSupported, addressingVersion)); + } + } + + void WriteContentsToNone(XmlDictionaryWriter writer) + { + writer.WriteString(Uri.AbsoluteUri); + } + + void WriteContentsTo10(XmlDictionaryWriter writer) + { + // Address + writer.WriteStartElement(XD.AddressingDictionary.Address, XD.Addressing10Dictionary.Namespace); + if (isAnonymous) + { + writer.WriteString(XD.Addressing10Dictionary.Anonymous); + } + else if (isNone) + { + writer.WriteString(XD.Addressing10Dictionary.NoneAddress); + } + else + { + writer.WriteString(Uri.AbsoluteUri); + } + writer.WriteEndElement(); + + // Headers + if (headers != null && headers.Count > 0) + { + writer.WriteStartElement(XD.AddressingDictionary.ReferenceParameters, XD.Addressing10Dictionary.Namespace); + headers.WriteContentsTo(writer); + writer.WriteEndElement(); + } + + // Metadata + if (metadataSection >= 0) + { + XmlDictionaryReader reader = GetReaderAtSection(buffer, metadataSection); + writer.WriteStartElement(XD.Addressing10Dictionary.Metadata, XD.Addressing10Dictionary.Namespace); + Copy(writer, reader); + writer.WriteEndElement(); + } + + // EndpointIdentity + if (Identity != null) + { + Identity.WriteTo(writer); + } + + // Extensions + if (extensionSection >= 0) + { + XmlDictionaryReader reader = GetReaderAtSection(buffer, extensionSection); + while (reader.IsStartElement()) + { + if (reader.NamespaceURI == AddressingVersion.WSAddressing10.Namespace) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateXmlException(reader, SR.Format(SR.AddressingExtensionInBadNS, reader.LocalName, reader.NamespaceURI))); + } + + writer.WriteNode(reader, true); + } + } + } + + public static bool operator ==(EndpointAddress address1, EndpointAddress address2) + { + if (object.ReferenceEquals(address2, null)) + { + return (object.ReferenceEquals(address1, null)); + } + + return address2.Equals(address1); + } + + public static bool operator !=(EndpointAddress address1, EndpointAddress address2) + { + if (object.ReferenceEquals(address2, null)) + { + return !object.ReferenceEquals(address1, null); + } + + return !address2.Equals(address1); + } + } + + public class EndpointAddressBuilder + { + Uri uri; + EndpointIdentity identity; + Collection headers; + XmlBuffer extensionBuffer; // this buffer is wrapped just like in EndpointAddress + XmlBuffer metadataBuffer; // this buffer is wrapped just like in EndpointAddress + bool hasExtension; + bool hasMetadata; + EndpointAddress epr; + + public EndpointAddressBuilder() + { + headers = new Collection(); + } + + public EndpointAddressBuilder(EndpointAddress address) + { + if (address == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("address"); + } + + epr = address; + uri = address.Uri; + identity = address.Identity; + headers = new Collection(); + for (int i = 0; i < address.Headers.Count; i++) + { + headers.Add(address.Headers[i]); + } + } + + public Uri Uri + { + get { return uri; } + set { uri = value; } + } + + public EndpointIdentity Identity + { + get { return identity; } + set { identity = value; } + } + + public Collection Headers + { + get { return headers; } + } + + public XmlDictionaryReader GetReaderAtMetadata() + { + if (!hasMetadata) + { + return epr == null ? null : epr.GetReaderAtMetadata(); + } + + if (metadataBuffer == null) + { + return null; + } + + XmlDictionaryReader reader = metadataBuffer.GetReader(0); + reader.MoveToContent(); + Fx.Assert(reader.Name == EndpointAddress.DummyName, "EndpointAddressBuilder: Expected dummy element not found"); + reader.Read(); // consume the wrapper element + return reader; + } + + public void SetMetadataReader(XmlDictionaryReader reader) + { + hasMetadata = true; + metadataBuffer = null; + if (reader != null) + { + metadataBuffer = new XmlBuffer(short.MaxValue); + XmlDictionaryWriter writer = metadataBuffer.OpenSection(reader.Quotas); + writer.WriteStartElement(EndpointAddress.DummyName, EndpointAddress.DummyNamespace); + EndpointAddress.Copy(writer, reader); + metadataBuffer.CloseSection(); + metadataBuffer.Close(); + } + } + + public XmlDictionaryReader GetReaderAtExtensions() + { + if (!hasExtension) + { + return epr == null ? null : epr.GetReaderAtExtensions(); + } + + if (extensionBuffer == null) + { + return null; + } + + XmlDictionaryReader reader = extensionBuffer.GetReader(0); + reader.MoveToContent(); + Fx.Assert(reader.Name == EndpointAddress.DummyName, "EndpointAddressBuilder: Expected dummy element not found"); + reader.Read(); // consume the wrapper element + return reader; + } + + public void SetExtensionReader(XmlDictionaryReader reader) + { + hasExtension = true; + EndpointIdentity identity; + int tmp; + extensionBuffer = EndpointAddress.ReadExtensions(reader, null, null, out identity, out tmp); + if (extensionBuffer != null) + { + extensionBuffer.Close(); + } + if (identity != null) + { + this.identity = identity; + } + } + + public EndpointAddress ToEndpointAddress() + { + return new EndpointAddress( + uri, + identity, + new AddressHeaderCollection(headers), + GetReaderAtMetadata(), + GetReaderAtExtensions(), + epr == null ? null : epr.GetReaderAtPsp()); + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/EndpointIdentity.cs b/src/CoreWCF.Primitives/src/CoreWCF/EndpointIdentity.cs new file mode 100644 index 000000000..f812d3213 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/EndpointIdentity.cs @@ -0,0 +1,220 @@ +using System; +using System.Collections.Generic; +using System.Xml; +using CoreWCF.IdentityModel.Claims; +using System.Security.Cryptography.X509Certificates; + +namespace CoreWCF +{ + public abstract class EndpointIdentity + { + private Claim _identityClaim; + private IEqualityComparer _claimComparer; + + protected EndpointIdentity() + { + } + + internal void Initialize(Claim identityClaim) + { + if (identityClaim == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("identityClaim"); + + Initialize(identityClaim, null); + } + + internal void Initialize(Claim identityClaim, IEqualityComparer claimComparer) + { + if (identityClaim == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("identityClaim"); + + _identityClaim = identityClaim; + _claimComparer = claimComparer; + } + + internal Claim IdentityClaim + { + get + { + if (_identityClaim == null) + { + EnsureIdentityClaim(); + } + return _identityClaim; + } + } + + public static EndpointIdentity CreateIdentity(Claim identity) + { + if (identity == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("identity"); + + if (identity.ClaimType.Equals(ClaimTypes.Dns)) + { + return new DnsEndpointIdentity(identity); + } + else if (identity.ClaimType.Equals(ClaimTypes.Spn)) + { + throw new PlatformNotSupportedException(); + //return new SpnEndpointIdentity(identity); + } + else if (identity.ClaimType.Equals(ClaimTypes.Upn)) + { + throw new PlatformNotSupportedException(); + //return new UpnEndpointIdentity(identity); + } + else if (identity.ClaimType.Equals(ClaimTypes.Rsa)) + { + throw new PlatformNotSupportedException(); + //return new RsaEndpointIdentity(identity); + } + else + { + return new GeneralEndpointIdentity(identity); + } + } + + internal static EndpointIdentity CreateDnsIdentity(string dnsName) + { + return new DnsEndpointIdentity(dnsName); + } + + internal static EndpointIdentity CreateSpnIdentity(string spnName) + { + return new SpnEndpointIdentity(spnName); + } + + internal static EndpointIdentity CreateUpnIdentity(string upnName) + { + return new UpnEndpointIdentity(upnName); + } + + public static EndpointIdentity CreateX509CertificateIdentity(X509Certificate2 certificate) + { + return new X509CertificateEndpointIdentity(certificate); + } + + internal virtual void EnsureIdentityClaim() + { + } + + public override bool Equals(object obj) + { + if (obj == (object)this) + return true; + + // as handles null do we need the double null check? + if (obj == null) + return false; + + EndpointIdentity otherIdentity = obj as EndpointIdentity; + if (otherIdentity == null) + return false; + + return Matches(otherIdentity.IdentityClaim); + } + + public override int GetHashCode() + { + return GetClaimComparer().GetHashCode(IdentityClaim); + } + + internal bool Matches(Claim claim) + { + return GetClaimComparer().Equals(IdentityClaim, claim); + } + + private IEqualityComparer GetClaimComparer() + { + if (_claimComparer == null) + { + throw new PlatformNotSupportedException("EndpointIdentity.GetClaimComparer is not supported."); + } + + return _claimComparer; + } + + + internal static EndpointIdentity ReadIdentity(XmlDictionaryReader reader) + { + if (reader == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader"); + + EndpointIdentity readIdentity = null; + + reader.MoveToContent(); + if (reader.IsEmptyElement) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.Format(SR.UnexpectedEmptyElementExpectingClaim, XD.AddressingDictionary.Identity.Value, XD.AddressingDictionary.IdentityExtensionNamespace.Value))); + + reader.ReadStartElement(XD.AddressingDictionary.Identity, XD.AddressingDictionary.IdentityExtensionNamespace); + + //if (reader.IsStartElement(XD.AddressingDictionary.Spn, XD.AddressingDictionary.IdentityExtensionNamespace)) + //{ + // readIdentity = new SpnEndpointIdentity(reader.ReadElementString()); + //} + //else if (reader.IsStartElement(XD.AddressingDictionary.Upn, XD.AddressingDictionary.IdentityExtensionNamespace)) + //{ + // readIdentity = new UpnEndpointIdentity(reader.ReadElementString()); + //} + //else + if (reader.IsStartElement(XD.AddressingDictionary.Dns, XD.AddressingDictionary.IdentityExtensionNamespace)) + { + readIdentity = new DnsEndpointIdentity(reader.ReadElementString()); + } + else if (reader.IsStartElement(XD.XmlSignatureDictionary.KeyInfo, XD.XmlSignatureDictionary.Namespace)) + { + reader.ReadStartElement(); + if (reader.IsStartElement(XD.XmlSignatureDictionary.X509Data, XD.XmlSignatureDictionary.Namespace)) + { + throw new PlatformNotSupportedException("EndpointIdentity.ReadIdentity X509CertificateEndpointIdentity is not supported."); + //readIdentity = new X509CertificateEndpointIdentity(reader); + } + else if (reader.IsStartElement(XD.XmlSignatureDictionary.RsaKeyValue, XD.XmlSignatureDictionary.Namespace)) + { + throw new PlatformNotSupportedException("EndpointIdentity.ReadIdentity RsaEndpointIdentity is not supported."); + } + else + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.Format(SR.UnrecognizedIdentityType, reader.Name, reader.NamespaceURI))); + } + //reader.ReadEndElement(); + } + else if (reader.NodeType == XmlNodeType.Element) + { + // + // Something unknown + // + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.Format(SR.UnrecognizedIdentityType, reader.Name, reader.NamespaceURI))); + } + else + { + // + // EndpointIdentity element is empty or some other invalid xml + // + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.InvalidIdentityElement)); + } + + reader.ReadEndElement(); + + return readIdentity; + } + + internal void WriteTo(XmlDictionaryWriter writer) + { + if (writer == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer"); + + writer.WriteStartElement(XD.AddressingDictionary.Identity, XD.AddressingDictionary.IdentityExtensionNamespace); + + WriteContentsTo(writer); + + writer.WriteEndElement(); + } + + internal virtual void WriteContentsTo(XmlDictionaryWriter writer) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.UnrecognizedIdentityPropertyType, IdentityClaim.GetType().ToString()))); + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/EndpointNotFoundException.cs b/src/CoreWCF.Primitives/src/CoreWCF/EndpointNotFoundException.cs new file mode 100644 index 000000000..f03f8b213 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/EndpointNotFoundException.cs @@ -0,0 +1,13 @@ +using System; + +namespace CoreWCF +{ + //[Serializable] + public class EndpointNotFoundException : CommunicationException + { + public EndpointNotFoundException() { } + public EndpointNotFoundException(string message) : base(message) { } + public EndpointNotFoundException(string message, Exception innerException) : base(message, innerException) { } + //protected EndpointNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context) { } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/EnvelopeVersion.cs b/src/CoreWCF.Primitives/src/CoreWCF/EnvelopeVersion.cs new file mode 100644 index 000000000..105332a36 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/EnvelopeVersion.cs @@ -0,0 +1,109 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// ------------------------------------------------------------------------------ +// Changes to this file must follow the http://aka.ms/api-review process. +// ------------------------------------------------------------------------------ + +using System; +using System.Xml; + +namespace CoreWCF +{ + public sealed class EnvelopeVersion + { + string _actor; + string _toStringFormat; + + EnvelopeVersion(string ultimateReceiverActor, string nextDestinationActorValue, + string ns, XmlDictionaryString dictionaryNs, string actor, XmlDictionaryString dictionaryActor, + string toStringFormat, string senderFaultName, string receiverFaultName) + { + _toStringFormat = toStringFormat; + UltimateDestinationActor = ultimateReceiverActor; + NextDestinationActorValue = nextDestinationActorValue; + Namespace = ns; + DictionaryNamespace = dictionaryNs; + _actor = actor; + DictionaryActor = dictionaryActor; + SenderFaultName = senderFaultName; + ReceiverFaultName = receiverFaultName; + + if (ultimateReceiverActor != null) + { + if (ultimateReceiverActor.Length == 0) + { + MustUnderstandActorValues = new string[] { "", nextDestinationActorValue }; + UltimateDestinationActorValues = new string[] { "", nextDestinationActorValue }; + } + else + { + MustUnderstandActorValues = new string[] { "", ultimateReceiverActor, nextDestinationActorValue }; + UltimateDestinationActorValues = new string[] { "", ultimateReceiverActor, nextDestinationActorValue }; + } + } + } + + internal XmlDictionaryString DictionaryActor { get; } + + internal string Namespace { get; } + + internal XmlDictionaryString DictionaryNamespace { get; } + + public string NextDestinationActorValue { get; } + + public static EnvelopeVersion None { get; } = new EnvelopeVersion( + null, + null, + MessageStrings.Namespace, + XD.MessageDictionary.Namespace, + null, + null, + SR.EnvelopeNoneToStringFormat, + "Sender", + "Receiver"); + + public static EnvelopeVersion Soap11 { get; } = new EnvelopeVersion( + "", + "http://schemas.xmlsoap.org/soap/actor/next", + Message11Strings.Namespace, + XD.Message11Dictionary.Namespace, + Message11Strings.Actor, + XD.Message11Dictionary.Actor, + SR.Soap11ToStringFormat, + "Client", + "Server"); + + public static EnvelopeVersion Soap12 { get; } = new EnvelopeVersion( + "http://www.w3.org/2003/05/soap-envelope/role/ultimateReceiver", + "http://www.w3.org/2003/05/soap-envelope/role/next", + Message12Strings.Namespace, + XD.Message12Dictionary.Namespace, + Message12Strings.Role, + XD.Message12Dictionary.Role, + SR.Soap12ToStringFormat, + "Sender", + "Receiver"); + + internal string ReceiverFaultName { get; } + + internal string[] MustUnderstandActorValues { get; } + + internal string UltimateDestinationActor { get; } + + public string[] GetUltimateDestinationActorValues() => (string[])UltimateDestinationActorValues.Clone(); + + internal string[] UltimateDestinationActorValues { get; } + + internal bool IsUltimateDestinationActor(string actor) + { + return actor.Length == 0 || actor == UltimateDestinationActor || actor == NextDestinationActorValue; + } + + public override string ToString() + { + return SR.Format(_toStringFormat, Namespace); + } + + internal string SenderFaultName { get; } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/ExceptionDetail.cs b/src/CoreWCF.Primitives/src/CoreWCF/ExceptionDetail.cs new file mode 100644 index 000000000..55ad98f06 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/ExceptionDetail.cs @@ -0,0 +1,69 @@ +using System; +using System.Globalization; +using System.Runtime.Serialization; +using System.Text; + +namespace CoreWCF +{ + [DataContract] + public class ExceptionDetail + { + public ExceptionDetail(Exception exception) + { + if (exception == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("exception"); + } + + HelpLink = exception.HelpLink; + Message = exception.Message; + StackTrace = exception.StackTrace; + Type = exception.GetType().ToString(); + + if (exception.InnerException != null) + { + InnerException = new ExceptionDetail(exception.InnerException); + } + } + + [DataMember] + public string HelpLink { get; set; } + + [DataMember] + public ExceptionDetail InnerException { get; set; } + + [DataMember] + public string Message { get; set; } + + [DataMember] + public string StackTrace { get; set; } + + [DataMember] + public string Type { get; set; } + + public override string ToString() + { + return string.Format(CultureInfo.InvariantCulture, "{0}\n{1}", SR.SFxExceptionDetailFormat, ToStringHelper(false)); + } + + internal string ToStringHelper(bool isInner) + { + StringBuilder sb = new StringBuilder(); + sb.AppendFormat("{0}: {1}", Type, Message); + if (InnerException != null) + { + sb.AppendFormat(" ----> {0}", InnerException.ToStringHelper(true)); + } + else + { + sb.Append("\n"); + } + sb.Append(StackTrace); + if (isInner) + { + sb.AppendFormat("\n {0}\n", SR.SFxExceptionDetailEndOfInner); + } + return sb.ToString(); + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/ExceptionMapper.cs b/src/CoreWCF.Primitives/src/CoreWCF/ExceptionMapper.cs new file mode 100644 index 000000000..c59671187 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/ExceptionMapper.cs @@ -0,0 +1,76 @@ +using CoreWCF.Runtime; +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoreWCF +{ + /// + /// Defines the mapping to be used for translating exceptions to faults. + /// + public class ExceptionMapper + { + internal const string SoapSenderFaultCode = "Sender"; + + /// + /// ExceptionMapper constructor. + /// + public ExceptionMapper() + { + } + + /// + /// Translates the input exception to a fault using the mapping defined in ExceptionMap. + /// + /// The exception to be mapped to a fault. + /// The fault corresponding to the input exception. + public virtual FaultException FromException(Exception ex) + { + return FromException(ex, string.Empty, string.Empty); + } + + /// + /// Translates the input exception to a fault using the mapping defined in ExceptionMap. + /// + /// The exception to be mapped to a fault. + /// The SOAP Namespace to be used when generating the mapped fault. + /// The WS-Trust Namespace to be used when generating the mapped fault. + /// The fault corresponding to the input exception. + public virtual FaultException FromException(Exception ex, string soapNamespace, string trustNamespace) + { + return null; + } + + /// + /// Determines whether an exception that occurred during the processing of a security token + /// should be handled using the defined ExceptionMap. + /// + /// The input exception. + /// A boolean value indicating whether the exception should be handled using the defined ExceptionMap. + public virtual bool HandleSecurityTokenProcessingException(Exception ex) + { + if (Fx.IsFatal(ex)) + { + return false; + } + + if (ex is FaultException) + { + // Just throw the original exception. + return false; + } + else + { + FaultException faultException = FromException(ex); + if (faultException != null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(faultException); + } + + // The exception is not one of the recognized exceptions. Just throw the original exception. + return false; + } + } + } + +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/ExtensionCollection.cs b/src/CoreWCF.Primitives/src/CoreWCF/ExtensionCollection.cs new file mode 100644 index 000000000..56a8b327f --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/ExtensionCollection.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using CoreWCF.Collections.Generic; + +namespace CoreWCF +{ + public sealed class ExtensionCollection : SynchronizedCollection>, IExtensionCollection + where T : IExtensibleObject + { + readonly T _owner; + + public ExtensionCollection(T owner) + { + if (owner == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(owner)); + + _owner = owner; + } + + public ExtensionCollection(T owner, object syncRoot) + : base(syncRoot) + { + if (owner == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(owner)); + + _owner = owner; + } + + bool ICollection>.IsReadOnly + { + get { return false; } + } + + protected override void ClearItems() + { + IExtension[] array; + + lock (SyncRoot) + { + array = new IExtension[Count]; + CopyTo(array, 0); + base.ClearItems(); + + foreach (IExtension extension in array) + { + extension.Detach(_owner); + } + } + } + + public TE Find() + { + List> items = Items; + + lock (SyncRoot) + { + for (int i = Count - 1; i >= 0; i--) + { + IExtension item = items[i]; + if (item is TE) + return (TE)item; + } + } + + return default(TE); + } + + public Collection FindAll() + { + Collection result = new Collection(); + List> items = Items; + + lock (SyncRoot) + { + for (int i = 0; i < items.Count; i++) + { + IExtension item = items[i]; + if (item is TE) + result.Add((TE)item); + } + } + + return result; + } + + protected override void InsertItem(int index, IExtension item) + { + if (item == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(item)); + + lock (SyncRoot) + { + item.Attach(_owner); + base.InsertItem(index, item); + } + } + + protected override void RemoveItem(int index) + { + lock (SyncRoot) + { + Items[index].Detach(_owner); + base.RemoveItem(index); + } + } + + protected override void SetItem(int index, IExtension item) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.SFxCannotSetExtensionsByIndex)); + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/FaultCode.cs b/src/CoreWCF.Primitives/src/CoreWCF/FaultCode.cs new file mode 100644 index 000000000..9add5b0e0 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/FaultCode.cs @@ -0,0 +1,124 @@ +using System; +using CoreWCF.Description; + +namespace CoreWCF +{ + public class FaultCode + { + FaultCode subCode; + string name; + string ns; + EnvelopeVersion version; + + public FaultCode(string name) + : this(name, "", null) + { + } + + public FaultCode(string name, FaultCode subCode) + : this(name, "", subCode) + { + } + + public FaultCode(string name, string ns) + : this(name, ns, null) + { + } + + public FaultCode(string name, string ns, FaultCode subCode) + { + if (name == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(name)); + if (name.Length == 0) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(name))); + + if (!string.IsNullOrEmpty(ns)) + NamingHelper.CheckUriParameter(ns, "ns"); + + this.name = name; + this.ns = ns; + this.subCode = subCode; + + if (ns == Message12Strings.Namespace) + version = EnvelopeVersion.Soap12; + else if (ns == Message11Strings.Namespace) + version = EnvelopeVersion.Soap11; + else if (ns == MessageStrings.Namespace) + version = EnvelopeVersion.None; + else + version = null; + } + + public bool IsPredefinedFault + { + get + { + return ns.Length == 0 || version != null; + } + } + + public bool IsSenderFault + { + get + { + if (IsPredefinedFault) + return name == (version ?? EnvelopeVersion.Soap12).SenderFaultName; + + return false; + } + } + + public bool IsReceiverFault + { + get + { + if (IsPredefinedFault) + return name == (version ?? EnvelopeVersion.Soap12).ReceiverFaultName; + + return false; + } + } + + public string Namespace + { + get + { + return ns; + } + } + + public string Name + { + get + { + return name; + } + } + + public FaultCode SubCode + { + get + { + return subCode; + } + } + + public static FaultCode CreateSenderFaultCode(FaultCode subCode) + { + return new FaultCode("Sender", subCode); + } + + // TODO: Consider making this public + internal static FaultCode CreateSenderFaultCode(string name, string ns) + { + return CreateSenderFaultCode(new FaultCode(name, ns)); + } + + internal static FaultCode CreateReceiverFaultCode(FaultCode subCode) + { + if (subCode == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(subCode)); + return new FaultCode("Receiver", subCode); + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/FaultCodeConstants.cs b/src/CoreWCF.Primitives/src/CoreWCF/FaultCodeConstants.cs new file mode 100644 index 000000000..0d98c05a4 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/FaultCodeConstants.cs @@ -0,0 +1,32 @@ +namespace CoreWCF +{ + internal class FaultCodeConstants + { + public static class Namespaces + { + public const string NetDispatch = "http://schemas.microsoft.com/net/2005/12/windowscommunicationfoundation/dispatcher"; + //public const string Transactions = "http://schemas.microsoft.com/net/2005/12/windowscommunicationfoundation/transactions"; + } + + public static class Codes + { + public const string DeserializationFailed = "DeserializationFailed"; + public const string SessionTerminated = "SessionTerminated"; + public const string InternalServiceFault = "InternalServiceFault"; + + // 'Transactions' feature fault codes + //public const string TransactionHeaderMalformed = "TransactionHeaderMalformed"; + //public const string TransactionHeaderMissing = "TransactionHeaderMissing"; + //public const string TransactionUnmarshalingFailed = "TransactionUnmarshalingFailed"; + //public const string TransactionIsolationLevelMismatch = "TransactionIsolationLevelMismatch"; + //public const string TransactionAborted = "TransactionAborted"; + //public const string IssuedTokenFlowNotAllowed = "IssuedTokenFlowNotAllowed"; + } + + public static class Actions + { + public const string NetDispatcher = "http://schemas.microsoft.com/net/2005/12/windowscommunicationfoundation/dispatcher/fault"; + //public const string Transactions = "http://schemas.microsoft.com/net/2005/12/windowscommunicationfoundation/transactions/fault"; + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/FaultContractAttribute.cs b/src/CoreWCF.Primitives/src/CoreWCF/FaultContractAttribute.cs new file mode 100644 index 000000000..5e78d6fd3 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/FaultContractAttribute.cs @@ -0,0 +1,63 @@ +using System; +using CoreWCF.Description; + +namespace CoreWCF +{ + [AttributeUsage(ServiceModelAttributeTargets.OperationContract, AllowMultiple = true, Inherited = false)] + public sealed class FaultContractAttribute : Attribute + { + string action; + string name; + string ns; + Type type; + + public FaultContractAttribute(Type detailType) + { + if (detailType == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(detailType)); + + type = detailType; + } + + public Type DetailType + { + get { return type; } + } + + public string Action + { + get { return action; } + set + { + if (value == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value"); + action = value; + } + } + + public string Name + { + get { return name; } + set + { + if (value == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value"); + if (value == string.Empty) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(value), + SR.SFxNameCannotBeEmpty)); + name = value; + } + } + + public string Namespace + { + get { return ns; } + set + { + if (!string.IsNullOrEmpty(value)) + NamingHelper.CheckUriProperty(value, "Namespace"); + ns = value; + } + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/FaultException.cs b/src/CoreWCF.Primitives/src/CoreWCF/FaultException.cs new file mode 100644 index 000000000..2b0031198 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/FaultException.cs @@ -0,0 +1,276 @@ +using System; +using System.Collections.ObjectModel; +using System.Runtime.Serialization; +using CoreWCF.Runtime; +using CoreWCF.Channels; +using CoreWCF.Dispatcher; + +namespace CoreWCF +{ + [KnownType(typeof(FaultException.FaultCodeData))] + [KnownType(typeof(FaultException.FaultCodeData[]))] + [KnownType(typeof(FaultException.FaultReasonData))] + [KnownType(typeof(FaultException.FaultReasonData[]))] + public class FaultException : CommunicationException + { + internal const string Namespace = "http://schemas.xmlsoap.org/Microsoft/WindowsCommunicationFoundation/2005/08/Faults/"; + + string action; + FaultCode code; + FaultReason reason; + MessageFault fault; + public FaultException() + : base(SR.SFxFaultReason) + { + code = FaultException.DefaultCode; + reason = FaultException.DefaultReason; + } + + internal FaultException(string reason, FaultCode code) + : base(reason) + { + this.code = FaultException.EnsureCode(code); + this.reason = FaultException.CreateReason(reason); + } + + public FaultException(FaultReason reason, FaultCode code, string action) + : base(FaultException.GetSafeReasonText(reason)) + { + this.code = FaultException.EnsureCode(code); + this.reason = FaultException.EnsureReason(reason); + this.action = action; + } + + internal FaultException(string reason, FaultCode code, string action, Exception innerException) + : base(reason, innerException) + { + this.code = FaultException.EnsureCode(code); + this.reason = FaultException.CreateReason(reason); + this.action = action; + } + + internal FaultException(FaultReason reason, FaultCode code, string action, Exception innerException) + : base(FaultException.GetSafeReasonText(reason), innerException) + { + this.code = FaultException.EnsureCode(code); + this.reason = FaultException.EnsureReason(reason); + this.action = action; + } + + public FaultException(MessageFault fault, string action) + : base(FaultException.GetSafeReasonText(GetReason(fault))) + { + if (fault == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("fault"); + + code = fault.Code; + reason = fault.Reason; + this.fault = fault; + this.action = action; + } + + // public on full framework + internal FaultException(FaultReason reason, FaultCode code) + : base(FaultException.GetSafeReasonText(reason)) + { + this.code = FaultException.EnsureCode(code); + this.reason = FaultException.EnsureReason(reason); + } + + public string Action + { + get { return action; } + } + + public FaultCode Code + { + get { return code; } + } + + internal static FaultReason DefaultReason + { + get { return new FaultReason(SR.SFxFaultReason); } + } + + internal static FaultCode DefaultCode + { + get { return new FaultCode("Sender"); } + } + + public override string Message + { + get { return FaultException.GetSafeReasonText(Reason); } + } + + public FaultReason Reason + { + get { return reason; } + } + + internal MessageFault Fault + { + get { return fault; } + } + + public static FaultException CreateFault(MessageFault messageFault, params Type[] faultDetailTypes) + { + return CreateFault(messageFault, null, faultDetailTypes); + } + + public static FaultException CreateFault(MessageFault messageFault, string action, params Type[] faultDetailTypes) + { + if (messageFault == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("messageFault"); + } + + if (faultDetailTypes == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("faultDetailTypes"); + } + DataContractSerializerFaultFormatter faultFormatter = new DataContractSerializerFaultFormatter(faultDetailTypes); + return faultFormatter.Deserialize(messageFault, action); + } + + public virtual Channels.MessageFault CreateMessageFault() { return default(Channels.MessageFault); } + + internal static string GetSafeReasonText(FaultReason reason) + { + if (reason == null) + return SR.SFxUnknownFaultNullReason0; + + try + { + return reason.GetMatchingTranslation(System.Globalization.CultureInfo.CurrentCulture).Text; + } + catch (ArgumentException) + { + if (reason.Translations.Count == 0) + { + return SR.SFxUnknownFaultZeroReasons0; + } + else + { + return SR.Format(SR.SFxUnknownFaultNoMatchingTranslation1, reason.Translations[0].Text); + } + } + } + + static FaultReason CreateReason(string reason) + { + return (reason != null) ? new FaultReason(reason) : DefaultReason; + } + + static FaultReason GetReason(MessageFault fault) + { + if (fault == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(fault)); + } + return fault.Reason; + } + + static FaultCode EnsureCode(FaultCode code) + { + return code ?? DefaultCode; + } + + static FaultReason EnsureReason(FaultReason reason) + { + return reason ?? DefaultReason; + } + + internal class FaultCodeData + { + string name; + string ns; + + internal static FaultCode Construct(FaultCodeData[] nodes) + { + FaultCode code = null; + + for (int i = nodes.Length - 1; i >= 0; i--) + { + code = new FaultCode(nodes[i].name, nodes[i].ns, code); + } + + return code; + } + + internal static FaultCodeData[] GetObjectData(FaultCode code) + { + FaultCodeData[] array = new FaultCodeData[FaultCodeData.GetDepth(code)]; + + for (int i = 0; i < array.Length; i++) + { + array[i] = new FaultCodeData(); + array[i].name = code.Name; + array[i].ns = code.Namespace; + code = code.SubCode; + } + + if (code != null) + { + Fx.Assert("FaultException.FaultCodeData.GetObjectData: (code != null)"); + } + return array; + } + + static int GetDepth(FaultCode code) + { + int depth = 0; + + while (code != null) + { + depth++; + code = code.SubCode; + } + + return depth; + } + } + + //[Serializable] + internal class FaultReasonData + { + string xmlLang; + string text; + + internal static FaultReason Construct(FaultReasonData[] nodes) + { + FaultReasonText[] reasons = new FaultReasonText[nodes.Length]; + + for (int i = 0; i < nodes.Length; i++) + { + reasons[i] = new FaultReasonText(nodes[i].text, nodes[i].xmlLang); + } + + return new FaultReason(reasons); + } + + internal static FaultReasonData[] GetObjectData(FaultReason reason) + { + ReadOnlyCollection translations = reason.Translations; + FaultReasonData[] array = new FaultReasonData[translations.Count]; + + for (int i = 0; i < translations.Count; i++) + { + array[i] = new FaultReasonData(); + array[i].xmlLang = translations[i].XmlLang; + array[i].text = translations[i].Text; + } + + return array; + } + } + + } + + public class FaultException : FaultException + { + public FaultException(TDetail detail, FaultReason reason, FaultCode code, string action) { } + public TDetail Detail { get { return default(TDetail); } } + public override Channels.MessageFault CreateMessageFault() { return default(Channels.MessageFault); } + public override string ToString() { return default(string); } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/FaultReason.cs b/src/CoreWCF.Primitives/src/CoreWCF/FaultReason.cs new file mode 100644 index 000000000..3bbacf483 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/FaultReason.cs @@ -0,0 +1,127 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Globalization; + +namespace CoreWCF +{ + public class FaultReason + { + private ReadOnlyCollection _translations; + + public FaultReason(FaultReasonText translation) + { + if (translation == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("translation"); + + Init(translation); + } + + public FaultReason(string text) + { + // Let FaultReasonText constructor throw + Init(new FaultReasonText(text)); + } + + internal FaultReason(string text, CultureInfo cultureInfo) + { + // Let FaultReasonText constructor throw + Init(new FaultReasonText(text, cultureInfo)); + } + + public FaultReason(IEnumerable translations) + { + if (translations == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(translations)); + int count = 0; + foreach (FaultReasonText faultReasonText in translations) + count++; + if (count == 0) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(nameof(translations), + SR.AtLeastOneFaultReasonMustBeSpecified); + FaultReasonText[] array = new FaultReasonText[count]; + int index = 0; + foreach (FaultReasonText faultReasonText in translations) + { + if (faultReasonText == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(nameof(translations), + SR.NoNullTranslations); + + array[index++] = faultReasonText; + } + Init(array); + } + + private void Init(FaultReasonText translation) + { + Init(new FaultReasonText[] {translation}); + } + + private void Init(FaultReasonText[] translations) + { + _translations = new ReadOnlyCollection(translations); + } + + public FaultReasonText GetMatchingTranslation() + { + return GetMatchingTranslation(CultureInfo.CurrentCulture); + } + + public FaultReasonText GetMatchingTranslation(CultureInfo cultureInfo) + { + if (cultureInfo == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(cultureInfo)); + + // If there's only one translation, use it + if (_translations.Count == 1) + return _translations[0]; + + // Search for an exact match + for (int i = 0; i < _translations.Count; i++) + if (_translations[i].Matches(cultureInfo)) + return _translations[i]; + + // If no exact match is found, proceed by looking for the a translation with a language that is a parent of the current culture + + if (_translations.Count == 0) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new ArgumentException(SR.NoMatchingTranslationFoundForFaultText)); + + // Search for a more general language + string localLang = cultureInfo.Name; + while (true) + { + int idx = localLang.LastIndexOf('-'); + + // We don't want to accept xml:lang="" + if (idx == -1) + break; + + // Clip off the last subtag and look for a match + localLang = localLang.Substring(0, idx); + + for (int i = 0; i < _translations.Count; i++) + if (_translations[i].XmlLang == localLang) + return _translations[i]; + } + + // Return the first translation if no match is found + return _translations[0]; + } + + // public on full framework, but exposes a SynchronizedReadOnlyCollection + internal ReadOnlyCollection Translations + { + get { return _translations; } + } + + public override string ToString() + { + if (_translations.Count == 0) + return string.Empty; + + return GetMatchingTranslation().Text; + + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/FaultReasonText.cs b/src/CoreWCF.Primitives/src/CoreWCF/FaultReasonText.cs new file mode 100644 index 000000000..b0871e331 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/FaultReasonText.cs @@ -0,0 +1,53 @@ +using System; +using System.Globalization; + +namespace CoreWCF +{ + public class FaultReasonText + { + readonly string _xmlLang; + readonly string _text; + + public FaultReasonText(string text) + { + if (text == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(text)); + _text = text; + _xmlLang = CultureInfo.CurrentCulture.Name; + } + + public FaultReasonText(string text, string xmlLang) + { + if (text == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(text)); + if (xmlLang == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(xmlLang)); + _text = text; + _xmlLang = xmlLang; + } + + // public on full framework + internal FaultReasonText(string text, CultureInfo cultureInfo) + { + if (text == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(text)); + if (cultureInfo == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(cultureInfo)); + + _text = text; + _xmlLang = cultureInfo.Name; + } + + public bool Matches(CultureInfo cultureInfo) + { + if (cultureInfo == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(cultureInfo)); + + return _xmlLang == cultureInfo.Name; + } + + public string XmlLang => _xmlLang; + + public string Text => _text; + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/GeneralEndpointIdentity.cs b/src/CoreWCF.Primitives/src/CoreWCF/GeneralEndpointIdentity.cs new file mode 100644 index 000000000..8599d291d --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/GeneralEndpointIdentity.cs @@ -0,0 +1,15 @@ +using CoreWCF.IdentityModel.Claims; +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoreWCF +{ + internal class GeneralEndpointIdentity : EndpointIdentity + { + public GeneralEndpointIdentity(Claim identityClaim) + { + base.Initialize(identityClaim); + } + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/HostNameComparisonMode.cs b/src/CoreWCF.Primitives/src/CoreWCF/HostNameComparisonMode.cs new file mode 100644 index 000000000..ae92de6c5 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/HostNameComparisonMode.cs @@ -0,0 +1,11 @@ +using System; + +namespace CoreWCF +{ + public enum HostNameComparisonMode + { + StrongWildcard = 0, // + + Exact = 1, + WeakWildcard = 2, // * + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/IClientChannel.cs b/src/CoreWCF.Primitives/src/CoreWCF/IClientChannel.cs new file mode 100644 index 000000000..d56212632 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/IClientChannel.cs @@ -0,0 +1,10 @@ +using System; +using CoreWCF.Channels; + +namespace CoreWCF +{ + public interface IClientChannel : IContextChannel, IDisposable + { + event EventHandler UnknownMessageReceived; + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/ICommunicationObject.cs b/src/CoreWCF.Primitives/src/CoreWCF/ICommunicationObject.cs new file mode 100644 index 000000000..dc0631f4e --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/ICommunicationObject.cs @@ -0,0 +1,28 @@ +namespace CoreWCF +{ + public interface ICommunicationObject + { + CommunicationState State { get; } + event System.EventHandler Closed; + event System.EventHandler Closing; + event System.EventHandler Faulted; + event System.EventHandler Opened; + event System.EventHandler Opening; + void Abort(); + System.Threading.Tasks.Task CloseAsync(); + System.Threading.Tasks.Task CloseAsync(System.Threading.CancellationToken token); + System.Threading.Tasks.Task OpenAsync(); + System.Threading.Tasks.Task OpenAsync(System.Threading.CancellationToken token); + //System.IAsyncResult BeginClose(System.AsyncCallback callback, object state); // No more APM + //System.IAsyncResult BeginClose(System.TimeSpan timeout, System.AsyncCallback callback, object state); + //System.IAsyncResult BeginOpen(System.AsyncCallback callback, object state); + //System.IAsyncResult BeginOpen(System.TimeSpan timeout, System.AsyncCallback callback, object state); + + //void Close(); // No more sync + //void Close(System.TimeSpan timeout); + //void EndClose(System.IAsyncResult result); // No more APM + //void EndOpen(System.IAsyncResult result); + //void Open(); // No more sync + //void Open(System.TimeSpan timeout); + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/IContextChannel.cs b/src/CoreWCF.Primitives/src/CoreWCF/IContextChannel.cs new file mode 100644 index 000000000..dfc510127 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/IContextChannel.cs @@ -0,0 +1,16 @@ +using System; +using CoreWCF.Channels; + +namespace CoreWCF +{ + public interface IContextChannel : IChannel, IExtensibleObject + { + //bool AllowOutputBatching { get; set; } + IInputSession InputSession { get; } + EndpointAddress LocalAddress { get; } + TimeSpan OperationTimeout { get; set; } + IOutputSession OutputSession { get; } + EndpointAddress RemoteAddress { get; } + string SessionId { get; } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/IDefaultCommunicationTimeouts.cs b/src/CoreWCF.Primitives/src/CoreWCF/IDefaultCommunicationTimeouts.cs new file mode 100644 index 000000000..d03b1f892 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/IDefaultCommunicationTimeouts.cs @@ -0,0 +1,12 @@ +using System; + +namespace CoreWCF +{ + public interface IDefaultCommunicationTimeouts + { + TimeSpan CloseTimeout { get; } + TimeSpan OpenTimeout { get; } + TimeSpan ReceiveTimeout { get; } + TimeSpan SendTimeout { get; } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/IDuplexContextChannel.cs b/src/CoreWCF.Primitives/src/CoreWCF/IDuplexContextChannel.cs new file mode 100644 index 000000000..f8917ad1b --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/IDuplexContextChannel.cs @@ -0,0 +1,13 @@ +using System.Threading; +using System.Threading.Tasks; +using CoreWCF.Channels; + +namespace CoreWCF +{ + internal interface IDuplexContextChannel : IContextChannel + { + bool AutomaticInputSessionShutdown { get; set; } + InstanceContext CallbackInstance { get; set; } + Task CloseOutputSessionAsync(CancellationToken token); + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/IExtensibleObject.cs b/src/CoreWCF.Primitives/src/CoreWCF/IExtensibleObject.cs new file mode 100644 index 000000000..8b77a8591 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/IExtensibleObject.cs @@ -0,0 +1,7 @@ +namespace CoreWCF +{ + public interface IExtensibleObject where T : IExtensibleObject + { + IExtensionCollection Extensions { get; } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/IExtension.cs b/src/CoreWCF.Primitives/src/CoreWCF/IExtension.cs new file mode 100644 index 000000000..a835e8091 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/IExtension.cs @@ -0,0 +1,8 @@ +namespace CoreWCF +{ + public interface IExtension where T : IExtensibleObject + { + void Attach(T owner); + void Detach(T owner); + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/IExtensionCollection.cs b/src/CoreWCF.Primitives/src/CoreWCF/IExtensionCollection.cs new file mode 100644 index 000000000..841784ef1 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/IExtensionCollection.cs @@ -0,0 +1,8 @@ +namespace CoreWCF +{ + public interface IExtensionCollection : System.Collections.Generic.ICollection> where T : IExtensibleObject + { + E Find(); + System.Collections.ObjectModel.Collection FindAll(); + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/IOperationContractAttributeProvider.cs b/src/CoreWCF.Primitives/src/CoreWCF/IOperationContractAttributeProvider.cs new file mode 100644 index 000000000..0738eaffd --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/IOperationContractAttributeProvider.cs @@ -0,0 +1,7 @@ +namespace CoreWCF +{ + interface IOperationContractAttributeProvider + { + OperationContractAttribute GetOperationContractAttribute(); + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/IServiceChannel.cs b/src/CoreWCF.Primitives/src/CoreWCF/IServiceChannel.cs new file mode 100644 index 000000000..0a9e63561 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/IServiceChannel.cs @@ -0,0 +1,10 @@ +using System; +using CoreWCF.Channels; + +namespace CoreWCF +{ + internal interface IServiceChannel : IContextChannel + { + Uri ListenUri { get; } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Claims/Claim.cs b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Claims/Claim.cs new file mode 100644 index 000000000..b19eb486d --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Claims/Claim.cs @@ -0,0 +1,201 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Runtime.Serialization; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using CoreWCF; +using System.Security.Principal; +using System.Net.Mail; + +namespace CoreWCF.IdentityModel.Claims +{ + public class Claim + { + static Claim system; + + [DataMember(Name = "ClaimType")] + string claimType; + [DataMember(Name = "Resource")] + object resource; + [DataMember(Name = "Right")] + string right; + + IEqualityComparer comparer; + + Claim(string claimType, object resource, string right, IEqualityComparer comparer) + { + if (claimType == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("claimType"); + if (claimType.Length <= 0) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("claimType", SR.ArgumentCannotBeEmptyString); + if (right == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("right"); + if (right.Length <= 0) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("right", SR.ArgumentCannotBeEmptyString); + + // String interning is only supported in .Net standard 1.7. Api tool says this is slow? + //this.claimType = StringUtil.OptimizeString(claimType); + this.claimType = claimType; + this.resource = resource; + //this.right = StringUtil.OptimizeString(right); + this.right = right; + this.comparer = comparer; + } + + public Claim(string claimType, object resource, string right) : this(claimType, resource, right, null) + { + } + + public static IEqualityComparer DefaultComparer + { + get + { + return EqualityComparer.Default; + } + } + + public static Claim System + { + get + { + if (system == null) + system = new Claim(ClaimTypes.System, XsiConstants.System, Rights.Identity); + + return system; + } + } + + public object Resource + { + get { return resource; } + } + + public string ClaimType + { + get { return claimType; } + } + + public string Right + { + get { return right; } + } + + // Turn key claims + public static Claim CreateDnsClaim(string dns) + { + if (dns == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("dns"); + + return new Claim(ClaimTypes.Dns, dns, Rights.PossessProperty, ClaimComparer.Dns); + } + + //public static Claim CreateDenyOnlyWindowsSidClaim(SecurityIdentifier sid) + //{ + // if (sid == null) + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("sid"); + + // return new Claim(ClaimTypes.DenyOnlySid, sid, Rights.PossessProperty); + //} + + //public static Claim CreateHashClaim(byte[] hash) + //{ + // if (hash == null) + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("hash"); + + // return new Claim(ClaimTypes.Hash, SecurityUtils.CloneBuffer(hash), Rights.PossessProperty, ClaimComparer.Hash); + //} + + public static Claim CreateMailAddressClaim(MailAddress mailAddress) + { + if (mailAddress == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("mailAddress"); + + return new Claim(ClaimTypes.Email, mailAddress, Rights.PossessProperty); + } + + public static Claim CreateNameClaim(string name) + { + if (name == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("name"); + + return new Claim(ClaimTypes.Name, name, Rights.PossessProperty); + } + + public static Claim CreateRsaClaim(RSA rsa) + { + if (rsa == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("rsa"); + + return new Claim(ClaimTypes.Rsa, rsa, Rights.PossessProperty, ClaimComparer.Rsa); + } + + public static Claim CreateSpnClaim(string spn) + { + if (spn == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("spn"); + + return new Claim(ClaimTypes.Spn, spn, Rights.PossessProperty); + } + + public static Claim CreateThumbprintClaim(byte[] thumbprint) + { + if (thumbprint == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("thumbprint"); + + return new Claim(ClaimTypes.Thumbprint, SecurityUtils.CloneBuffer(thumbprint), Rights.PossessProperty, ClaimComparer.Thumbprint); + } + + public static Claim CreateUpnClaim(string upn) + { + if (upn == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("upn"); + + return new Claim(ClaimTypes.Upn, upn, Rights.PossessProperty, ClaimComparer.Upn); + } + + public static Claim CreateUriClaim(Uri uri) + { + if (uri == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("uri"); + + return new Claim(ClaimTypes.Uri, uri, Rights.PossessProperty); + } + + public static Claim CreateWindowsSidClaim(SecurityIdentifier sid) + { + if (sid == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("sid"); + + return new Claim(ClaimTypes.Sid, sid, Rights.PossessProperty); + } + + public static Claim CreateX500DistinguishedNameClaim(X500DistinguishedName x500DistinguishedName) + { + if (x500DistinguishedName == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("x500DistinguishedName"); + + return new Claim(ClaimTypes.X500DistinguishedName, x500DistinguishedName, Rights.PossessProperty, ClaimComparer.X500DistinguishedName); + } + + public override bool Equals(object obj) + { + if (comparer == null) + comparer = ClaimComparer.GetComparer(claimType); + return comparer.Equals(this, obj as Claim); + } + + public override int GetHashCode() + { + if (comparer == null) + comparer = ClaimComparer.GetComparer(claimType); + return comparer.GetHashCode(this); + } + + public override string ToString() + { + return string.Format(CultureInfo.CurrentCulture, "{0}: {1}", right, claimType); + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Claims/ClaimComparer.cs b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Claims/ClaimComparer.cs new file mode 100644 index 000000000..86f1ad0dd --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Claims/ClaimComparer.cs @@ -0,0 +1,348 @@ +using System; +using CoreWCF; +using System.Collections; +using System.Collections.Generic; + +namespace CoreWCF.IdentityModel.Claims +{ + internal class ClaimComparer : IEqualityComparer + { + static IEqualityComparer defaultComparer; + static IEqualityComparer hashComparer; + static IEqualityComparer dnsComparer; + //static IEqualityComparer rsaComparer; + static IEqualityComparer thumbprintComparer; + //static IEqualityComparer upnComparer; + //static IEqualityComparer x500DistinguishedNameComparer; + IEqualityComparer resourceComparer; + + ClaimComparer(IEqualityComparer resourceComparer) + { + this.resourceComparer = resourceComparer; + } + + public static IEqualityComparer GetComparer(string claimType) + { + if (claimType == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("claimType"); + if (claimType == ClaimTypes.Dns) + return Dns; + if (claimType == ClaimTypes.Hash) + return Hash; + if (claimType == ClaimTypes.Rsa) + return Rsa; + if (claimType == ClaimTypes.Thumbprint) + return Thumbprint; + if (claimType == ClaimTypes.Upn) + return Upn; + if (claimType == ClaimTypes.X500DistinguishedName) + return X500DistinguishedName; + return Default; + } + + public static IEqualityComparer Default + { + get + { + if (defaultComparer == null) + { + defaultComparer = new ClaimComparer(new ObjectComparer()); + } + return defaultComparer; + } + } + + public static IEqualityComparer Dns + { + get + { + if (dnsComparer == null) + { + dnsComparer = new ClaimComparer(StringComparer.OrdinalIgnoreCase); + } + return dnsComparer; + } + } + + public static IEqualityComparer Hash + { + get + { + if (hashComparer == null) + { + hashComparer = new ClaimComparer(new BinaryObjectComparer()); + } + return hashComparer; + } + } + + public static IEqualityComparer Rsa + { + get + { + throw new PlatformNotSupportedException(); + //if (rsaComparer == null) + //{ + // rsaComparer = new ClaimComparer(new RsaObjectComparer()); + //} + //return rsaComparer; + } + } + + public static IEqualityComparer Thumbprint + { + get + { + if (thumbprintComparer == null) + { + thumbprintComparer = new ClaimComparer(new BinaryObjectComparer()); + } + return thumbprintComparer; + } + } + + public static IEqualityComparer Upn + { + get + { + throw new PlatformNotSupportedException(); + //if (upnComparer == null) + //{ + // upnComparer = new ClaimComparer(new UpnObjectComparer()); + //} + //return upnComparer; + } + } + + public static IEqualityComparer X500DistinguishedName + { + get + { + throw new PlatformNotSupportedException(); + //if (x500DistinguishedNameComparer == null) + //{ + // x500DistinguishedNameComparer = new ClaimComparer(new X500DistinguishedNameObjectComparer()); + //} + //return x500DistinguishedNameComparer; + } + } + + // we still need to review how the default equals works, this is not how Doug envisioned it. + public bool Equals(Claim claim1, Claim claim2) + { + if (ReferenceEquals(claim1, claim2)) + return true; + + if (claim1 == null || claim2 == null) + return false; + + if (claim1.ClaimType != claim2.ClaimType || claim1.Right != claim2.Right) + return false; + + return resourceComparer.Equals(claim1.Resource, claim2.Resource); + } + + public int GetHashCode(Claim claim) + { + if (claim == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("claim"); + + return claim.ClaimType.GetHashCode() ^ claim.Right.GetHashCode() + ^ ((claim.Resource == null) ? 0 : resourceComparer.GetHashCode(claim.Resource)); + } + + class ObjectComparer : IEqualityComparer + { + bool IEqualityComparer.Equals(object obj1, object obj2) + { + if (obj1 == null && obj2 == null) + return true; + if (obj1 == null || obj2 == null) + return false; + return obj1.Equals(obj2); + } + + int IEqualityComparer.GetHashCode(object obj) + { + if (obj == null) + return 0; + return obj.GetHashCode(); + } + } + + class BinaryObjectComparer : IEqualityComparer + { + bool IEqualityComparer.Equals(object obj1, object obj2) + { + if (ReferenceEquals(obj1, obj2)) + return true; + + byte[] bytes1 = obj1 as byte[]; + byte[] bytes2 = obj2 as byte[]; + if (bytes1 == null || bytes2 == null) + return false; + + if (bytes1.Length != bytes2.Length) + return false; + + for (int i = 0; i < bytes1.Length; ++i) + { + if (bytes1[i] != bytes2[i]) + return false; + } + + return true; + } + + int IEqualityComparer.GetHashCode(object obj) + { + byte[] bytes = obj as byte[]; + if (bytes == null) + return 0; + + int hashCode = 0; + for (int i = 0; i < bytes.Length && i < 4; ++i) + { + hashCode = (hashCode << 8) | bytes[i]; + } + + return hashCode ^ bytes.Length; + } + } + + //class RsaObjectComparer : IEqualityComparer + //{ + // bool IEqualityComparer.Equals(object obj1, object obj2) + // { + // if (ReferenceEquals(obj1, obj2)) + // return true; + + // RSA rsa1 = obj1 as RSA; + // RSA rsa2 = obj2 as RSA; + // if (rsa1 == null || rsa2 == null) + // return false; + + // RSAParameters parm1 = rsa1.ExportParameters(false); + // RSAParameters parm2 = rsa2.ExportParameters(false); + + // if (parm1.Modulus.Length != parm2.Modulus.Length || + // parm1.Exponent.Length != parm2.Exponent.Length) + // return false; + + // for (int i = 0; i < parm1.Modulus.Length; ++i) + // { + // if (parm1.Modulus[i] != parm2.Modulus[i]) + // return false; + // } + // for (int i = 0; i < parm1.Exponent.Length; ++i) + // { + // if (parm1.Exponent[i] != parm2.Exponent[i]) + // return false; + // } + // return true; + // } + + // int IEqualityComparer.GetHashCode(object obj) + // { + // RSA rsa = obj as RSA; + // if (rsa == null) + // return 0; + + // RSAParameters parm = rsa.ExportParameters(false); + // return parm.Modulus.Length ^ parm.Exponent.Length; + // } + //} + + //class X500DistinguishedNameObjectComparer : IEqualityComparer + //{ + // IEqualityComparer binaryComparer; + // public X500DistinguishedNameObjectComparer() + // { + // binaryComparer = new BinaryObjectComparer(); + // } + + // bool IEqualityComparer.Equals(object obj1, object obj2) + // { + // if (ReferenceEquals(obj1, obj2)) + // return true; + + // X500DistinguishedName dn1 = obj1 as X500DistinguishedName; + // X500DistinguishedName dn2 = obj2 as X500DistinguishedName; + // if (dn1 == null || dn2 == null) + // return false; + + // // 1) Hopefully cover most cases (perf reason). + // if (StringComparer.Ordinal.Equals(dn1.Name, dn2.Name)) + // return true; + + // // 2) Raw byte compare. Note: we assume the rawbyte is in the same order + // // (default = X500DistinguishedNameFlags.Reversed). + // return binaryComparer.Equals(dn1.RawData, dn2.RawData); + // } + + // int IEqualityComparer.GetHashCode(object obj) + // { + // X500DistinguishedName dn = obj as X500DistinguishedName; + // if (dn == null) + // return 0; + + // return binaryComparer.GetHashCode(dn.RawData); + // } + //} + + //class UpnObjectComparer : IEqualityComparer + //{ + // bool IEqualityComparer.Equals(object obj1, object obj2) + // { + // if (StringComparer.OrdinalIgnoreCase.Equals(obj1, obj2)) + // return true; + + // string upn1 = obj1 as string; + // string upn2 = obj2 as string; + // if (upn1 == null || upn2 == null) + // return false; + + // SecurityIdentifier sid1; + // if (!TryLookupSidFromName(upn1, out sid1)) + // return false; + + // // Normalize to sid + // SecurityIdentifier sid2; + // if (!TryLookupSidFromName(upn2, out sid2)) + // return false; + + // return sid1 == sid2; + // } + + // int IEqualityComparer.GetHashCode(object obj) + // { + // string upn = obj as string; + // if (upn == null) + // return 0; + + // // Normalize to sid + // SecurityIdentifier sid; + // if (TryLookupSidFromName(upn, out sid)) + // return sid.GetHashCode(); + + // return StringComparer.OrdinalIgnoreCase.GetHashCode(upn); + // } + + // bool TryLookupSidFromName(string upn, out SecurityIdentifier sid) + // { + // sid = null; + // try + // { + // NTAccount acct = new NTAccount(upn); + // sid = acct.Translate(typeof(SecurityIdentifier)) as SecurityIdentifier; + // } + // catch (IdentityNotMappedException e) + // { + // DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); + // } + // return sid != null; + // } + //} + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Claims/ClaimSet.cs b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Claims/ClaimSet.cs new file mode 100644 index 000000000..f07d2a852 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Claims/ClaimSet.cs @@ -0,0 +1,112 @@ +using System.Collections; +using System.Collections.Generic; +using System.Runtime.Serialization; +using CoreWCF; +using System.Security.Principal; + +namespace CoreWCF.IdentityModel.Claims +{ + [DataContract(Namespace = XsiConstants.Namespace)] + internal abstract class ClaimSet : IEnumerable + { + static ClaimSet system; + static ClaimSet windows; + static ClaimSet anonymous; + + public static ClaimSet System + { + get + { + if (system == null) + { + List claims = new List(2); + claims.Add(Claim.System); + claims.Add(new Claim(ClaimTypes.System, XsiConstants.System, Rights.PossessProperty)); + system = new DefaultClaimSet(claims); + } + return system; + } + } + + public static ClaimSet Windows + { + get + { + if (windows == null) + { + List claims = new List(2); + SecurityIdentifier sid = new SecurityIdentifier(WellKnownSidType.NTAuthoritySid, null); + claims.Add(new Claim(ClaimTypes.Sid, sid, Rights.Identity)); + claims.Add(Claim.CreateWindowsSidClaim(sid)); + windows = new DefaultClaimSet(claims); + } + return windows; + } + } + + internal static ClaimSet Anonymous + { + get + { + if (anonymous == null) + anonymous = new DefaultClaimSet(); + + return anonymous; + } + } + + static internal bool SupportedRight(string right) + { + return right == null || + Rights.Identity.Equals(right) || + Rights.PossessProperty.Equals(right); + } + + + public virtual bool ContainsClaim(Claim claim, IEqualityComparer comparer) + { + if (claim == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("claim"); + if (comparer == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("comparer"); + + IEnumerable claims = FindClaims(null, null); + if (claims != null) + { + foreach (Claim matchingClaim in claims) + { + if (comparer.Equals(claim, matchingClaim)) + return true; + } + } + return false; + } + + public virtual bool ContainsClaim(Claim claim) + { + if (claim == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("claim"); + + IEnumerable claims = FindClaims(claim.ClaimType, claim.Right); + if (claims != null) + { + foreach (Claim matchingClaim in claims) + { + if (claim.Equals(matchingClaim)) + return true; + } + } + return false; + } + + public abstract Claim this[int index] { get; } + public abstract int Count { get; } + public abstract ClaimSet Issuer { get; } + // Note: null string represents any. + public abstract IEnumerable FindClaims(string claimType, string right); + public abstract IEnumerator GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } + + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Claims/ClaimTypes.cs b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Claims/ClaimTypes.cs new file mode 100644 index 000000000..fcc85d1c3 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Claims/ClaimTypes.cs @@ -0,0 +1,75 @@ +namespace CoreWCF.IdentityModel.Claims +{ + internal static class ClaimTypes + { + const string claimTypeNamespace = XsiConstants.Namespace + "/claims"; + + const string anonymous = claimTypeNamespace + "/anonymous"; + const string dns = claimTypeNamespace + "/dns"; + const string email = claimTypeNamespace + "/emailaddress"; + const string hash = claimTypeNamespace + "/hash"; + const string name = claimTypeNamespace + "/name"; + const string rsa = claimTypeNamespace + "/rsa"; + const string sid = claimTypeNamespace + "/sid"; + const string denyOnlySid = claimTypeNamespace + "/denyonlysid"; + const string spn = claimTypeNamespace + "/spn"; + const string system = claimTypeNamespace + "/system"; + const string thumbprint = claimTypeNamespace + "/thumbprint"; + const string upn = claimTypeNamespace + "/upn"; + const string uri = claimTypeNamespace + "/uri"; + const string x500DistinguishedName = claimTypeNamespace + "/x500distinguishedname"; + + const string givenname = claimTypeNamespace + "/givenname"; + const string surname = claimTypeNamespace + "/surname"; + const string streetaddress = claimTypeNamespace + "/streetaddress"; + const string locality = claimTypeNamespace + "/locality"; + const string stateorprovince = claimTypeNamespace + "/stateorprovince"; + const string postalcode = claimTypeNamespace + "/postalcode"; + const string country = claimTypeNamespace + "/country"; + const string homephone = claimTypeNamespace + "/homephone"; + const string otherphone = claimTypeNamespace + "/otherphone"; + const string mobilephone = claimTypeNamespace + "/mobilephone"; + const string dateofbirth = claimTypeNamespace + "/dateofbirth"; + const string gender = claimTypeNamespace + "/gender"; + const string ppid = claimTypeNamespace + "/privatepersonalidentifier"; + const string webpage = claimTypeNamespace + "/webpage"; + const string nameidentifier = claimTypeNamespace + "/nameidentifier"; + const string authentication = claimTypeNamespace + "/authentication"; + const string authorizationdecision = claimTypeNamespace + "/authorizationdecision"; + + static public string Anonymous { get { return anonymous; } } + static public string DenyOnlySid { get { return denyOnlySid; } } + static public string Dns { get { return dns; } } + static public string Email { get { return email; } } + static public string Hash { get { return hash; } } + static public string Name { get { return name; } } + static public string Rsa { get { return rsa; } } + static public string Sid { get { return sid; } } + static public string Spn { get { return spn; } } + static public string System { get { return system; } } + static public string Thumbprint { get { return thumbprint; } } + static public string Upn { get { return upn; } } + static public string Uri { get { return uri; } } + static public string X500DistinguishedName { get { return x500DistinguishedName; } } + static public string NameIdentifier { get { return nameidentifier; } } + static public string Authentication { get { return authentication; } } + static public string AuthorizationDecision { get { return authorizationdecision; } } + + // used in info card + static public string GivenName { get { return givenname; } } + static public string Surname { get { return surname; } } + static public string StreetAddress { get { return streetaddress; } } + static public string Locality { get { return locality; } } + static public string StateOrProvince { get { return stateorprovince; } } + static public string PostalCode { get { return postalcode; } } + static public string Country { get { return country; } } + static public string HomePhone { get { return homephone; } } + static public string OtherPhone { get { return otherphone; } } + static public string MobilePhone { get { return mobilephone; } } + static public string DateOfBirth { get { return dateofbirth; } } + static public string Gender { get { return gender; } } + static public string PPID { get { return ppid; } } + static public string Webpage { get { return webpage; } } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Claims/DefaultClaimSet.cs b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Claims/DefaultClaimSet.cs new file mode 100644 index 000000000..750d9cf85 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Claims/DefaultClaimSet.cs @@ -0,0 +1,104 @@ +using System.Collections.Generic; +using System.Runtime.Serialization; +using CoreWCF; + +namespace CoreWCF.IdentityModel.Claims +{ + [DataContract(Namespace = XsiConstants.Namespace)] + internal class DefaultClaimSet : ClaimSet + { + [DataMember(Name = "Issuer")] + ClaimSet issuer; + [DataMember(Name = "Claims")] + IList claims; + + public DefaultClaimSet(params Claim[] claims) + { + Initialize(this, claims); + } + + public DefaultClaimSet(IList claims) + { + Initialize(this, claims); + } + + public DefaultClaimSet(ClaimSet issuer, params Claim[] claims) + { + Initialize(issuer, claims); + } + + public DefaultClaimSet(ClaimSet issuer, IList claims) + { + Initialize(issuer, claims); + } + + public override Claim this[int index] + { + get { return claims[index]; } + } + + public override int Count + { + get { return claims.Count; } + } + + public override ClaimSet Issuer + { + get { return issuer; } + } + + public override bool ContainsClaim(Claim claim) + { + if (claim == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("claim"); + + for (int i = 0; i < claims.Count; ++i) + { + if (claim.Equals(claims[i])) + { + return true; + } + } + return false; + } + + public override IEnumerable FindClaims(string claimType, string right) + { + bool anyClaimType = (claimType == null); + bool anyRight = (right == null); + + for (int i = 0; i < claims.Count; ++i) + { + Claim claim = claims[i]; + if ((claim != null) && + (anyClaimType || claimType == claim.ClaimType) && + (anyRight || right == claim.Right)) + { + yield return claim; + } + } + } + + public override IEnumerator GetEnumerator() + { + return claims.GetEnumerator(); + } + + protected void Initialize(ClaimSet issuer, IList claims) + { + if (issuer == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("issuer"); + if (claims == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("claims"); + + this.issuer = issuer; + this.claims = claims; + } + + public override string ToString() + { + return SecurityUtils.ClaimSetToString(this); + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Claims/Rights.cs b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Claims/Rights.cs new file mode 100644 index 000000000..25f707aa4 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Claims/Rights.cs @@ -0,0 +1,14 @@ +namespace CoreWCF.IdentityModel.Claims +{ + internal static class Rights + { + const string rightNamespace = XsiConstants.Namespace + "/right"; + + const string identity = rightNamespace + "/identity"; + const string possessProperty = rightNamespace + "/possessproperty"; + + static public string Identity { get { return identity; } } + static public string PossessProperty { get { return possessProperty; } } + + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Claims/WindowsClaimSet.cs b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Claims/WindowsClaimSet.cs new file mode 100644 index 000000000..12e8c44cb --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Claims/WindowsClaimSet.cs @@ -0,0 +1,247 @@ +using CoreWCF.IdentityModel.Policy; +using CoreWCF; +using System; +using System.Collections.Generic; +using System.Security.Principal; +using System.Text; + +namespace CoreWCF.IdentityModel.Claims +{ + internal class WindowsClaimSet : ClaimSet, IIdentityInfo, IDisposable + { + internal const bool DefaultIncludeWindowsGroups = true; + private WindowsIdentity _windowsIdentity; + private DateTime _expirationTime; + private bool _includeWindowsGroups; + private IList _claims; + private bool _disposed = false; + private string _authenticationType; + + public WindowsClaimSet(WindowsIdentity windowsIdentity) + : this(windowsIdentity, DefaultIncludeWindowsGroups) + { + } + + public WindowsClaimSet(WindowsIdentity windowsIdentity, bool includeWindowsGroups) + : this(windowsIdentity, includeWindowsGroups, DateTime.UtcNow.AddHours(10)) + { + } + + public WindowsClaimSet(WindowsIdentity windowsIdentity, DateTime expirationTime) + : this(windowsIdentity, DefaultIncludeWindowsGroups, expirationTime) + { + } + + public WindowsClaimSet(WindowsIdentity windowsIdentity, bool includeWindowsGroups, DateTime expirationTime) + : this(windowsIdentity, null, includeWindowsGroups, expirationTime, true) + { + } + + public WindowsClaimSet(WindowsIdentity windowsIdentity, string authenticationType, bool includeWindowsGroups, DateTime expirationTime) + : this(windowsIdentity, authenticationType, includeWindowsGroups, expirationTime, true) + { + } + + internal WindowsClaimSet(WindowsIdentity windowsIdentity, string authenticationType, bool includeWindowsGroups, bool clone) + : this(windowsIdentity, authenticationType, includeWindowsGroups, DateTime.UtcNow.AddHours(10), clone) + { + } + + internal WindowsClaimSet(WindowsIdentity windowsIdentity, string authenticationType, bool includeWindowsGroups, DateTime expirationTime, bool clone) + { + if (windowsIdentity == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("windowsIdentity"); + + _windowsIdentity = clone ? SecurityUtils.CloneWindowsIdentityIfNecessary(windowsIdentity, authenticationType) : windowsIdentity; + _includeWindowsGroups = includeWindowsGroups; + _expirationTime = expirationTime; + _authenticationType = authenticationType; + } + + private WindowsClaimSet(WindowsClaimSet from) + : this(from.WindowsIdentity, from._authenticationType, from._includeWindowsGroups, from._expirationTime, true) + { + } + + public override Claim this[int index] + { + get + { + ThrowIfDisposed(); + EnsureClaims(); + return _claims[index]; + } + } + + public override int Count + { + get + { + ThrowIfDisposed(); + EnsureClaims(); + return _claims.Count; + } + } + + IIdentity IIdentityInfo.Identity + { + get + { + ThrowIfDisposed(); + return _windowsIdentity; + } + } + + public WindowsIdentity WindowsIdentity + { + get + { + ThrowIfDisposed(); + return _windowsIdentity; + } + } + + public override ClaimSet Issuer + { + get { return ClaimSet.Windows; } + } + + public DateTime ExpirationTime + { + get { return _expirationTime; } + } + + internal WindowsClaimSet Clone() + { + ThrowIfDisposed(); + return new WindowsClaimSet(this); + } + + public void Dispose() + { + if (!_disposed) + { + _disposed = true; + _windowsIdentity.Dispose(); + } + } + + private IList InitializeClaimsCore() + { + if (_windowsIdentity.AccessToken == null) + return new List(); + + List claims = new List(3); + claims.Add(new Claim(ClaimTypes.Sid, _windowsIdentity.User, Rights.Identity)); + Claim claim; + if (TryCreateWindowsSidClaim(_windowsIdentity, out claim)) + { + claims.Add(claim); + } + claims.Add(Claim.CreateNameClaim(_windowsIdentity.Name)); + if (_includeWindowsGroups) + { + // claims.AddRange(Groups); + } + return claims; + } + + private void EnsureClaims() + { + if (_claims != null) + return; + + _claims = InitializeClaimsCore(); + } + + private void ThrowIfDisposed() + { + if (_disposed) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ObjectDisposedException(GetType().FullName)); + } + } + + private static bool SupportedClaimType(string claimType) + { + return claimType == null || + ClaimTypes.Sid == claimType || + ClaimTypes.DenyOnlySid == claimType || + ClaimTypes.Name == claimType; + } + + // Note: null string represents any. + public override IEnumerable FindClaims(string claimType, string right) + { + ThrowIfDisposed(); + if (!SupportedClaimType(claimType) || !ClaimSet.SupportedRight(right)) + { + yield break; + } + else if (_claims == null && (ClaimTypes.Sid == claimType || ClaimTypes.DenyOnlySid == claimType)) + { + if (ClaimTypes.Sid == claimType) + { + if (right == null || Rights.Identity == right) + { + yield return new Claim(ClaimTypes.Sid, _windowsIdentity.User, Rights.Identity); + } + } + + if (right == null || Rights.PossessProperty == right) + { + Claim sid; + if (TryCreateWindowsSidClaim(_windowsIdentity, out sid)) + { + if (claimType == sid.ClaimType) + { + yield return sid; + } + } + } + + if (_includeWindowsGroups && (right == null || Rights.PossessProperty == right)) + { + // Not sure yet if GroupSidClaimCollections are necessary in .NET Core, but default + // _includeWindowsGroups is true; don't throw here or we bust the default case on UWP/.NET Core + } + } + else + { + EnsureClaims(); + + bool anyClaimType = (claimType == null); + bool anyRight = (right == null); + + for (int i = 0; i < _claims.Count; ++i) + { + Claim claim = _claims[i]; + if ((claim != null) && + (anyClaimType || claimType == claim.ClaimType) && + (anyRight || right == claim.Right)) + { + yield return claim; + } + } + } + } + + public override IEnumerator GetEnumerator() + { + ThrowIfDisposed(); + EnsureClaims(); + return _claims.GetEnumerator(); + } + + public override string ToString() + { + return _disposed ? base.ToString() : SecurityUtils.ClaimSetToString(this); + } + + public static bool TryCreateWindowsSidClaim(WindowsIdentity windowsIdentity, out Claim claim) + { + throw new PlatformNotSupportedException("CreateWindowsSidClaim is not yet supported"); + } + } + +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Claims/X509CertificateClaimSet.cs b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Claims/X509CertificateClaimSet.cs new file mode 100644 index 000000000..fd9666653 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Claims/X509CertificateClaimSet.cs @@ -0,0 +1,525 @@ +using CoreWCF.IdentityModel.Policy; +using CoreWCF; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.Net.Mail; +using System.Security.Claims; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using System.Security.Principal; +using System.Text; + +namespace CoreWCF.IdentityModel.Claims +{ + internal class X509CertificateClaimSet : ClaimSet, IIdentityInfo, IDisposable + { + X509Certificate2 certificate; + DateTime expirationTime = SecurityUtils.MinUtcDateTime; + ClaimSet issuer; + X509Identity identity; + X509ChainElementCollection elements; + IList claims; + int index; + bool disposed = false; + + public X509CertificateClaimSet(X509Certificate2 certificate) + : this(certificate, true) + { + } + + internal X509CertificateClaimSet(X509Certificate2 certificate, bool clone) + { + if (certificate == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("certificate"); + this.certificate = clone ? new X509Certificate2(certificate) : certificate; + } + + X509CertificateClaimSet(X509CertificateClaimSet from) + : this(from.X509Certificate, true) + { + } + + X509CertificateClaimSet(X509ChainElementCollection elements, int index) + { + this.elements = elements; + this.index = index; + certificate = elements[index].Certificate; + } + + public override Claim this[int index] + { + get + { + ThrowIfDisposed(); + EnsureClaims(); + return claims[index]; + } + } + + public override int Count + { + get + { + ThrowIfDisposed(); + EnsureClaims(); + return claims.Count; + } + } + + IIdentity IIdentityInfo.Identity + { + get + { + ThrowIfDisposed(); + if (identity == null) + identity = new X509Identity(certificate, false, false); + return identity; + } + } + + public DateTime ExpirationTime + { + get + { + ThrowIfDisposed(); + if (expirationTime == SecurityUtils.MinUtcDateTime) + expirationTime = certificate.NotAfter.ToUniversalTime(); + return expirationTime; + } + } + + public override ClaimSet Issuer + { + get + { + ThrowIfDisposed(); + if (issuer == null) + { + if (elements == null) + { + X509Chain chain = new X509Chain(); + chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; + chain.Build(certificate); + index = 0; + elements = chain.ChainElements; + } + + if (index + 1 < elements.Count) + { + issuer = new X509CertificateClaimSet(elements, index + 1); + elements = null; + } + // SelfSigned? + else if (StringComparer.OrdinalIgnoreCase.Equals(certificate.SubjectName.Name, certificate.IssuerName.Name)) + issuer = this; + else + issuer = new X500DistinguishedNameClaimSet(certificate.IssuerName); + + } + return issuer; + } + } + + public X509Certificate2 X509Certificate + { + get + { + ThrowIfDisposed(); + return certificate; + } + } + + internal X509CertificateClaimSet Clone() + { + ThrowIfDisposed(); + return new X509CertificateClaimSet(this); + } + + public void Dispose() + { + if (!disposed) + { + disposed = true; + SecurityUtils.DisposeIfNecessary(identity); + if (issuer != null) + { + if (issuer != this) + { + SecurityUtils.DisposeIfNecessary(issuer as IDisposable); + } + } + if (elements != null) + { + for (int i = index + 1; i < elements.Count; ++i) + { + SecurityUtils.ResetCertificate(elements[i].Certificate); + } + } + SecurityUtils.ResetCertificate(certificate); + } + } + + IList InitializeClaimsCore() + { + List claims = new List(); + byte[] thumbprint = certificate.GetCertHash(); + claims.Add(new Claim(ClaimTypes.Thumbprint, thumbprint, Rights.Identity)); + claims.Add(new Claim(ClaimTypes.Thumbprint, thumbprint, Rights.PossessProperty)); + + // Ordering SubjectName, Dns, SimpleName, Email, Upn + string value = certificate.SubjectName.Name; + if (!string.IsNullOrEmpty(value)) + claims.Add(Claim.CreateX500DistinguishedNameClaim(certificate.SubjectName)); + + claims.AddRange(GetDnsClaims(certificate)); + + value = certificate.GetNameInfo(X509NameType.SimpleName, false); + if (!string.IsNullOrEmpty(value)) + claims.Add(Claim.CreateNameClaim(value)); + + value = certificate.GetNameInfo(X509NameType.EmailName, false); + if (!string.IsNullOrEmpty(value)) + claims.Add(Claim.CreateMailAddressClaim(new MailAddress(value))); + + value = certificate.GetNameInfo(X509NameType.UpnName, false); + if (!string.IsNullOrEmpty(value)) + claims.Add(Claim.CreateUpnClaim(value)); + + value = certificate.GetNameInfo(X509NameType.UrlName, false); + if (!string.IsNullOrEmpty(value)) + claims.Add(Claim.CreateUriClaim(new Uri(value))); + + RSA rsa = certificate.PublicKey.Key as RSA; + if (rsa != null) + claims.Add(Claim.CreateRsaClaim(rsa)); + + return claims; + } + + void EnsureClaims() + { + if (claims != null) + return; + + claims = InitializeClaimsCore(); + } + + static bool SupportedClaimType(string claimType) + { + return claimType == null || + ClaimTypes.Thumbprint.Equals(claimType) || + ClaimTypes.X500DistinguishedName.Equals(claimType) || + ClaimTypes.Dns.Equals(claimType) || + ClaimTypes.Name.Equals(claimType) || + ClaimTypes.Email.Equals(claimType) || + ClaimTypes.Upn.Equals(claimType) || + ClaimTypes.Uri.Equals(claimType) || + ClaimTypes.Rsa.Equals(claimType); + } + + // Note: null string represents any. + public override IEnumerable FindClaims(string claimType, string right) + { + ThrowIfDisposed(); + if (!SupportedClaimType(claimType) || !ClaimSet.SupportedRight(right)) + { + yield break; + } + else if (claims == null && ClaimTypes.Thumbprint.Equals(claimType)) + { + if (right == null || Rights.Identity.Equals(right)) + { + yield return new Claim(ClaimTypes.Thumbprint, certificate.GetCertHash(), Rights.Identity); + } + if (right == null || Rights.PossessProperty.Equals(right)) + { + yield return new Claim(ClaimTypes.Thumbprint, certificate.GetCertHash(), Rights.PossessProperty); + } + } + else if (claims == null && ClaimTypes.Dns.Equals(claimType)) + { + if (right == null || Rights.PossessProperty.Equals(right)) + { + foreach (var claim in GetDnsClaims(certificate)) + yield return claim; + } + } + else + { + EnsureClaims(); + + bool anyClaimType = (claimType == null); + bool anyRight = (right == null); + + for (int i = 0; i < claims.Count; ++i) + { + Claim claim = claims[i]; + if ((claim != null) && + (anyClaimType || claimType.Equals(claim.ClaimType)) && + (anyRight || right.Equals(claim.Right))) + { + yield return claim; + } + } + } + } + + private static List GetDnsClaims(X509Certificate2 cert) + { + List dnsClaimEntries = new List(); + + // old behavior, default for <= 4.6 + string value = cert.GetNameInfo(X509NameType.DnsName, false); + if (!string.IsNullOrEmpty(value)) + dnsClaimEntries.Add(Claim.CreateDnsClaim(value)); + + return dnsClaimEntries; + } + + public override IEnumerator GetEnumerator() + { + ThrowIfDisposed(); + EnsureClaims(); + return claims.GetEnumerator(); + } + + public override string ToString() + { + return disposed ? base.ToString() : SecurityUtils.ClaimSetToString(this); + } + + void ThrowIfDisposed() + { + if (disposed) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ObjectDisposedException(GetType().FullName)); + } + } + + class X500DistinguishedNameClaimSet : DefaultClaimSet, IIdentityInfo + { + IIdentity identity; + + public X500DistinguishedNameClaimSet(X500DistinguishedName x500DistinguishedName) + { + if (x500DistinguishedName == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("x500DistinguishedName"); + + identity = new X509Identity(x500DistinguishedName); + List claims = new List(2); + claims.Add(new Claim(ClaimTypes.X500DistinguishedName, x500DistinguishedName, Rights.Identity)); + claims.Add(Claim.CreateX500DistinguishedNameClaim(x500DistinguishedName)); + Initialize(ClaimSet.Anonymous, claims); + } + + public IIdentity Identity + { + get { return identity; } + } + } + + // We don't have a strongly typed extension to parse Subject Alt Names, so we have to do a workaround + // to figure out what the identifier, delimiter, and separator is by using a well-known extension + private static class X509SubjectAlternativeNameConstants + { + public const string SanOid = "2.5.29.7"; + public const string San2Oid = "2.5.29.17"; + + public static string Identifier + { + get; + private set; + } + + public static char Delimiter + { + get; + private set; + } + + public static string Separator + { + get; + private set; + } + + public static string[] SeparatorArray + { + get; + private set; + } + + public static bool SuccessfullyInitialized + { + get; + private set; + } + + // static initializer will run before properties are accessed + static X509SubjectAlternativeNameConstants() + { + // Extracted a well-known X509Extension + byte[] x509ExtensionBytes = new byte[] { + 48, 36, 130, 21, 110, 111, 116, 45, 114, 101, 97, 108, 45, 115, 117, 98, 106, 101, 99, + 116, 45, 110, 97, 109, 101, 130, 11, 101, 120, 97, 109, 112, 108, 101, 46, 99, 111, 109 + }; + const string subjectName = "not-real-subject-name"; + string x509ExtensionFormattedString = string.Empty; + try + { + X509Extension x509Extension = new X509Extension(SanOid, x509ExtensionBytes, true); + x509ExtensionFormattedString = x509Extension.Format(false); + + // Each OS has a different dNSName identifier and delimiter + // On Windows, dNSName == "DNS Name" (localizable), on Linux, dNSName == "DNS" + // e.g., + // Windows: x509ExtensionFormattedString is: "DNS Name=not-real-subject-name, DNS Name=example.com" + // Linux: x509ExtensionFormattedString is: "DNS:not-real-subject-name, DNS:example.com" + // Parse: + + int delimiterIndex = x509ExtensionFormattedString.IndexOf(subjectName) - 1; + Delimiter = x509ExtensionFormattedString[delimiterIndex]; + + // Make an assumption that all characters from the the start of string to the delimiter + // are part of the identifier + Identifier = x509ExtensionFormattedString.Substring(0, delimiterIndex); + + int separatorFirstChar = delimiterIndex + subjectName.Length + 1; + int separatorLength = 1; + for (int i = separatorFirstChar + 1; i < x509ExtensionFormattedString.Length; i++) + { + // We advance until the first character of the identifier to determine what the + // separator is. This assumes that the identifier assumption above is correct + if (x509ExtensionFormattedString[i] == Identifier[0]) + { + break; + } + + separatorLength++; + } + + Separator = x509ExtensionFormattedString.Substring(separatorFirstChar, separatorLength); + SeparatorArray = new string[1] { Separator }; + SuccessfullyInitialized = true; + } + catch (Exception ex) + { + SuccessfullyInitialized = false; + DiagnosticUtility.TraceHandledException( + new FormatException(string.Format(CultureInfo.InvariantCulture, + "There was an error parsing the SubjectAlternativeNames: '{0}'. See inner exception for more details.{1}Detected values were: Identifier: '{2}'; Delimiter:'{3}'; Separator:'{4}'", + x509ExtensionFormattedString, + Environment.NewLine, + Identifier, + Delimiter, + Separator), + ex), + TraceEventType.Warning); + } + } + } + } + + class X509Identity : GenericIdentity, IDisposable + { + const string X509 = "X509"; + const string Thumbprint = "; "; + X500DistinguishedName x500DistinguishedName; + X509Certificate2 certificate; + string name; + bool disposed = false; + bool disposable = true; + + public X509Identity(X509Certificate2 certificate) + : this(certificate, true, true) + { + } + + public X509Identity(X500DistinguishedName x500DistinguishedName) + : base(X509, X509) + { + this.x500DistinguishedName = x500DistinguishedName; + } + + internal X509Identity(X509Certificate2 certificate, bool clone, bool disposable) + : base(X509, X509) + { + this.certificate = clone ? new X509Certificate2(certificate) : certificate; + this.disposable = clone || disposable; + } + + public override string Name + { + get + { + ThrowIfDisposed(); + if (name == null) + { + // + // DCR 48092: PrincipalPermission authorization using certificates could cause Elevation of Privilege. + // because there could be duplicate subject name. In order to be more unique, we use SubjectName + Thumbprint + // instead + // + name = GetName() + Thumbprint + certificate.Thumbprint; + } + return name; + } + } + + string GetName() + { + if (x500DistinguishedName != null) + return x500DistinguishedName.Name; + + string value = certificate.SubjectName.Name; + if (!string.IsNullOrEmpty(value)) + return value; + + value = certificate.GetNameInfo(X509NameType.DnsName, false); + if (!string.IsNullOrEmpty(value)) + return value; + + value = certificate.GetNameInfo(X509NameType.SimpleName, false); + if (!string.IsNullOrEmpty(value)) + return value; + + value = certificate.GetNameInfo(X509NameType.EmailName, false); + if (!string.IsNullOrEmpty(value)) + return value; + + value = certificate.GetNameInfo(X509NameType.UpnName, false); + if (!string.IsNullOrEmpty(value)) + return value; + + return string.Empty; + } + + public override ClaimsIdentity Clone() + { + return certificate != null ? new X509Identity(certificate) : new X509Identity(x500DistinguishedName); + } + + public void Dispose() + { + if (disposable && !disposed) + { + disposed = true; + if (certificate != null) + { + certificate.Reset(); + } + } + } + + void ThrowIfDisposed() + { + if (disposed) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ObjectDisposedException(GetType().FullName)); + } + } + } + +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Claims/XsiConstants.cs b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Claims/XsiConstants.cs new file mode 100644 index 000000000..eb65228c8 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Claims/XsiConstants.cs @@ -0,0 +1,8 @@ +namespace CoreWCF.IdentityModel.Claims +{ + internal static class XsiConstants + { + public const string Namespace = "http://schemas.xmlsoap.org/ws/2005/05/identity"; + public const string System = "System"; + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/IdentityModelStrings.cs b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/IdentityModelStrings.cs new file mode 100644 index 000000000..fbef39091 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/IdentityModelStrings.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoreWCF.IdentityModel +{ + abstract class IdentityModelStrings + { + public abstract int Count { get; } + public abstract string this[int index] { get; } + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/IdentityModelStringsVersion1.cs b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/IdentityModelStringsVersion1.cs new file mode 100644 index 000000000..3eb5a91a8 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/IdentityModelStringsVersion1.cs @@ -0,0 +1,583 @@ +using CoreWCF; +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoreWCF.IdentityModel +{ + class IdentityModelStringsVersion1 : IdentityModelStrings + { + public const string String0 = "Algorithm"; + public const string String1 = "URI"; + public const string String2 = "Reference"; + public const string String3 = "Id"; + public const string String4 = "Transforms"; + public const string String5 = "Transform"; + public const string String6 = "DigestMethod"; + public const string String7 = "DigestValue"; + public const string String8 = "http://www.w3.org/2000/09/xmldsig#"; + public const string String9 = "http://www.w3.org/2000/09/xmldsig#enveloped-signature"; + public const string String10 = "KeyInfo"; + public const string String11 = "Signature"; + public const string String12 = "SignedInfo"; + public const string String13 = "CanonicalizationMethod"; + public const string String14 = "SignatureMethod"; + public const string String15 = "SignatureValue"; + public const string String16 = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"; + public const string String17 = "Timestamp"; + public const string String18 = "Created"; + public const string String19 = "Expires"; + public const string String20 = "http://www.w3.org/2001/10/xml-exc-c14n#"; + public const string String21 = "PrefixList"; + public const string String22 = "InclusiveNamespaces"; + public const string String23 = "ec"; + public const string String24 = "Access"; + public const string String25 = "AccessDecision"; + public const string String26 = "Action"; + public const string String27 = "Advice"; + public const string String28 = "Assertion"; + public const string String29 = "AssertionID"; + public const string String30 = "AssertionIDReference"; + public const string String31 = "Attribute"; + public const string String32 = "AttributeName"; + public const string String33 = "AttributeNamespace"; + public const string String34 = "AttributeStatement"; + public const string String35 = "AttributeValue"; + public const string String36 = "Audience"; + public const string String37 = "AudienceRestrictionCondition"; + public const string String38 = "AuthenticationInstant"; + public const string String39 = "AuthenticationMethod"; + public const string String40 = "AuthenticationStatement"; + public const string String41 = "AuthorityBinding"; + public const string String42 = "AuthorityKind"; + public const string String43 = "AuthorizationDecisionStatement"; + public const string String44 = "Binding"; + public const string String45 = "Condition"; + public const string String46 = "Conditions"; + public const string String47 = "Decision"; + public const string String48 = "DoNotCacheCondition"; + public const string String49 = "Evidence"; + public const string String50 = "IssueInstant"; + public const string String51 = "Issuer"; + public const string String52 = "Location"; + public const string String53 = "MajorVersion"; + public const string String54 = "MinorVersion"; + public const string String55 = "urn:oasis:names:tc:SAML:1.0:assertion"; + public const string String56 = "NameIdentifier"; + public const string String57 = "Format"; + public const string String58 = "NameQualifier"; + public const string String59 = "Namespace"; + public const string String60 = "NotBefore"; + public const string String61 = "NotOnOrAfter"; + public const string String62 = "saml"; + public const string String63 = "Statement"; + public const string String64 = "Subject"; + public const string String65 = "SubjectConfirmation"; + public const string String66 = "SubjectConfirmationData"; + public const string String67 = "ConfirmationMethod"; + public const string String68 = "urn:oasis:names:tc:SAML:1.0:cm:holder-of-key"; + public const string String69 = "urn:oasis:names:tc:SAML:1.0:cm:sender-vouches"; + public const string String70 = "SubjectLocality"; + public const string String71 = "DNSAddress"; + public const string String72 = "IPAddress"; + public const string String73 = "SubjectStatement"; + public const string String74 = "urn:oasis:names:tc:SAML:1.0:am:unspecified"; + public const string String75 = "xmlns"; + public const string String76 = "Resource"; + public const string String77 = "UserName"; + public const string String78 = "urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName"; + public const string String79 = "EmailName"; + public const string String80 = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"; + public const string String81 = "u"; + public const string String82 = "KeyName"; + public const string String83 = "Type"; + public const string String84 = "MgmtData"; + public const string String85 = ""; + public const string String86 = "KeyValue"; + public const string String87 = "RSAKeyValue"; + public const string String88 = "Modulus"; + public const string String89 = "Exponent"; + public const string String90 = "X509Data"; + public const string String91 = "X509IssuerSerial"; + public const string String92 = "X509IssuerName"; + public const string String93 = "X509SerialNumber"; + public const string String94 = "X509Certificate"; + public const string String95 = "http://www.w3.org/2001/04/xmlenc#aes128-cbc"; + public const string String96 = "http://www.w3.org/2001/04/xmlenc#kw-aes128"; + public const string String97 = "http://www.w3.org/2001/04/xmlenc#aes192-cbc"; + public const string String98 = "http://www.w3.org/2001/04/xmlenc#kw-aes192"; + public const string String99 = "http://www.w3.org/2001/04/xmlenc#aes256-cbc"; + public const string String100 = "http://www.w3.org/2001/04/xmlenc#kw-aes256"; + public const string String101 = "http://www.w3.org/2001/04/xmlenc#des-cbc"; + public const string String102 = "http://www.w3.org/2000/09/xmldsig#dsa-sha1"; + public const string String103 = "http://www.w3.org/2001/10/xml-exc-c14n#WithComments"; + public const string String104 = "http://www.w3.org/2000/09/xmldsig#hmac-sha1"; + public const string String105 = "http://www.w3.org/2001/04/xmldsig-more#hmac-sha256"; + public const string String106 = "http://schemas.xmlsoap.org/ws/2005/02/sc/dk/p_sha1"; + public const string String107 = "http://www.w3.org/2001/04/xmlenc#ripemd160"; + public const string String108 = "http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p"; + public const string String109 = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; + public const string String110 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"; + public const string String111 = "http://www.w3.org/2001/04/xmlenc#rsa-1_5"; + public const string String112 = "http://www.w3.org/2000/09/xmldsig#sha1"; + public const string String113 = "http://www.w3.org/2001/04/xmlenc#sha256"; + public const string String114 = "http://www.w3.org/2001/04/xmlenc#sha512"; + public const string String115 = "http://www.w3.org/2001/04/xmlenc#tripledes-cbc"; + public const string String116 = "http://www.w3.org/2001/04/xmlenc#kw-tripledes"; + public const string String117 = "http://schemas.xmlsoap.org/2005/02/trust/tlsnego#TLS_Wrap"; + public const string String118 = "http://schemas.xmlsoap.org/2005/02/trust/spnego#GSS_Wrap"; + public const string String119 = "o"; + public const string String120 = "Nonce"; + public const string String121 = "Password"; + public const string String122 = "PasswordText"; + public const string String123 = "Username"; + public const string String124 = "UsernameToken"; + public const string String125 = "BinarySecurityToken"; + public const string String126 = "EncodingType"; + public const string String127 = "KeyIdentifier"; + public const string String128 = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary"; + public const string String129 = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#HexBinary"; + public const string String130 = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Text"; + public const string String131 = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509SubjectKeyIdentifier"; + public const string String132 = "http://docs.oasis-open.org/wss/oasis-wss-kerberos-token-profile-1.1#GSS_Kerberosv5_AP_REQ"; + public const string String133 = "http://docs.oasis-open.org/wss/oasis-wss-kerberos-token-profile-1.1#GSS_Kerberosv5_AP_REQ1510"; + public const string String134 = "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.0#SAMLAssertionID"; + public const string String135 = "http://docs.oasis-open.org/wss/oasis-wss-rel-token-profile-1.0.pdf#license"; + public const string String136 = "FailedAuthentication"; + public const string String137 = "InvalidSecurityToken"; + public const string String138 = "InvalidSecurity"; + public const string String139 = "SecurityTokenReference"; + public const string String140 = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"; + public const string String141 = "Security"; + public const string String142 = "ValueType"; + public const string String143 = "http://docs.oasis-open.org/wss/oasis-wss-kerberos-token-profile-1.1#Kerberosv5APREQSHA1"; + public const string String144 = "k"; + public const string String145 = "SignatureConfirmation"; + public const string String146 = "Value"; + public const string String147 = "TokenType"; + public const string String148 = "http://docs.oasis-open.org/wss/oasis-wss-soap-message-security-1.1#ThumbprintSHA1"; + public const string String149 = "http://docs.oasis-open.org/wss/oasis-wss-soap-message-security-1.1#EncryptedKey"; + public const string String150 = "http://docs.oasis-open.org/wss/oasis-wss-soap-message-security-1.1#EncryptedKeySHA1"; + public const string String151 = "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1"; + public const string String152 = "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0"; + public const string String153 = "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLID"; + public const string String154 = "EncryptedHeader"; + public const string String155 = "http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd"; + public const string String156 = "http://www.w3.org/2001/04/xmlenc#"; + public const string String157 = "DataReference"; + public const string String158 = "EncryptedData"; + public const string String159 = "EncryptionMethod"; + public const string String160 = "CipherData"; + public const string String161 = "CipherValue"; + public const string String162 = "ReferenceList"; + public const string String163 = "Encoding"; + public const string String164 = "MimeType"; + public const string String165 = "CarriedKeyName"; + public const string String166 = "Recipient"; + public const string String167 = "EncryptedKey"; + public const string String168 = "KeyReference"; + public const string String169 = "e"; + public const string String170 = "http://www.w3.org/2001/04/xmlenc#Element"; + public const string String171 = "http://www.w3.org/2001/04/xmlenc#Content"; + public const string String172 = "http://schemas.xmlsoap.org/ws/2005/02/sc"; + public const string String173 = "DerivedKeyToken"; + public const string String174 = "Length"; + public const string String175 = "SecurityContextToken"; + public const string String176 = "Generation"; + public const string String177 = "Label"; + public const string String178 = "Offset"; + public const string String179 = "Properties"; + public const string String180 = "Identifier"; + public const string String181 = "Cookie"; + public const string String182 = "RenewNeeded"; + public const string String183 = "BadContextToken"; + public const string String184 = "c"; + public const string String185 = "http://schemas.xmlsoap.org/ws/2005/02/sc/dk"; + public const string String186 = "http://schemas.xmlsoap.org/ws/2005/02/sc/sct"; + public const string String187 = "http://schemas.xmlsoap.org/ws/2005/02/trust/RST/SCT"; + public const string String188 = "http://schemas.xmlsoap.org/ws/2005/02/trust/RSTR/SCT"; + public const string String189 = "http://schemas.xmlsoap.org/ws/2005/02/trust/RST/SCT/Renew"; + public const string String190 = "http://schemas.xmlsoap.org/ws/2005/02/trust/RSTR/SCT/Renew"; + public const string String191 = "http://schemas.xmlsoap.org/ws/2005/02/trust/RST/SCT/Cancel"; + public const string String192 = "http://schemas.xmlsoap.org/ws/2005/02/trust/RSTR/SCT/Cancel"; + public const string String193 = "RequestSecurityTokenResponseCollection"; + public const string String194 = "http://schemas.xmlsoap.org/ws/2005/02/trust"; + public const string String195 = "http://schemas.xmlsoap.org/ws/2005/02/trust#BinarySecret"; + public const string String196 = "AUTH-HASH"; + public const string String197 = "RequestSecurityTokenResponse"; + public const string String198 = "KeySize"; + public const string String199 = "RequestedTokenReference"; + public const string String200 = "AppliesTo"; + public const string String201 = "Authenticator"; + public const string String202 = "CombinedHash"; + public const string String203 = "BinaryExchange"; + public const string String204 = "Lifetime"; + public const string String205 = "RequestedSecurityToken"; + public const string String206 = "Entropy"; + public const string String207 = "RequestedProofToken"; + public const string String208 = "ComputedKey"; + public const string String209 = "RequestSecurityToken"; + public const string String210 = "RequestType"; + public const string String211 = "Context"; + public const string String212 = "BinarySecret"; + public const string String213 = "http://schemas.microsoft.com/net/2004/07/secext/WS-SPNego"; + public const string String214 = "http://schemas.microsoft.com/net/2004/07/secext/TLSNego"; + public const string String215 = "t"; + public const string String216 = "http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue"; + public const string String217 = "http://schemas.xmlsoap.org/ws/2005/02/trust/RSTR/Issue"; + public const string String218 = "http://schemas.xmlsoap.org/ws/2005/02/trust/Issue"; + public const string String219 = "http://schemas.xmlsoap.org/ws/2005/02/trust/SymmetricKey"; + public const string String220 = "http://schemas.xmlsoap.org/ws/2005/02/trust/CK/PSHA1"; + public const string String221 = "http://schemas.xmlsoap.org/ws/2005/02/trust/Nonce"; + public const string String222 = "RenewTarget"; + public const string String223 = "CancelTarget"; + public const string String224 = "RequestedTokenCancelled"; + public const string String225 = "RequestedAttachedReference"; + public const string String226 = "RequestedUnattachedReference"; + public const string String227 = "IssuedTokens"; + public const string String228 = "http://schemas.xmlsoap.org/ws/2005/02/trust/Renew"; + public const string String229 = "http://schemas.xmlsoap.org/ws/2005/02/trust/Cancel"; + public const string String230 = "KeyType"; + public const string String231 = "http://schemas.xmlsoap.org/ws/2005/02/trust/PublicKey"; + public const string String232 = "Claims"; + public const string String233 = "InvalidRequest"; + public const string String234 = "UseKey"; + public const string String235 = "SignWith"; + public const string String236 = "EncryptWith"; + public const string String237 = "EncryptionAlgorithm"; + public const string String238 = "CanonicalizationAlgorithm"; + public const string String239 = "ComputedKeyAlgorithm"; + public const string String240 = "http://schemas.xmlsoap.org/ws/2005/02/trust/spnego"; + public const string String241 = "http://schemas.xmlsoap.org/ws/2005/02/trust/tlsnego"; + public const string String242 = "trust"; + public const string String243 = "http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue"; + public const string String244 = "http://docs.oasis-open.org/ws-sx/ws-trust/200512/RSTR/Issue"; + public const string String245 = "http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue"; + public const string String246 = "http://docs.oasis-open.org/ws-sx/ws-trust/200512/AsymmetricKey"; + public const string String247 = "http://docs.oasis-open.org/ws-sx/ws-trust/200512/SymmetricKey"; + public const string String248 = "http://docs.oasis-open.org/ws-sx/ws-trust/200512/Nonce"; + public const string String249 = "http://docs.oasis-open.org/ws-sx/ws-trust/200512/CK/PSHA1"; + public const string String250 = "http://docs.oasis-open.org/ws-sx/ws-trust/200512/PublicKey"; + public const string String251 = "http://docs.oasis-open.org/ws-sx/ws-trust/200512"; + public const string String252 = "http://docs.oasis-open.org/ws-sx/ws-trust/200512#BinarySecret"; + public const string String253 = "http://docs.oasis-open.org/ws-sx/ws-trust/200512/RSTRC/IssueFinal"; + public const string String254 = "http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Renew"; + public const string String255 = "http://docs.oasis-open.org/ws-sx/ws-trust/200512/RSTR/Renew"; + public const string String256 = "http://docs.oasis-open.org/ws-sx/ws-trust/200512/RSTR/RenewFinal"; + public const string String257 = "http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Cancel"; + public const string String258 = "http://docs.oasis-open.org/ws-sx/ws-trust/200512/RSTR/Cancel"; + public const string String259 = "http://docs.oasis-open.org/ws-sx/ws-trust/200512/RSTR/CancelFinal"; + public const string String260 = "http://docs.oasis-open.org/ws-sx/ws-trust/200512/Renew"; + public const string String261 = "http://docs.oasis-open.org/ws-sx/ws-trust/200512/Cancel"; + public const string String262 = "KeyWrapAlgorithm"; + public const string String263 = "http://docs.oasis-open.org/ws-sx/ws-trust/200512/Bearer"; + public const string String264 = "SecondaryParameters"; + public const string String265 = "Dialect"; + public const string String266 = "http://schemas.xmlsoap.org/ws/2005/05/identity"; + public const string String267 = "http://docs.oasis-open.org/ws-sx/ws-secureconversation/200512/dk/p_sha1"; + public const string String268 = "sc"; + public const string String269 = "http://docs.oasis-open.org/ws-sx/ws-secureconversation/200512/dk"; + public const string String270 = "http://docs.oasis-open.org/ws-sx/ws-secureconversation/200512/sct"; + public const string String271 = "http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/SCT"; + public const string String272 = "http://docs.oasis-open.org/ws-sx/ws-trust/200512/RSTR/SCT"; + public const string String273 = "http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/SCT/Renew"; + public const string String274 = "http://docs.oasis-open.org/ws-sx/ws-trust/200512/RSTR/SCT/Renew"; + public const string String275 = "http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/SCT/Cancel"; + public const string String276 = "http://docs.oasis-open.org/ws-sx/ws-trust/200512/RSTR/SCT/Cancel"; + public const string String277 = "http://docs.oasis-open.org/ws-sx/ws-secureconversation/200512"; + public const string String278 = "Instance"; + + public override int Count { get { return 279; } } + + public override string this[int index] + { + get + { + DiagnosticUtility.DebugAssert(index >= 0 && index < Count, "The index is out of range. It should be greater than or equal to 0 and less than" + Count.ToString()); + switch (index) + { + case 0: return String0; + case 1: return String1; + case 2: return String2; + case 3: return String3; + case 4: return String4; + case 5: return String5; + case 6: return String6; + case 7: return String7; + case 8: return String8; + case 9: return String9; + case 10: return String10; + case 11: return String11; + case 12: return String12; + case 13: return String13; + case 14: return String14; + case 15: return String15; + case 16: return String16; + case 17: return String17; + case 18: return String18; + case 19: return String19; + case 20: return String20; + case 21: return String21; + case 22: return String22; + case 23: return String23; + case 24: return String24; + case 25: return String25; + case 26: return String26; + case 27: return String27; + case 28: return String28; + case 29: return String29; + case 30: return String30; + case 31: return String31; + case 32: return String32; + case 33: return String33; + case 34: return String34; + case 35: return String35; + case 36: return String36; + case 37: return String37; + case 38: return String38; + case 39: return String39; + case 40: return String40; + case 41: return String41; + case 42: return String42; + case 43: return String43; + case 44: return String44; + case 45: return String45; + case 46: return String46; + case 47: return String47; + case 48: return String48; + case 49: return String49; + case 50: return String50; + case 51: return String51; + case 52: return String52; + case 53: return String53; + case 54: return String54; + case 55: return String55; + case 56: return String56; + case 57: return String57; + case 58: return String58; + case 59: return String59; + case 60: return String60; + case 61: return String61; + case 62: return String62; + case 63: return String63; + case 64: return String64; + case 65: return String65; + case 66: return String66; + case 67: return String67; + case 68: return String68; + case 69: return String69; + case 70: return String70; + case 71: return String71; + case 72: return String72; + case 73: return String73; + case 74: return String74; + case 75: return String75; + case 76: return String76; + case 77: return String77; + case 78: return String78; + case 79: return String79; + case 80: return String80; + case 81: return String81; + case 82: return String82; + case 83: return String83; + case 84: return String84; + case 85: return String85; + case 86: return String86; + case 87: return String87; + case 88: return String88; + case 89: return String89; + case 90: return String90; + case 91: return String91; + case 92: return String92; + case 93: return String93; + case 94: return String94; + case 95: return String95; + case 96: return String96; + case 97: return String97; + case 98: return String98; + case 99: return String99; + case 100: return String100; + case 101: return String101; + case 102: return String102; + case 103: return String103; + case 104: return String104; + case 105: return String105; + case 106: return String106; + case 107: return String107; + case 108: return String108; + case 109: return String109; + case 110: return String110; + case 111: return String111; + case 112: return String112; + case 113: return String113; + case 114: return String114; + case 115: return String115; + case 116: return String116; + case 117: return String117; + case 118: return String118; + case 119: return String119; + case 120: return String120; + case 121: return String121; + case 122: return String122; + case 123: return String123; + case 124: return String124; + case 125: return String125; + case 126: return String126; + case 127: return String127; + case 128: return String128; + case 129: return String129; + case 130: return String130; + case 131: return String131; + case 132: return String132; + case 133: return String133; + case 134: return String134; + case 135: return String135; + case 136: return String136; + case 137: return String137; + case 138: return String138; + case 139: return String139; + case 140: return String140; + case 141: return String141; + case 142: return String142; + case 143: return String143; + case 144: return String144; + case 145: return String145; + case 146: return String146; + case 147: return String147; + case 148: return String148; + case 149: return String149; + case 150: return String150; + case 151: return String151; + case 152: return String152; + case 153: return String153; + case 154: return String154; + case 155: return String155; + case 156: return String156; + case 157: return String157; + case 158: return String158; + case 159: return String159; + case 160: return String160; + case 161: return String161; + case 162: return String162; + case 163: return String163; + case 164: return String164; + case 165: return String165; + case 166: return String166; + case 167: return String167; + case 168: return String168; + case 169: return String169; + case 170: return String170; + case 171: return String171; + case 172: return String172; + case 173: return String173; + case 174: return String174; + case 175: return String175; + case 176: return String176; + case 177: return String177; + case 178: return String178; + case 179: return String179; + case 180: return String180; + case 181: return String181; + case 182: return String182; + case 183: return String183; + case 184: return String184; + case 185: return String185; + case 186: return String186; + case 187: return String187; + case 188: return String188; + case 189: return String189; + case 190: return String190; + case 191: return String191; + case 192: return String192; + case 193: return String193; + case 194: return String194; + case 195: return String195; + case 196: return String196; + case 197: return String197; + case 198: return String198; + case 199: return String199; + case 200: return String200; + case 201: return String201; + case 202: return String202; + case 203: return String203; + case 204: return String204; + case 205: return String205; + case 206: return String206; + case 207: return String207; + case 208: return String208; + case 209: return String209; + case 210: return String210; + case 211: return String211; + case 212: return String212; + case 213: return String213; + case 214: return String214; + case 215: return String215; + case 216: return String216; + case 217: return String217; + case 218: return String218; + case 219: return String219; + case 220: return String220; + case 221: return String221; + case 222: return String222; + case 223: return String223; + case 224: return String224; + case 225: return String225; + case 226: return String226; + case 227: return String227; + case 228: return String228; + case 229: return String229; + case 230: return String230; + case 231: return String231; + case 232: return String232; + case 233: return String233; + case 234: return String234; + case 235: return String235; + case 236: return String236; + case 237: return String237; + case 238: return String238; + case 239: return String239; + case 240: return String240; + case 241: return String241; + case 242: return String242; + case 243: return String243; + case 244: return String244; + case 245: return String245; + case 246: return String246; + case 247: return String247; + case 248: return String248; + case 249: return String249; + case 250: return String250; + case 251: return String251; + case 252: return String252; + case 253: return String253; + case 254: return String254; + case 255: return String255; + case 256: return String256; + case 257: return String257; + case 258: return String258; + case 259: return String259; + case 260: return String260; + case 261: return String261; + case 262: return String262; + case 263: return String263; + case 264: return String264; + case 265: return String265; + case 266: return String266; + case 267: return String267; + case 268: return String268; + case 269: return String269; + case 270: return String270; + case 271: return String271; + case 272: return String272; + case 273: return String273; + case 274: return String274; + case 275: return String275; + case 276: return String276; + case 277: return String277; + case 278: return String278; + default: return null; + } + } + } + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Policy/AuthorizationContext.cs b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Policy/AuthorizationContext.cs new file mode 100644 index 000000000..6602a8e89 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Policy/AuthorizationContext.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using CoreWCF.IdentityModel.Claims; + +namespace CoreWCF.IdentityModel.Policy +{ + internal abstract class AuthorizationContext : IAuthorizationComponent + { + public abstract string Id { get; } + public abstract ReadOnlyCollection ClaimSets { get; } + public abstract DateTime ExpirationTime { get; } + public abstract IDictionary Properties { get; } + + public static AuthorizationContext CreateDefaultAuthorizationContext(IList authorizationPolicies) + { + //return SecurityUtils.CreateDefaultAuthorizationContext(authorizationPolicies); + throw new PlatformNotSupportedException(); + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Policy/EvaluationContext.cs b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Policy/EvaluationContext.cs new file mode 100644 index 000000000..a0ce1c3f7 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Policy/EvaluationContext.cs @@ -0,0 +1,17 @@ +using CoreWCF.IdentityModel.Claims; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Text; + +namespace CoreWCF.IdentityModel.Policy +{ + internal abstract class EvaluationContext + { + public abstract ReadOnlyCollection ClaimSets { get; } + public abstract IDictionary Properties { get; } + public abstract int Generation { get; } + public abstract void AddClaimSet(IAuthorizationPolicy policy, ClaimSet claimSet); + public abstract void RecordExpirationTime(DateTime expirationTime); + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Policy/IAuthorizationComponent.cs b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Policy/IAuthorizationComponent.cs new file mode 100644 index 000000000..3bd7e41ca --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Policy/IAuthorizationComponent.cs @@ -0,0 +1,7 @@ +namespace CoreWCF.IdentityModel.Policy +{ + public interface IAuthorizationComponent + { + string Id { get; } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Policy/IAuthorizationPolicy.cs b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Policy/IAuthorizationPolicy.cs new file mode 100644 index 000000000..742bbccbb --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Policy/IAuthorizationPolicy.cs @@ -0,0 +1,15 @@ +namespace CoreWCF.IdentityModel.Policy +{ + // Issues claimsets whose conditions (if any) have been evaluated. + public interface IAuthorizationPolicy : IAuthorizationComponent + { + //ClaimSet Issuer { get; } + + // Evaluates conditions (if any) against the context, may add grants to the context + // Return 'false' if for this evaluation, should be called again if claims change. (eg. not done) + // Return 'true' if no more claims will be added regardless of changes for this evaluation (eg. done). + // 'state' is good for this evaluation only. Will be null if starting again. + // Implementations should expect to be called multiple times on different threads. + //bool Evaluate(EvaluationContext evaluationContext, ref object state); + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Policy/UnconditionalPolicy.cs b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Policy/UnconditionalPolicy.cs new file mode 100644 index 000000000..e9c4e0819 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Policy/UnconditionalPolicy.cs @@ -0,0 +1,252 @@ +using CoreWCF.IdentityModel.Claims; +using CoreWCF; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Security.Principal; +using System.Text; + +namespace CoreWCF.IdentityModel.Policy +{ + internal interface IIdentityInfo + { + IIdentity Identity { get; } + } + + class UnconditionalPolicy : IAuthorizationPolicy, IDisposable + { + SecurityUniqueId id; + ClaimSet issuer; + ClaimSet issuance; + ReadOnlyCollection issuances; + DateTime expirationTime; + IIdentity primaryIdentity; + bool disposable = false; + bool disposed = false; + + public UnconditionalPolicy(ClaimSet issuance) + : this(issuance, SecurityUtils.MaxUtcDateTime) + { + } + + public UnconditionalPolicy(ClaimSet issuance, DateTime expirationTime) + { + if (issuance == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("issuance"); + + Initialize(ClaimSet.System, issuance, null, expirationTime); + } + + public UnconditionalPolicy(ReadOnlyCollection issuances, DateTime expirationTime) + { + if (issuances == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("issuances"); + + Initialize(ClaimSet.System, null, issuances, expirationTime); + } + + internal UnconditionalPolicy(IIdentity primaryIdentity, ClaimSet issuance) + : this(issuance) + { + this.primaryIdentity = primaryIdentity; + } + + internal UnconditionalPolicy(IIdentity primaryIdentity, ClaimSet issuance, DateTime expirationTime) + : this(issuance, expirationTime) + { + this.primaryIdentity = primaryIdentity; + } + + internal UnconditionalPolicy(IIdentity primaryIdentity, ReadOnlyCollection issuances, DateTime expirationTime) + : this(issuances, expirationTime) + { + this.primaryIdentity = primaryIdentity; + } + + UnconditionalPolicy(UnconditionalPolicy from) + { + disposable = from.disposable; + primaryIdentity = from.disposable ? SecurityUtils.CloneIdentityIfNecessary(from.primaryIdentity) : from.primaryIdentity; + if (from.issuance != null) + { + issuance = from.disposable ? SecurityUtils.CloneClaimSetIfNecessary(from.issuance) : from.issuance; + } + else + { + issuances = from.disposable ? SecurityUtils.CloneClaimSetsIfNecessary(from.issuances) : from.issuances; + } + issuer = from.issuer; + expirationTime = from.expirationTime; + } + + void Initialize(ClaimSet issuer, ClaimSet issuance, ReadOnlyCollection issuances, DateTime expirationTime) + { + this.issuer = issuer; + this.issuance = issuance; + this.issuances = issuances; + this.expirationTime = expirationTime; + if (issuance != null) + { + disposable = issuance is WindowsClaimSet; + } + else + { + for (int i = 0; i < issuances.Count; ++i) + { + if (issuances[i] is WindowsClaimSet) + { + disposable = true; + break; + } + } + } + } + + public string Id + { + get + { + if (id == null) + id = SecurityUniqueId.Create(); + return id.Value; + } + } + + public ClaimSet Issuer + { + get { return issuer; } + } + + internal IIdentity PrimaryIdentity + { + get + { + ThrowIfDisposed(); + if (primaryIdentity == null) + { + IIdentity identity = null; + if (this.issuance != null) + { + if (issuance is IIdentityInfo) + { + identity = ((IIdentityInfo)issuance).Identity; + } + } + else + { + for (int i = 0; i < issuances.Count; ++i) + { + ClaimSet issuance = issuances[i]; + if (issuance is IIdentityInfo) + { + identity = ((IIdentityInfo)issuance).Identity; + // Preferably Non-Anonymous + if (identity != null && identity != SecurityUtils.AnonymousIdentity) + { + break; + } + } + } + } + primaryIdentity = identity ?? SecurityUtils.AnonymousIdentity; + } + return primaryIdentity; + } + } + + internal ReadOnlyCollection Issuances + { + get + { + ThrowIfDisposed(); + if (this.issuances == null) + { + List issuances = new List(1); + issuances.Add(issuance); + this.issuances = issuances.AsReadOnly(); + } + return this.issuances; + } + } + + public DateTime ExpirationTime + { + get { return expirationTime; } + } + + internal bool IsDisposable + { + get { return disposable; } + } + + internal UnconditionalPolicy Clone() + { + ThrowIfDisposed(); + return (disposable) ? new UnconditionalPolicy(this) : this; + } + + public virtual void Dispose() + { + if (disposable && !disposed) + { + disposed = true; + SecurityUtils.DisposeIfNecessary(primaryIdentity as WindowsIdentity); + SecurityUtils.DisposeClaimSetIfNecessary(issuance); + SecurityUtils.DisposeClaimSetsIfNecessary(issuances); + } + } + + void ThrowIfDisposed() + { + if (disposed) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ObjectDisposedException(GetType().FullName)); + } + } + + public virtual bool Evaluate(EvaluationContext evaluationContext, ref object state) + { + ThrowIfDisposed(); + if (issuance != null) + { + evaluationContext.AddClaimSet(this, issuance); + } + else + { + for (int i = 0; i < issuances.Count; ++i) + { + if (issuances[i] != null) + { + evaluationContext.AddClaimSet(this, issuances[i]); + } + } + } + + // Preferably Non-Anonymous + if (PrimaryIdentity != null && PrimaryIdentity != SecurityUtils.AnonymousIdentity) + { + IList identities; + object obj; + if (!evaluationContext.Properties.TryGetValue(SecurityUtils.Identities, out obj)) + { + identities = new List(1); + evaluationContext.Properties.Add(SecurityUtils.Identities, identities); + } + else + { + // null if other overrides the property with something else + identities = obj as IList; + } + + if (identities != null) + { + identities.Add(PrimaryIdentity); + } + } + + evaluationContext.RecordExpirationTime(expirationTime); + return true; + } + } + +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/SecurityKeyIdentifierClause.cs b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/SecurityKeyIdentifierClause.cs new file mode 100644 index 000000000..50f2b02b4 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/SecurityKeyIdentifierClause.cs @@ -0,0 +1,71 @@ +using CoreWCF.IdentityModel.Tokens; +using CoreWCF; +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoreWCF.IdentityModel +{ + // All subclasses are required to be thread-safe and immutable + + // Self-resolving clauses such as RSA and X509 raw data should + // override CanCreateKey and return true, and implement + // CreateKey() + + internal abstract class SecurityKeyIdentifierClause + { + readonly string clauseType; + byte[] derivationNonce; + int derivationLength; + string id = null; + + protected SecurityKeyIdentifierClause(string clauseType) + : this(clauseType, null, 0) + { + } + + protected SecurityKeyIdentifierClause(string clauseType, byte[] nonce, int length) + { + this.clauseType = clauseType; + derivationNonce = nonce; + derivationLength = length; + } + + public virtual bool CanCreateKey + { + get { return false; } + } + + public string ClauseType + { + get { return clauseType; } + } + + public string Id + { + get { return id; } + set { id = value; } + } + + public virtual SecurityKey CreateKey() + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.KeyIdentifierClauseDoesNotSupportKeyCreation)); + } + + public virtual bool Matches(SecurityKeyIdentifierClause keyIdentifierClause) + { + return ReferenceEquals(this, keyIdentifierClause); + } + + public byte[] GetDerivationNonce() + { + return (derivationNonce != null) ? (byte[])derivationNonce.Clone() : null; + } + + public int DerivationLength + { + get { return derivationLength; } + } + } + +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/SecurityUniqueId.cs b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/SecurityUniqueId.cs new file mode 100644 index 000000000..172af2a5b --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/SecurityUniqueId.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Threading; + +namespace CoreWCF.IdentityModel +{ + internal class SecurityUniqueId + { + static long nextId = 0; + static string commonPrefix = "uuid-" + Guid.NewGuid().ToString() + "-"; + + long id; + string prefix; + string val; + + SecurityUniqueId(string prefix, long id) + { + this.id = id; + this.prefix = prefix; + val = null; + } + + public static SecurityUniqueId Create() + { + return SecurityUniqueId.Create(commonPrefix); + } + + public static SecurityUniqueId Create(string prefix) + { + return new SecurityUniqueId(prefix, Interlocked.Increment(ref nextId)); + } + + public string Value + { + get + { + if (val == null) + val = prefix + id.ToString(CultureInfo.InvariantCulture); + + return val; + } + } + } + +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/SecurityUtils.cs b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/SecurityUtils.cs new file mode 100644 index 000000000..329349425 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/SecurityUtils.cs @@ -0,0 +1,253 @@ +using System; +using System.Text; +using CoreWCF.IdentityModel.Claims; +using CoreWCF.Runtime; +using CoreWCF; +using System.Security.Cryptography.X509Certificates; +using System.Security.Principal; +using System.Collections.ObjectModel; +using System.Collections.Generic; +using CoreWCF.IdentityModel.Policy; + +namespace CoreWCF.IdentityModel +{ + internal static class SecurityUtils + { + public const string Identities = "Identities"; + static IIdentity anonymousIdentity; + + public const string AuthTypeCertMap = "SSL/PCT"; // mapped from a cert + + internal static IIdentity AnonymousIdentity + { + get + { + if (anonymousIdentity == null) + anonymousIdentity = SecurityUtils.CreateIdentity(string.Empty); + return anonymousIdentity; + } + } + + public static DateTime MaxUtcDateTime + { + get + { + // + and - TimeSpan.TicksPerDay is to compensate the DateTime.ParseExact (to localtime) overflow. + return new DateTime(DateTime.MaxValue.Ticks - TimeSpan.TicksPerDay, DateTimeKind.Utc); + } + } + + public static DateTime MinUtcDateTime + { + get + { + // + and - TimeSpan.TicksPerDay is to compensate the DateTime.ParseExact (to localtime) overflow. + return new DateTime(DateTime.MinValue.Ticks + TimeSpan.TicksPerDay, DateTimeKind.Utc); + } + } + + internal static IIdentity CreateIdentity(string name, string authenticationType) + { + return new GenericIdentity(name, authenticationType); + } + + internal static IIdentity CreateIdentity(string name) + { + return new GenericIdentity(name); + } + + internal static byte[] CloneBuffer(byte[] buffer) + { + return CloneBuffer(buffer, 0, buffer.Length); + } + + internal static byte[] CloneBuffer(byte[] buffer, int offset, int len) + { + DiagnosticUtility.DebugAssert(offset >= 0, "Negative offset passed to CloneBuffer."); + DiagnosticUtility.DebugAssert(len >= 0, "Negative len passed to CloneBuffer."); + DiagnosticUtility.DebugAssert(buffer.Length - offset >= len, "Invalid parameters to CloneBuffer."); + + byte[] copy = Fx.AllocateByteArray(len); + Buffer.BlockCopy(buffer, offset, copy, 0, len); + return copy; + } + + internal static string GetCertificateId(X509Certificate2 certificate) + { + string certificateId = certificate.SubjectName.Name; + if (string.IsNullOrEmpty(certificateId)) + certificateId = certificate.Thumbprint; + return certificateId; + } + + internal static void ResetCertificate(X509Certificate2 certificate) + { + // Check that Dispose() and Reset() do the same thing + certificate.Dispose(); + } + + internal static string ClaimSetToString(ClaimSet claimSet) + { + StringBuilder sb = new StringBuilder(); + sb.AppendLine("ClaimSet ["); + for (int i = 0; i < claimSet.Count; i++) + { + Claim claim = claimSet[i]; + if (claim != null) + { + sb.Append(" "); + sb.AppendLine(claim.ToString()); + } + } + string prefix = "] by "; + ClaimSet issuer = claimSet; + do + { + issuer = issuer.Issuer; + sb.AppendFormat("{0}{1}", prefix, issuer == claimSet ? "Self" : (issuer.Count <= 0 ? "Unknown" : issuer[0].ToString())); + prefix = " -> "; + } while (issuer.Issuer != issuer); + return sb.ToString(); + } + + // This is the workaround, Since store.Certificates returns a full collection + // of certs in store. These are holding native resources. + internal static void ResetAllCertificates(X509Certificate2Collection certificates) + { + if (certificates != null) + { + for (int i = 0; i < certificates.Count; ++i) + { + ResetCertificate(certificates[i]); + } + } + } + + internal static ReadOnlyCollection CreateAuthorizationPolicies(ClaimSet claimSet) + { + return CreateAuthorizationPolicies(claimSet, SecurityUtils.MaxUtcDateTime); + } + + internal static ReadOnlyCollection CreateAuthorizationPolicies(ClaimSet claimSet, DateTime expirationTime) + { + List policies = new List(1); + policies.Add(new UnconditionalPolicy(claimSet, expirationTime)); + return policies.AsReadOnly(); + } + + internal static IIdentity CloneIdentityIfNecessary(IIdentity identity) + { + if (identity != null) + { + WindowsIdentity wid = identity as WindowsIdentity; + if (wid != null) + { + return CloneWindowsIdentityIfNecessary(wid); + } + } + return identity; + } + + internal static WindowsIdentity CloneWindowsIdentityIfNecessary(WindowsIdentity wid) + { + return SecurityUtils.CloneWindowsIdentityIfNecessary(wid, null); + } + + internal static WindowsIdentity CloneWindowsIdentityIfNecessary(WindowsIdentity wid, string authType) + { + if (wid != null) + { + IntPtr token = UnsafeGetWindowsIdentityToken(wid); + if (token != IntPtr.Zero) + { + return UnsafeCreateWindowsIdentityFromToken(token, authType); + } + } + return wid; + } + + static IntPtr UnsafeGetWindowsIdentityToken(WindowsIdentity wid) + { + return wid.Token; + } + + static WindowsIdentity UnsafeCreateWindowsIdentityFromToken(IntPtr token, string authType) + { + if (authType != null) + return new WindowsIdentity(token, authType); + else + return new WindowsIdentity(token); + } + + internal static ClaimSet CloneClaimSetIfNecessary(ClaimSet claimSet) + { + if (claimSet != null) + { + WindowsClaimSet wic = claimSet as WindowsClaimSet; + if (wic != null) + { + return wic.Clone(); + } + } + return claimSet; + } + + internal static ReadOnlyCollection CloneClaimSetsIfNecessary(ReadOnlyCollection claimSets) + { + if (claimSets != null) + { + bool clone = false; + for (int i = 0; i < claimSets.Count; ++i) + { + if (claimSets[i] is WindowsClaimSet) + { + clone = true; + break; + } + } + if (clone) + { + List ret = new List(claimSets.Count); + for (int i = 0; i < claimSets.Count; ++i) + { + ret.Add(SecurityUtils.CloneClaimSetIfNecessary(claimSets[i])); + } + return ret.AsReadOnly(); + } + } + return claimSets; + } + + internal static void DisposeClaimSetIfNecessary(ClaimSet claimSet) + { + if (claimSet != null) + { + SecurityUtils.DisposeIfNecessary(claimSet as WindowsClaimSet); + } + } + + internal static void DisposeClaimSetsIfNecessary(ReadOnlyCollection claimSets) + { + if (claimSets != null) + { + for (int i = 0; i < claimSets.Count; ++i) + { + SecurityUtils.DisposeIfNecessary(claimSets[i] as WindowsClaimSet); + } + } + } + + public static void DisposeIfNecessary(IDisposable obj) + { + if (obj != null) + { + obj.Dispose(); + } + } + } + + static class EmptyReadOnlyCollection + { + public static ReadOnlyCollection Instance = new ReadOnlyCollection(new List()); + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/CustomUserNameSecurityTokenAuthenticator.cs b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/CustomUserNameSecurityTokenAuthenticator.cs new file mode 100644 index 000000000..1c92ff384 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/CustomUserNameSecurityTokenAuthenticator.cs @@ -0,0 +1,52 @@ +using CoreWCF.IdentityModel.Claims; +using CoreWCF.IdentityModel.Policy; +using CoreWCF; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Security.Principal; +using System.Text; + +namespace CoreWCF.IdentityModel.Selectors +{ + internal class CustomUserNameSecurityTokenAuthenticator : UserNameSecurityTokenAuthenticator + { + UserNamePasswordValidator validator; + + public CustomUserNameSecurityTokenAuthenticator(UserNamePasswordValidator validator) + { + if (validator == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("validator"); + this.validator = validator; + } + + protected override ReadOnlyCollection ValidateUserNamePasswordCore(string userName, string password) + { + validator.Validate(userName, password); + return SecurityUtils.CreateAuthorizationPolicies(new UserNameClaimSet(userName, validator.GetType().Name)); + } + + class UserNameClaimSet : DefaultClaimSet, IIdentityInfo + { + IIdentity identity; + + public UserNameClaimSet(string userName, string authType) + { + if (userName == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("userName"); + + identity = SecurityUtils.CreateIdentity(userName, authType); + + List claims = new List(2); + claims.Add(new Claim(ClaimTypes.Name, userName, Rights.Identity)); + claims.Add(Claim.CreateNameClaim(userName)); + Initialize(ClaimSet.System, claims); + } + + public IIdentity Identity + { + get { return identity; } + } + } + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/RsaSecurityTokenAuthenticator.cs b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/RsaSecurityTokenAuthenticator.cs new file mode 100644 index 000000000..4150bcf94 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/RsaSecurityTokenAuthenticator.cs @@ -0,0 +1,35 @@ +using CoreWCF.IdentityModel.Claims; +using CoreWCF.IdentityModel.Policy; +using CoreWCF.IdentityModel.Tokens; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Text; + +namespace CoreWCF.IdentityModel.Selectors +{ + internal class RsaSecurityTokenAuthenticator : SecurityTokenAuthenticator + { + public RsaSecurityTokenAuthenticator() + { + } + + protected override bool CanValidateTokenCore(SecurityToken token) + { + return token is RsaSecurityToken; + } + + protected override ReadOnlyCollection ValidateTokenCore(SecurityToken token) + { + RsaSecurityToken rsaToken = (RsaSecurityToken)token; + List claims = new List(2); + claims.Add(new Claim(ClaimTypes.Rsa, rsaToken.Rsa, Rights.Identity)); + claims.Add(Claim.CreateRsaClaim(rsaToken.Rsa)); + + DefaultClaimSet claimSet = new DefaultClaimSet(ClaimSet.Anonymous, claims); + List policies = new List(1); + policies.Add(new UnconditionalPolicy(claimSet, rsaToken.ValidTo)); + return policies.AsReadOnly(); + } + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/SecurityTokenAuthenticator.cs b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/SecurityTokenAuthenticator.cs new file mode 100644 index 000000000..1df69ba1a --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/SecurityTokenAuthenticator.cs @@ -0,0 +1,49 @@ +using CoreWCF.IdentityModel.Policy; +using CoreWCF.IdentityModel.Tokens; +using CoreWCF; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Text; + +namespace CoreWCF.IdentityModel.Selectors +{ + public abstract class SecurityTokenAuthenticator + { + protected SecurityTokenAuthenticator() { } + + public bool CanValidateToken(SecurityToken token) + { + if (token == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("token"); + } + return CanValidateTokenCore(token); + } + + public ReadOnlyCollection ValidateToken(SecurityToken token) + { + if (token == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("token"); + } + if (!CanValidateToken(token)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenValidationException(SR.Format(SR.CannotValidateSecurityTokenType, this, token.GetType()))); + } + + ReadOnlyCollection authorizationPolicies = ValidateTokenCore(token); + if (authorizationPolicies == null) + { + string errorMsg = SR.Format(SR.CannotValidateSecurityTokenType, this, token.GetType()); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenValidationException(errorMsg)); + } + + return authorizationPolicies; + } + + protected abstract bool CanValidateTokenCore(SecurityToken token); + protected abstract ReadOnlyCollection ValidateTokenCore(SecurityToken token); + } + +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/SecurityTokenManager.cs b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/SecurityTokenManager.cs new file mode 100644 index 000000000..d148a4d73 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/SecurityTokenManager.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoreWCF.IdentityModel.Selectors +{ + internal abstract class SecurityTokenManager + { + public abstract SecurityTokenProvider CreateSecurityTokenProvider(SecurityTokenRequirement tokenRequirement); + internal abstract SecurityTokenSerializer CreateSecurityTokenSerializer(SecurityTokenVersion version); + public abstract SecurityTokenAuthenticator CreateSecurityTokenAuthenticator(SecurityTokenRequirement tokenRequirement, out SecurityTokenResolver outOfBandTokenResolver); + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/SecurityTokenProvider.cs b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/SecurityTokenProvider.cs new file mode 100644 index 000000000..008c5833d --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/SecurityTokenProvider.cs @@ -0,0 +1,118 @@ +using CoreWCF.IdentityModel.Tokens; +using CoreWCF; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace CoreWCF.IdentityModel.Selectors +{ + public abstract class SecurityTokenProvider + { + protected SecurityTokenProvider() { } + + public virtual bool SupportsTokenRenewal + { + get { return false; } + } + + public virtual bool SupportsTokenCancellation + { + get { return false; } + } + + public SecurityToken GetToken(TimeSpan timeout) + { + SecurityToken token = GetTokenCore(timeout); + if (token == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.Format(SR.TokenProviderUnableToGetToken, this))); + } + return token; + } + + public Task GetTokenAsync(CancellationToken token) + { + return GetTokenCoreAsync(token); + } + + public SecurityToken RenewToken(TimeSpan timeout, SecurityToken tokenToBeRenewed) + { + if (tokenToBeRenewed == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("tokenToBeRenewed"); + } + SecurityToken token = RenewTokenCore(timeout, tokenToBeRenewed); + if (token == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.Format(SR.TokenProviderUnableToRenewToken, this))); + } + return token; + } + + public async Task RenewTokenAsync(SecurityToken tokenToBeRenewed, TimeSpan timeout) + { + if (tokenToBeRenewed == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("tokenToBeRenewed"); + } + SecurityToken token = await RenewTokenCoreAsync(tokenToBeRenewed, timeout); + if (token == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.Format(SR.TokenProviderUnableToRenewToken, this))); + } + return token; + } + + public void CancelToken(TimeSpan timeout, SecurityToken token) + { + if (token == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("token"); + } + CancelTokenCore(timeout, token); + } + + public Task CancelTokenAsync(SecurityToken token, TimeSpan timeout) + { + if (token == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("token"); + } + return CancelTokenCoreAsync(token, timeout); + } + + // protected methods + protected abstract SecurityToken GetTokenCore(TimeSpan timeout); + + protected virtual SecurityToken RenewTokenCore(TimeSpan timeout, SecurityToken tokenToBeRenewed) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.Format(SR.TokenRenewalNotSupported, this))); + } + + protected virtual void CancelTokenCore(TimeSpan timeout, SecurityToken token) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.Format(SR.TokenCancellationNotSupported, this))); + } + + protected virtual Task GetTokenCoreAsync(CancellationToken cancellationToken) + { + SecurityToken token = GetToken(Timeout.InfiniteTimeSpan); + return Task.FromResult(token); + } + + protected virtual Task RenewTokenCoreAsync(SecurityToken tokenToBeRenewed, TimeSpan timeout) + { + SecurityToken token = RenewTokenCore(timeout, tokenToBeRenewed); + return Task.FromResult(token); + } + + protected virtual Task CancelTokenCoreAsync(SecurityToken token, TimeSpan timeout) + { + CancelToken(timeout, token); + return Task.CompletedTask; + } + } +} + diff --git a/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/SecurityTokenRequirement.cs b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/SecurityTokenRequirement.cs new file mode 100644 index 000000000..49bc064d5 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/SecurityTokenRequirement.cs @@ -0,0 +1,170 @@ +using CoreWCF.IdentityModel.Tokens; +using CoreWCF; +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoreWCF.IdentityModel.Selectors +{ + internal class SecurityTokenRequirement + { + const string Namespace = "http://schemas.microsoft.com/ws/2006/05/identitymodel/securitytokenrequirement"; + const string tokenTypeProperty = Namespace + "/TokenType"; + const string keyUsageProperty = Namespace + "/KeyUsage"; + const string keyTypeProperty = Namespace + "/KeyType"; + const string keySizeProperty = Namespace + "/KeySize"; + const string requireCryptographicTokenProperty = Namespace + "/RequireCryptographicToken"; + const string peerAuthenticationMode = Namespace + "/PeerAuthenticationMode"; + const string isOptionalTokenProperty = Namespace + "/IsOptionalTokenProperty"; + + const bool defaultRequireCryptographicToken = false; + const SecurityKeyUsage defaultKeyUsage = SecurityKeyUsage.Signature; + const SecurityKeyType defaultKeyType = SecurityKeyType.SymmetricKey; + const int defaultKeySize = 0; + const bool defaultIsOptionalToken = false; + + Dictionary properties; + + public SecurityTokenRequirement() + { + properties = new Dictionary(); + Initialize(); + } + + static public string TokenTypeProperty { get { return tokenTypeProperty; } } + static public string KeyUsageProperty { get { return keyUsageProperty; } } + static public string KeyTypeProperty { get { return keyTypeProperty; } } + static public string KeySizeProperty { get { return keySizeProperty; } } + static public string RequireCryptographicTokenProperty { get { return requireCryptographicTokenProperty; } } + static public string PeerAuthenticationMode { get { return peerAuthenticationMode; } } + static public string IsOptionalTokenProperty { get { return isOptionalTokenProperty; } } + + public string TokenType + { + get + { + string result; + return (TryGetProperty(TokenTypeProperty, out result)) ? result : null; + } + set + { + properties[TokenTypeProperty] = value; + } + } + + internal bool IsOptionalToken + { + get + { + bool result; + return (TryGetProperty(IsOptionalTokenProperty, out result)) ? result : defaultIsOptionalToken; + } + set + { + properties[IsOptionalTokenProperty] = value; + } + } + + public bool RequireCryptographicToken + { + get + { + bool result; + return (TryGetProperty(RequireCryptographicTokenProperty, out result)) ? result : defaultRequireCryptographicToken; + } + set + { + properties[RequireCryptographicTokenProperty] = (object)value; + } + } + + public SecurityKeyUsage KeyUsage + { + get + { + SecurityKeyUsage result; + return (TryGetProperty(KeyUsageProperty, out result)) ? result : defaultKeyUsage; + } + set + { + SecurityKeyUsageHelper.Validate(value); + properties[KeyUsageProperty] = (object)value; + } + } + + public SecurityKeyType KeyType + { + get + { + SecurityKeyType result; + return (TryGetProperty(KeyTypeProperty, out result)) ? result : defaultKeyType; + } + set + { + SecurityKeyTypeHelper.Validate(value); + properties[KeyTypeProperty] = (object)value; + } + } + + public int KeySize + { + get + { + int result; + return (TryGetProperty(KeySizeProperty, out result)) ? result : defaultKeySize; + } + set + { + if (value < 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(value), SR.ValueMustBeNonNegative)); + } + Properties[KeySizeProperty] = value; + } + } + + public IDictionary Properties + { + get + { + return properties; + } + } + + void Initialize() + { + KeyType = defaultKeyType; + KeyUsage = defaultKeyUsage; + RequireCryptographicToken = defaultRequireCryptographicToken; + KeySize = defaultKeySize; + IsOptionalToken = defaultIsOptionalToken; + } + + public TValue GetProperty(string propertyName) + { + TValue result; + if (!TryGetProperty(propertyName, out result)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.Format(SR.SecurityTokenRequirementDoesNotContainProperty, propertyName))); + } + return result; + } + + public bool TryGetProperty(string propertyName, out TValue result) + { + object dictionaryValue; + if (!Properties.TryGetValue(propertyName, out dictionaryValue)) + { + result = default(TValue); + return false; + } + if (dictionaryValue != null && !typeof(TValue).IsAssignableFrom(dictionaryValue.GetType())) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.Format(SR.SecurityTokenRequirementHasInvalidTypeForProperty, propertyName, dictionaryValue.GetType(), typeof(TValue)))); + } + result = (TValue)dictionaryValue; + return true; + } + + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/SecurityTokenResolver.cs b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/SecurityTokenResolver.cs new file mode 100644 index 000000000..1ba4f68ec --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/SecurityTokenResolver.cs @@ -0,0 +1,28 @@ +using CoreWCF.IdentityModel.Tokens; +using CoreWCF; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Text; + +namespace CoreWCF.IdentityModel.Selectors +{ + // TODO: Evaluate removing this class and all usages as the full framework implementation has a lot more functionality. + internal abstract class SecurityTokenResolver + { + private class SimpleTokenResolver : SecurityTokenResolver + { + private ReadOnlyCollection _tokens; + private bool _canMatchLocalId; + + public SimpleTokenResolver(ReadOnlyCollection tokens, bool canMatchLocalId) + { + if (tokens == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(tokens)); + + _tokens = tokens; + _canMatchLocalId = canMatchLocalId; + } + } + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/SecurityTokenSerializer.cs b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/SecurityTokenSerializer.cs new file mode 100644 index 000000000..7e10afa4a --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/SecurityTokenSerializer.cs @@ -0,0 +1,248 @@ +using CoreWCF.IdentityModel.Tokens; +using CoreWCF; +using System; +using System.Collections.Generic; +using System.Text; +using System.Xml; + +namespace CoreWCF.IdentityModel.Selectors +{ + /// + /// SecurityTokenSerializer is responsible for writing and reading SecurityKeyIdentifiers, SecurityKeyIdentifierClauses and SecurityTokens. + /// In order to read SecurityTokens the SecurityTokenSerializer may need to resolve token references using the SecurityTokenResolvers that get passed in. + /// The SecurityTokenSerializer is stateless + /// Exceptions: XmlException, SecurityTokenException, NotSupportedException, InvalidOperationException, ArgumentException + /// + internal abstract class SecurityTokenSerializer + { + // public methods + public bool CanReadToken(XmlReader reader) + { + if (reader == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(reader)); + } + return CanReadTokenCore(reader); + } + + public bool CanWriteToken(SecurityToken token) + { + if (token == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(token)); + } + return CanWriteTokenCore(token); + } + + public bool CanReadKeyIdentifier(XmlReader reader) + { + if (reader == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(reader)); + } + return CanReadKeyIdentifierCore(reader); + } + + public bool CanWriteKeyIdentifier(SecurityKeyIdentifier keyIdentifier) + { + if (keyIdentifier == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(keyIdentifier)); + } + return CanWriteKeyIdentifierCore(keyIdentifier); + } + + public bool CanReadKeyIdentifierClause(XmlReader reader) + { + if (reader == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(reader)); + } + return CanReadKeyIdentifierClauseCore(reader); + } + + public bool CanWriteKeyIdentifierClause(SecurityKeyIdentifierClause keyIdentifierClause) + { + if (keyIdentifierClause == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(keyIdentifierClause)); + } + return CanWriteKeyIdentifierClauseCore(keyIdentifierClause); + } + + + public SecurityToken ReadToken(XmlReader reader, SecurityTokenResolver tokenResolver) + { + if (reader == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(reader)); + } + return ReadTokenCore(reader, tokenResolver); + } + + public void WriteToken(XmlWriter writer, SecurityToken token) + { + if (writer == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(writer)); + } + if (token == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(token)); + } + WriteTokenCore(writer, token); + } + + public SecurityKeyIdentifier ReadKeyIdentifier(XmlReader reader) + { + if (reader == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(reader)); + } + return ReadKeyIdentifierCore(reader); + } + + public void WriteKeyIdentifier(XmlWriter writer, SecurityKeyIdentifier keyIdentifier) + { + if (writer == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(writer)); + } + if (keyIdentifier == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(keyIdentifier)); + } + WriteKeyIdentifierCore(writer, keyIdentifier); + } + + public SecurityKeyIdentifierClause ReadKeyIdentifierClause(XmlReader reader) + { + if (reader == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(reader)); + } + return ReadKeyIdentifierClauseCore(reader); + } + + public void WriteKeyIdentifierClause(XmlWriter writer, SecurityKeyIdentifierClause keyIdentifierClause) + { + if (writer == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(writer)); + } + if (keyIdentifierClause == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(keyIdentifierClause)); + } + WriteKeyIdentifierClauseCore(writer, keyIdentifierClause); + } + + + // protected abstract methods + protected abstract bool CanReadTokenCore(XmlReader reader); + protected abstract bool CanWriteTokenCore(SecurityToken token); + protected abstract bool CanReadKeyIdentifierCore(XmlReader reader); + protected abstract bool CanWriteKeyIdentifierCore(SecurityKeyIdentifier keyIdentifier); + protected abstract bool CanReadKeyIdentifierClauseCore(XmlReader reader); + protected abstract bool CanWriteKeyIdentifierClauseCore(SecurityKeyIdentifierClause keyIdentifierClause); + + protected abstract SecurityToken ReadTokenCore(XmlReader reader, SecurityTokenResolver tokenResolver); + protected abstract void WriteTokenCore(XmlWriter writer, SecurityToken token); + protected abstract SecurityKeyIdentifier ReadKeyIdentifierCore(XmlReader reader); + protected abstract void WriteKeyIdentifierCore(XmlWriter writer, SecurityKeyIdentifier keyIdentifier); + protected abstract SecurityKeyIdentifierClause ReadKeyIdentifierClauseCore(XmlReader reader); + protected abstract void WriteKeyIdentifierClauseCore(XmlWriter writer, SecurityKeyIdentifierClause keyIdentifierClause); + + internal abstract class KeyIdentifierClauseEntry + { + + protected abstract XmlDictionaryString LocalName { get; } + protected abstract XmlDictionaryString NamespaceUri { get; } + + public virtual bool CanReadKeyIdentifierClauseCore(XmlDictionaryReader reader) + { + return reader.IsStartElement(LocalName, NamespaceUri); + } + + public abstract SecurityKeyIdentifierClause ReadKeyIdentifierClauseCore(XmlDictionaryReader reader); + + public abstract bool SupportsCore(SecurityKeyIdentifierClause keyIdentifierClause); + + public abstract void WriteKeyIdentifierClauseCore(XmlDictionaryWriter writer, SecurityKeyIdentifierClause keyIdentifierClause); + } + + internal abstract class StrEntry + { + public abstract string GetTokenTypeUri(); + public abstract Type GetTokenType(SecurityKeyIdentifierClause clause); + public abstract bool CanReadClause(XmlDictionaryReader reader, string tokenType); + public abstract SecurityKeyIdentifierClause ReadClause(XmlDictionaryReader reader, byte[] derivationNonce, int derivationLength, string tokenType); + public abstract bool SupportsCore(SecurityKeyIdentifierClause clause); + public abstract void WriteContent(XmlDictionaryWriter writer, SecurityKeyIdentifierClause clause); + } + + internal abstract class SerializerEntries + { + public virtual void PopulateTokenEntries(IList tokenEntries) { } + public virtual void PopulateKeyIdentifierEntries(IList keyIdentifierEntries) { } + public virtual void PopulateKeyIdentifierClauseEntries(IList keyIdentifierClauseEntries) { } + public virtual void PopulateStrEntries(IList strEntries) { } + } + + internal abstract class KeyIdentifierEntry + { + protected abstract XmlDictionaryString LocalName { get; } + protected abstract XmlDictionaryString NamespaceUri { get; } + + public virtual bool CanReadKeyIdentifierCore(XmlDictionaryReader reader) + { + return reader.IsStartElement(LocalName, NamespaceUri); + } + + public abstract SecurityKeyIdentifier ReadKeyIdentifierCore(XmlDictionaryReader reader); + + public abstract bool SupportsCore(SecurityKeyIdentifier keyIdentifier); + + public abstract void WriteKeyIdentifierCore(XmlDictionaryWriter writer, SecurityKeyIdentifier keyIdentifier); + } + + internal abstract class TokenEntry + { + Type[] tokenTypes = null; + + protected abstract XmlDictionaryString LocalName { get; } + protected abstract XmlDictionaryString NamespaceUri { get; } + public Type TokenType { get { return GetTokenTypes()[0]; } } + public abstract string TokenTypeUri { get; } + protected abstract string ValueTypeUri { get; } + + public bool SupportsCore(Type tokenType) + { + Type[] tokenTypes = GetTokenTypes(); + for (int i = 0; i < tokenTypes.Length; ++i) + { + if (tokenTypes[i].IsAssignableFrom(tokenType)) + return true; + } + return false; + } + + protected abstract Type[] GetTokenTypesCore(); + + public Type[] GetTokenTypes() + { + if (tokenTypes == null) + tokenTypes = GetTokenTypesCore(); + return tokenTypes; + } + + public virtual bool SupportsTokenTypeUri(string tokenTypeUri) + { + return (TokenTypeUri == tokenTypeUri); + } + + } + + } + +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/SecurityTokenVersion.cs b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/SecurityTokenVersion.cs new file mode 100644 index 000000000..806b9cf64 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/SecurityTokenVersion.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Text; + +namespace CoreWCF.IdentityModel.Selectors +{ + public abstract class SecurityTokenVersion + { + public abstract ReadOnlyCollection GetSecuritySpecifications(); + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/UserNamePasswordValidator.cs b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/UserNamePasswordValidator.cs new file mode 100644 index 000000000..b55b620ca --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/UserNamePasswordValidator.cs @@ -0,0 +1,32 @@ +using CoreWCF; +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoreWCF.IdentityModel.Selectors +{ + // TODO: Either consider moving this back to System.IdentityModel.Selectors and/or move to ServiceModel and make async + public abstract class UserNamePasswordValidator + { + static UserNamePasswordValidator none; + + public static UserNamePasswordValidator None + { + get + { + if (none == null) + none = new NoneUserNamePasswordValidator(); + return none; + } + } + + public abstract void Validate(string userName, string password); + + class NoneUserNamePasswordValidator : UserNamePasswordValidator + { + public override void Validate(string userName, string password) + { + } + } + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/UserNameSecurityTokenAuthenticator.cs b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/UserNameSecurityTokenAuthenticator.cs new file mode 100644 index 000000000..6beaadbef --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/UserNameSecurityTokenAuthenticator.cs @@ -0,0 +1,29 @@ +using CoreWCF.IdentityModel.Policy; +using CoreWCF.IdentityModel.Tokens; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Text; + +namespace CoreWCF.IdentityModel.Selectors +{ + internal abstract class UserNameSecurityTokenAuthenticator : SecurityTokenAuthenticator + { + protected UserNameSecurityTokenAuthenticator() + { + } + + protected override bool CanValidateTokenCore(SecurityToken token) + { + return token is UserNameSecurityToken; + } + + protected override ReadOnlyCollection ValidateTokenCore(SecurityToken token) + { + UserNameSecurityToken userNameToken = (UserNameSecurityToken)token; + return ValidateUserNamePasswordCore(userNameToken.UserName, userNameToken.Password); + } + + protected abstract ReadOnlyCollection ValidateUserNamePasswordCore(string userName, string password); + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/WindowsSecurityTokenAuthenticator.cs b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/WindowsSecurityTokenAuthenticator.cs new file mode 100644 index 000000000..a30fced52 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/WindowsSecurityTokenAuthenticator.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Text; +using CoreWCF.IdentityModel.Claims; +using CoreWCF.IdentityModel.Policy; +using CoreWCF.IdentityModel.Tokens; + +namespace CoreWCF.IdentityModel.Selectors +{ + public class WindowsSecurityTokenAuthenticator : SecurityTokenAuthenticator + { + bool includeWindowsGroups; + + public WindowsSecurityTokenAuthenticator() : this(WindowsClaimSet.DefaultIncludeWindowsGroups) + { + } + + public WindowsSecurityTokenAuthenticator(bool includeWindowsGroups) + { + this.includeWindowsGroups = includeWindowsGroups; + } + + protected override bool CanValidateTokenCore(SecurityToken token) + { + return token is WindowsSecurityToken; + } + + protected override ReadOnlyCollection ValidateTokenCore(SecurityToken token) + { + var windowsToken = (WindowsSecurityToken)token; + var claimSet = new WindowsClaimSet(windowsToken.WindowsIdentity, windowsToken.AuthenticationType, this.includeWindowsGroups, windowsToken.ValidTo); + return SecurityUtils.CreateAuthorizationPolicies(claimSet, windowsToken.ValidTo); + } + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/X509CertificateChain.cs b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/X509CertificateChain.cs new file mode 100644 index 000000000..d68f9dfc2 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/X509CertificateChain.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoreWCF.IdentityModel.Selectors +{ + internal class X509CertificateChain + { + public const uint DefaultChainPolicyOID = 0x1; + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/X509CertificateValidator.cs b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/X509CertificateValidator.cs new file mode 100644 index 000000000..d06e6334c --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/X509CertificateValidator.cs @@ -0,0 +1,257 @@ +using CoreWCF.IdentityModel.Tokens; +using CoreWCF; +using System; +using System.Collections.Generic; +using System.Security.Cryptography.X509Certificates; +using System.Text; + +namespace CoreWCF.IdentityModel.Selectors +{ + public abstract class X509CertificateValidator + { + internal const uint CAPI_CERT_CHAIN_POLICY_NT_AUTH = 6; + + static X509CertificateValidator peerTrust; + static X509CertificateValidator chainTrust; + static X509CertificateValidator ntAuthChainTrust; + static X509CertificateValidator peerOrChainTrust; + static X509CertificateValidator none; + + public static X509CertificateValidator None + { + get + { + if (none == null) + none = new NoneX509CertificateValidator(); + return none; + } + } + + public static X509CertificateValidator PeerTrust + { + get + { + if (peerTrust == null) + peerTrust = new PeerTrustValidator(); + return peerTrust; + } + } + + public static X509CertificateValidator ChainTrust + { + get + { + if (chainTrust == null) + chainTrust = new ChainTrustValidator(); + return chainTrust; + } + } + + // TODO: Consider creating platform specific package which contains windows only implementations such as NTAuthChainTruse + internal static X509CertificateValidator NTAuthChainTrust + { + get + { + if (ntAuthChainTrust == null) + ntAuthChainTrust = new ChainTrustValidator(false, null, CAPI_CERT_CHAIN_POLICY_NT_AUTH); + return ntAuthChainTrust; + } + } + + public static X509CertificateValidator PeerOrChainTrust + { + get + { + if (peerOrChainTrust == null) + peerOrChainTrust = new PeerOrChainTrustValidator(); + return peerOrChainTrust; + } + } + + public static X509CertificateValidator CreateChainTrustValidator(bool useMachineContext, X509ChainPolicy chainPolicy) + { + if (chainPolicy == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("chainPolicy"); + return new ChainTrustValidator(useMachineContext, chainPolicy, X509CertificateChain.DefaultChainPolicyOID); + } + + public static X509CertificateValidator CreatePeerOrChainTrustValidator(bool useMachineContext, X509ChainPolicy chainPolicy) + { + if (chainPolicy == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("chainPolicy"); + return new PeerOrChainTrustValidator(useMachineContext, chainPolicy); + } + + public abstract void Validate(X509Certificate2 certificate); + + class NoneX509CertificateValidator : X509CertificateValidator + { + public override void Validate(X509Certificate2 certificate) + { + if (certificate == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("certificate"); + } + } + + class PeerTrustValidator : X509CertificateValidator + { + public override void Validate(X509Certificate2 certificate) + { + if (certificate == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("certificate"); + + Exception exception; + if (!TryValidate(certificate, out exception)) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(exception); + } + + static bool StoreContainsCertificate(StoreName storeName, X509Certificate2 certificate) + { + X509Store store = new X509Store(storeName, StoreLocation.CurrentUser); + X509Certificate2Collection certificates = null; + try + { + store.Open(OpenFlags.ReadOnly); + certificates = store.Certificates.Find(X509FindType.FindByThumbprint, certificate.GetCertHash(), false); + return certificates.Count > 0; + } + finally + { + SecurityUtils.ResetAllCertificates(certificates); + store.Close(); + } + } + + internal bool TryValidate(X509Certificate2 certificate, out Exception exception) + { + // Checklist + // 1) time validity of cert + // 2) in trusted people store + // 3) not in disallowed store + + // The following code could be written as: + // DateTime now = DateTime.UtcNow; + // if (now > certificate.NotAfter.ToUniversalTime() || now < certificate.NotBefore.ToUniversalTime()) + // + // this is because X509Certificate2.xxx doesn't return UT. However this would be a SMALL perf hit. + // I put a DebugAssert so that this will ensure that the we are compatible with the CLR we shipped with + + DateTime now = DateTime.Now; + DiagnosticUtility.DebugAssert(now.Kind == certificate.NotAfter.Kind && now.Kind == certificate.NotBefore.Kind, ""); + + if (now > certificate.NotAfter || now < certificate.NotBefore) + { + exception = new SecurityTokenValidationException(SR.Format(SR.X509InvalidUsageTime, + SecurityUtils.GetCertificateId(certificate), now, certificate.NotBefore, certificate.NotAfter)); + return false; + } + + if (!StoreContainsCertificate(StoreName.TrustedPeople, certificate)) + { + exception = new SecurityTokenValidationException(SR.Format(SR.X509IsNotInTrustedStore, + SecurityUtils.GetCertificateId(certificate))); + return false; + } + + if (StoreContainsCertificate(StoreName.Disallowed, certificate)) + { + exception = new SecurityTokenValidationException(SR.Format(SR.X509IsInUntrustedStore, + SecurityUtils.GetCertificateId(certificate))); + return false; + } + exception = null; + return true; + } + } + + class ChainTrustValidator : X509CertificateValidator + { + bool useMachineContext; + X509ChainPolicy chainPolicy; + uint chainPolicyOID = X509CertificateChain.DefaultChainPolicyOID; + + public ChainTrustValidator() + { + chainPolicy = null; + } + + public ChainTrustValidator(bool useMachineContext, X509ChainPolicy chainPolicy, uint chainPolicyOID) + { + this.useMachineContext = useMachineContext; + this.chainPolicy = chainPolicy; + this.chainPolicyOID = chainPolicyOID; + } + + public override void Validate(X509Certificate2 certificate) + { + if (certificate == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("certificate"); + + X509Chain chain = new X509Chain(); + if (chainPolicy != null) + { + chain.ChainPolicy = chainPolicy; + } + + if (!chain.Build(certificate)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenValidationException(SR.Format(SR.X509ChainBuildFail, + SecurityUtils.GetCertificateId(certificate), GetChainStatusInformation(chain.ChainStatus)))); + } + } + + static string GetChainStatusInformation(X509ChainStatus[] chainStatus) + { + if (chainStatus != null) + { + StringBuilder error = new StringBuilder(128); + for (int i = 0; i < chainStatus.Length; ++i) + { + error.Append(chainStatus[i].StatusInformation); + error.Append(" "); + } + return error.ToString(); + } + return string.Empty; + } + } + + class PeerOrChainTrustValidator : X509CertificateValidator + { + X509CertificateValidator chain; + PeerTrustValidator peer; + + public PeerOrChainTrustValidator() + { + chain = X509CertificateValidator.ChainTrust; + peer = (PeerTrustValidator)X509CertificateValidator.PeerTrust; + } + + public PeerOrChainTrustValidator(bool useMachineContext, X509ChainPolicy chainPolicy) + { + chain = X509CertificateValidator.CreateChainTrustValidator(useMachineContext, chainPolicy); + peer = (PeerTrustValidator)X509CertificateValidator.PeerTrust; + } + + public override void Validate(X509Certificate2 certificate) + { + if (certificate == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("certificate"); + + Exception exception; + if (peer.TryValidate(certificate, out exception)) + return; + + try + { + chain.Validate(certificate); + } + catch (SecurityTokenValidationException ex) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenValidationException(exception.Message + " " + ex.Message)); + } + } + } + } + +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/X509SecurityTokenAuthenticator.cs b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/X509SecurityTokenAuthenticator.cs new file mode 100644 index 000000000..48b2f9829 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/X509SecurityTokenAuthenticator.cs @@ -0,0 +1,99 @@ +using CoreWCF.IdentityModel.Claims; +using CoreWCF.IdentityModel.Policy; +using CoreWCF.IdentityModel.Tokens; +using CoreWCF; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Security.Cryptography.X509Certificates; +using System.Security.Principal; +using System.Text; + +namespace CoreWCF.IdentityModel.Selectors +{ + internal class X509SecurityTokenAuthenticator : SecurityTokenAuthenticator + { + X509CertificateValidator validator; + bool mapToWindows; + bool includeWindowsGroups; + bool cloneHandle; + + public X509SecurityTokenAuthenticator() + : this(X509CertificateValidator.ChainTrust) + { + } + + public X509SecurityTokenAuthenticator(X509CertificateValidator validator) + : this(validator, false) + { + } + + public X509SecurityTokenAuthenticator(X509CertificateValidator validator, bool mapToWindows) + : this(validator, mapToWindows, WindowsClaimSet.DefaultIncludeWindowsGroups) + { + } + + public X509SecurityTokenAuthenticator(X509CertificateValidator validator, bool mapToWindows, bool includeWindowsGroups) + : this(validator, mapToWindows, includeWindowsGroups, true) + { + } + + internal X509SecurityTokenAuthenticator(X509CertificateValidator validator, bool mapToWindows, bool includeWindowsGroups, bool cloneHandle) + { + if (validator == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("validator"); + } + + this.validator = validator; + this.mapToWindows = mapToWindows; + this.includeWindowsGroups = includeWindowsGroups; + this.cloneHandle = cloneHandle; + } + + public bool MapCertificateToWindowsAccount + { + get { return mapToWindows; } + } + + protected override bool CanValidateTokenCore(SecurityToken token) + { + return token is X509SecurityToken; + } + + protected override ReadOnlyCollection ValidateTokenCore(SecurityToken token) + { + X509SecurityToken x509Token = (X509SecurityToken)token; + validator.Validate(x509Token.Certificate); + + X509CertificateClaimSet x509ClaimSet = new X509CertificateClaimSet(x509Token.Certificate, cloneHandle); + if (!mapToWindows) + return SecurityUtils.CreateAuthorizationPolicies(x509ClaimSet, x509Token.ValidTo); + + WindowsClaimSet windowsClaimSet; + if (token is X509WindowsSecurityToken) + { + windowsClaimSet = new WindowsClaimSet(((X509WindowsSecurityToken)token).WindowsIdentity, SecurityUtils.AuthTypeCertMap, includeWindowsGroups, cloneHandle); + } + else + { + throw new PlatformNotSupportedException(); + // Ensure NT_AUTH chain policy for certificate account mapping + //X509CertificateValidator.NTAuthChainTrust.Validate(x509Token.Certificate); + + //WindowsIdentity windowsIdentity = null; + //windowsIdentity = KerberosCertificateLogon(x509Token.Certificate); + + + //windowsClaimSet = new WindowsClaimSet(windowsIdentity, SecurityUtils.AuthTypeCertMap, this.includeWindowsGroups, false); + } + List claimSets = new List(2); + claimSets.Add(windowsClaimSet); + claimSets.Add(x509ClaimSet); + + List policies = new List(1); + policies.Add(new UnconditionalPolicy(claimSets.AsReadOnly(), x509Token.ValidTo)); + return policies.AsReadOnly(); + } + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/X509SecurityTokenProvider.cs b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/X509SecurityTokenProvider.cs new file mode 100644 index 000000000..a224f87de --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Selectors/X509SecurityTokenProvider.cs @@ -0,0 +1,71 @@ +using CoreWCF.IdentityModel.Tokens; +using CoreWCF; +using System; +using System.Collections.Generic; +using System.Security.Cryptography.X509Certificates; +using System.Text; + +namespace CoreWCF.IdentityModel.Selectors +{ + internal class X509SecurityTokenProvider : SecurityTokenProvider, IDisposable + { + X509Certificate2 certificate; + + public X509SecurityTokenProvider(X509Certificate2 certificate) + { + if (certificate == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("certificate"); + } + + this.certificate = new X509Certificate2(certificate); + } + + public X509SecurityTokenProvider(StoreLocation storeLocation, StoreName storeName, X509FindType findType, object findValue) + { + if (findValue == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("findValue"); + } + + X509Store store = new X509Store(storeName, storeLocation); + X509Certificate2Collection certificates = null; + try + { + store.Open(OpenFlags.ReadOnly); + certificates = store.Certificates.Find(findType, findValue, false); + if (certificates.Count < 1) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.Format(SR.CannotFindCert, storeName, storeLocation, findType, findValue))); + } + if (certificates.Count > 1) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.Format(SR.FoundMultipleCerts, storeName, storeLocation, findType, findValue))); + } + + certificate = new X509Certificate2(certificates[0]); + } + finally + { + SecurityUtils.ResetAllCertificates(certificates); + store.Close(); + } + } + + public X509Certificate2 Certificate + { + get { return certificate; } + } + + protected override SecurityToken GetTokenCore(TimeSpan timeout) + { + return new X509SecurityToken(certificate); + } + + public void Dispose() + { + SecurityUtils.ResetCertificate(certificate); + } + } + +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/RsaSecurityToken.cs b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/RsaSecurityToken.cs new file mode 100644 index 000000000..3666c2cdd --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/RsaSecurityToken.cs @@ -0,0 +1,62 @@ +using CoreWCF; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Runtime.InteropServices; +using System.Security.Cryptography; +using System.Text; + +namespace CoreWCF.IdentityModel.Tokens +{ + internal class RsaSecurityToken : SecurityToken + { + string id; + DateTime effectiveTime; + RSA rsa; + + public RsaSecurityToken(RSA rsa) + : this(rsa, SecurityUniqueId.Create().Value) + { + } + + public RsaSecurityToken(RSA rsa, string id) + { + if (rsa == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("rsa"); + if (id == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("id"); + this.rsa = rsa; + this.id = id; + effectiveTime = DateTime.UtcNow; + } + + public override string Id + { + get { return id; } + } + + public override DateTime ValidFrom + { + get { return effectiveTime; } + } + + public override DateTime ValidTo + { + // Never expire + get { return SecurityUtils.MaxUtcDateTime; } + } + + public override ReadOnlyCollection SecurityKeys + { + get + { + throw new PlatformNotSupportedException("RsaSecurityKey"); + } + } + + public RSA Rsa + { + get { return rsa; } + } + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/SecurityAlgorithms.cs b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/SecurityAlgorithms.cs new file mode 100644 index 000000000..b2b5027b5 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/SecurityAlgorithms.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoreWCF.IdentityModel.Tokens +{ + public static class SecurityAlgorithms + { + public const string Aes128Encryption = SecurityAlgorithmStrings.Aes128Encryption; + + public const string Aes128KeyWrap = SecurityAlgorithmStrings.Aes128KeyWrap; + public const string Aes192Encryption = SecurityAlgorithmStrings.Aes192Encryption; + public const string Aes192KeyWrap = SecurityAlgorithmStrings.Aes192KeyWrap; + public const string Aes256Encryption = SecurityAlgorithmStrings.Aes256Encryption; + public const string Aes256KeyWrap = SecurityAlgorithmStrings.Aes256KeyWrap; + public const string DesEncryption = SecurityAlgorithmStrings.DesEncryption; + + public const string DsaSha1Signature = SecurityAlgorithmStrings.DsaSha1Signature; + + public const string ExclusiveC14n = SecurityAlgorithmStrings.ExclusiveC14n; + public const string ExclusiveC14nWithComments = SecurityAlgorithmStrings.ExclusiveC14nWithComments; + public const string HmacSha1Signature = SecurityAlgorithmStrings.HmacSha1Signature; + public const string HmacSha256Signature = SecurityAlgorithmStrings.HmacSha256Signature; + + public const string Psha1KeyDerivation = SecurityAlgorithmStrings.Psha1KeyDerivation; + public const string Psha1KeyDerivationDec2005 = SecurityAlgorithmDec2005Strings.Psha1KeyDerivationDec2005; + + public const string Ripemd160Digest = SecurityAlgorithmStrings.Ripemd160Digest; + public const string RsaOaepKeyWrap = SecurityAlgorithmStrings.RsaOaepKeyWrap; + public const string RsaSha1Signature = SecurityAlgorithmStrings.RsaSha1Signature; + public const string RsaSha256Signature = SecurityAlgorithmStrings.RsaSha256Signature; + public const string RsaV15KeyWrap = SecurityAlgorithmStrings.RsaV15KeyWrap; + + public const string Sha1Digest = SecurityAlgorithmStrings.Sha1Digest; + public const string Sha256Digest = SecurityAlgorithmStrings.Sha256Digest; + public const string Sha512Digest = SecurityAlgorithmStrings.Sha512Digest; + public const string StrTransform = SecurityAlgorithmStrings.StrTransform; + public const string TripleDesEncryption = SecurityAlgorithmStrings.TripleDesEncryption; + public const string TripleDesKeyWrap = SecurityAlgorithmStrings.TripleDesKeyWrap; + + public const string TlsSspiKeyWrap = SecurityAlgorithmStrings.TlsSspiKeyWrap; + public const string WindowsSspiKeyWrap = SecurityAlgorithmStrings.WindowsSspiKeyWrap; + + internal const int DefaultSymmetricKeyLength = 256; + internal const string DefaultEncryptionAlgorithm = Aes256Encryption; + internal const string DefaultAsymmetricKeyWrapAlgorithm = RsaOaepKeyWrap; + internal const string DefaultAsymmetricSignatureAlgorithm = RsaSha256Signature; + internal const string DefaultDigestAlgorithm = Sha256Digest; + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/SecurityKey.cs b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/SecurityKey.cs new file mode 100644 index 000000000..9c545a43c --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/SecurityKey.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoreWCF.IdentityModel.Tokens +{ + public abstract class SecurityKey + { + public abstract int KeySize { get; } + public abstract byte[] DecryptKey(string algorithm, byte[] keyData); + public abstract byte[] EncryptKey(string algorithm, byte[] keyData); + public abstract bool IsAsymmetricAlgorithm(string algorithm); + public abstract bool IsSupportedAlgorithm(string algorithm); + public abstract bool IsSymmetricAlgorithm(string algorithm); + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/SecurityKeyIdentifier.cs b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/SecurityKeyIdentifier.cs new file mode 100644 index 000000000..19758586e --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/SecurityKeyIdentifier.cs @@ -0,0 +1,148 @@ +using CoreWCF; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Text; + +namespace CoreWCF.IdentityModel.Tokens +{ + internal class SecurityKeyIdentifier : IEnumerable + { + const int InitialSize = 2; + readonly List clauses; + bool isReadOnly; + + public SecurityKeyIdentifier() + { + clauses = new List(InitialSize); + } + + public SecurityKeyIdentifier(params SecurityKeyIdentifierClause[] clauses) + { + if (clauses == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("clauses"); + } + this.clauses = new List(clauses.Length); + for (int i = 0; i < clauses.Length; i++) + { + Add(clauses[i]); + } + } + + public SecurityKeyIdentifierClause this[int index] + { + get { return clauses[index]; } + } + + public bool CanCreateKey + { + get + { + for (int i = 0; i < Count; i++) + { + if (this[i].CanCreateKey) + { + return true; + } + } + return false; + } + } + + public int Count + { + get { return clauses.Count; } + } + + public bool IsReadOnly + { + get { return isReadOnly; } + } + + public void Add(SecurityKeyIdentifierClause clause) + { + if (isReadOnly) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.ObjectIsReadOnly)); + } + if (clause == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("clause")); + } + clauses.Add(clause); + } + + public SecurityKey CreateKey() + { + for (int i = 0; i < Count; i++) + { + if (this[i].CanCreateKey) + { + return this[i].CreateKey(); + } + } + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.KeyIdentifierCannotCreateKey)); + } + + public TClause Find() where TClause : SecurityKeyIdentifierClause + { + TClause clause; + if (!TryFind(out clause)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.Format(SR.NoKeyIdentifierClauseFound, typeof(TClause)), "TClause")); + } + return clause; + } + + public IEnumerator GetEnumerator() + { + return clauses.GetEnumerator(); + } + + public void MakeReadOnly() + { + isReadOnly = true; + } + + public override string ToString() + { + using (StringWriter writer = new StringWriter(CultureInfo.InvariantCulture)) + { + writer.WriteLine("SecurityKeyIdentifier"); + writer.WriteLine(" ("); + writer.WriteLine(" IsReadOnly = {0},", IsReadOnly); + writer.WriteLine(" Count = {0}{1}", Count, Count > 0 ? "," : ""); + for (int i = 0; i < Count; i++) + { + writer.WriteLine(" Clause[{0}] = {1}{2}", i, this[i], i < Count - 1 ? "," : ""); + } + writer.WriteLine(" )"); + return writer.ToString(); + } + } + + public bool TryFind(out TClause clause) where TClause : SecurityKeyIdentifierClause + { + for (int i = 0; i < clauses.Count; i++) + { + TClause c = clauses[i] as TClause; + if (c != null) + { + clause = c; + return true; + } + } + clause = null; + return false; + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/SecurityKeyType.cs b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/SecurityKeyType.cs new file mode 100644 index 000000000..59ed2516d --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/SecurityKeyType.cs @@ -0,0 +1,36 @@ +using CoreWCF; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Text; + +namespace CoreWCF.IdentityModel.Tokens +{ + internal enum SecurityKeyType + { + SymmetricKey, + AsymmetricKey, + BearerKey + } + + internal static class SecurityKeyTypeHelper + { + internal static bool IsDefined(SecurityKeyType value) + { + return (value == SecurityKeyType.SymmetricKey + || value == SecurityKeyType.AsymmetricKey + || value == SecurityKeyType.BearerKey); + } + + internal static void Validate(SecurityKeyType value) + { + if (!IsDefined(value)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidEnumArgumentException("value", (int)value, + typeof(SecurityKeyType))); + } + } + + + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/SecurityKeyUsage.cs b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/SecurityKeyUsage.cs new file mode 100644 index 000000000..12b597baa --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/SecurityKeyUsage.cs @@ -0,0 +1,32 @@ +using CoreWCF; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Text; + +namespace CoreWCF.IdentityModel.Tokens +{ + internal enum SecurityKeyUsage + { + Exchange, + Signature + } + + internal static class SecurityKeyUsageHelper + { + internal static bool IsDefined(SecurityKeyUsage value) + { + return (value == SecurityKeyUsage.Exchange + || value == SecurityKeyUsage.Signature); + } + + internal static void Validate(SecurityKeyUsage value) + { + if (!IsDefined(value)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidEnumArgumentException(nameof(value), (int)value, + typeof(SecurityKeyUsage))); + } + } + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/SecurityToken.cs b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/SecurityToken.cs new file mode 100644 index 000000000..5f7da1d0f --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/SecurityToken.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Text; + +namespace CoreWCF.IdentityModel.Tokens +{ + public abstract class SecurityToken + { + public abstract string Id { get; } + public abstract ReadOnlyCollection SecurityKeys { get; } + public abstract DateTime ValidFrom { get; } + public abstract DateTime ValidTo { get; } + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/SecurityTokenException.cs b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/SecurityTokenException.cs new file mode 100644 index 000000000..d3ab2ec02 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/SecurityTokenException.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; +using System.Text; + +namespace CoreWCF.IdentityModel.Tokens +{ + internal class SecurityTokenException : Exception + { + public SecurityTokenException() + : base() + { + } + + public SecurityTokenException(string message) + : base(message) + { + } + + public SecurityTokenException(string message, Exception innerException) + : base(message, innerException) + { + } + + protected SecurityTokenException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/SecurityTokenTypes.cs b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/SecurityTokenTypes.cs new file mode 100644 index 000000000..a789d6c37 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/SecurityTokenTypes.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoreWCF.IdentityModel.Tokens +{ + internal static class SecurityTokenTypes + { + const string Namespace = "http://schemas.microsoft.com/ws/2006/05/identitymodel/tokens"; + const string userName = Namespace + "/UserName"; + const string x509Certificate = Namespace + "/X509Certificate"; + const string kerberos = Namespace + "/Kerberos"; + const string saml = Namespace + "/Saml"; + const string rsa = Namespace + "/Rsa"; + + internal const string SamlTokenProfile11 = "urn:oasis:names:tc:SAML:1.0:assertion"; + internal const string Saml2TokenProfile11 = "urn:oasis:names:tc:SAML:2.0:assertion"; + + internal const string OasisWssSamlTokenProfile11 = "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1"; + internal const string OasisWssSaml2TokenProfile11 = "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0"; + + static public string UserName { get { return userName; } } + static public string X509Certificate { get { return x509Certificate; } } + static public string Kerberos { get { return kerberos; } } + static public string Saml { get { return saml; } } + static public string Rsa { get { return rsa; } } + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/SecurityTokenValidationException.cs b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/SecurityTokenValidationException.cs new file mode 100644 index 000000000..fd55d9bce --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/SecurityTokenValidationException.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; +using System.Text; + +namespace CoreWCF.IdentityModel.Tokens +{ + [Serializable] + internal class SecurityTokenValidationException : SecurityTokenException + { + public SecurityTokenValidationException() + : base() + { + } + + public SecurityTokenValidationException(string message) + : base(message) + { + } + + public SecurityTokenValidationException(string message, Exception innerException) + : base(message, innerException) + { + } + + protected SecurityTokenValidationException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/USerNameSecurityToken.cs b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/USerNameSecurityToken.cs new file mode 100644 index 000000000..4c3dde694 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/USerNameSecurityToken.cs @@ -0,0 +1,66 @@ +using CoreWCF; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Text; + +namespace CoreWCF.IdentityModel.Tokens +{ + internal class UserNameSecurityToken : SecurityToken + { + string id; + string password; + string userName; + DateTime effectiveTime; + + public UserNameSecurityToken(string userName, string password) + : this(userName, password, SecurityUniqueId.Create().Value) + { + } + + public UserNameSecurityToken(string userName, string password, string id) + { + if (userName == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("userName"); + if (userName == string.Empty) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.UserNameCannotBeEmpty); + + this.userName = userName; + this.password = password; + this.id = id; + effectiveTime = DateTime.UtcNow; + } + + public override string Id + { + get { return id; } + } + + public override ReadOnlyCollection SecurityKeys + { + get { return EmptyReadOnlyCollection.Instance; } + } + + public override DateTime ValidFrom + { + get { return effectiveTime; } + } + + public override DateTime ValidTo + { + // Never expire + get { return SecurityUtils.MaxUtcDateTime; } + } + + public string UserName + { + get { return userName; } + } + + public string Password + { + get { return password; } + } + } + +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/WindowsSecurityToken.cs b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/WindowsSecurityToken.cs new file mode 100644 index 000000000..54224dd67 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/WindowsSecurityToken.cs @@ -0,0 +1,95 @@ +using CoreWCF; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Security.Principal; +using System.Text; + +namespace CoreWCF.IdentityModel.Tokens +{ + public class WindowsSecurityToken : SecurityToken, IDisposable + { + private string _id; + private DateTime _effectiveTime; + private DateTime _expirationTime; + private WindowsIdentity _windowsIdentity; + private bool _disposed = false; + + public WindowsSecurityToken(WindowsIdentity windowsIdentity) + : this(windowsIdentity, SecurityUniqueId.Create().Value) + { + } + + public WindowsSecurityToken(WindowsIdentity windowsIdentity, string id) + : this(windowsIdentity, id, null) + { + } + + public WindowsSecurityToken(WindowsIdentity windowsIdentity, string id, string authenticationType) + { + DateTime effectiveTime = DateTime.UtcNow; + Initialize(id, authenticationType, effectiveTime, DateTime.UtcNow.AddHours(10), windowsIdentity, true); + } + + protected WindowsSecurityToken() + { + } + + protected void Initialize(string id, DateTime effectiveTime, DateTime expirationTime, WindowsIdentity windowsIdentity, bool clone) + { + Initialize(id, null, effectiveTime, expirationTime, windowsIdentity, clone); + } + + protected void Initialize(string id, string authenticationType, DateTime effectiveTime, DateTime expirationTime, WindowsIdentity windowsIdentity, bool clone) + { + + if (windowsIdentity == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(windowsIdentity)); + _id = id ?? throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(id)); + AuthenticationType = authenticationType; + _effectiveTime = effectiveTime; + _expirationTime = expirationTime; + _windowsIdentity = clone ? SecurityUtils.CloneWindowsIdentityIfNecessary(windowsIdentity, authenticationType) : windowsIdentity; + } + + public override string Id => _id; + + public string AuthenticationType { get; private set; } + + public override DateTime ValidFrom => _effectiveTime; + + public override DateTime ValidTo => _expirationTime; + + public virtual WindowsIdentity WindowsIdentity + { + get + { + ThrowIfDisposed(); + return _windowsIdentity; + } + } + + public override ReadOnlyCollection SecurityKeys => EmptyReadOnlyCollection.Instance; + + public virtual void Dispose() + { + if (!_disposed) + { + _disposed = true; + if (_windowsIdentity != null) + { + _windowsIdentity.Dispose(); + _windowsIdentity = null; + } + } + } + + protected void ThrowIfDisposed() + { + if (_disposed) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ObjectDisposedException(GetType().FullName)); + } + } + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/X509SecurityToken.cs b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/X509SecurityToken.cs new file mode 100644 index 000000000..fe2bdf3c6 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/X509SecurityToken.cs @@ -0,0 +1,120 @@ +using CoreWCF; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Security.Cryptography.X509Certificates; +using System.Text; + +namespace CoreWCF.IdentityModel.Tokens +{ + internal class X509SecurityToken : SecurityToken, IDisposable + { + string id; + X509Certificate2 certificate; + DateTime effectiveTime = SecurityUtils.MaxUtcDateTime; + DateTime expirationTime = SecurityUtils.MinUtcDateTime; + bool disposed = false; + bool disposable; + + public X509SecurityToken(X509Certificate2 certificate) + : this(certificate, SecurityUniqueId.Create().Value) + { + } + + public X509SecurityToken(X509Certificate2 certificate, string id) + : this(certificate, id, true) + { + } + + internal X509SecurityToken(X509Certificate2 certificate, bool clone) + : this(certificate, SecurityUniqueId.Create().Value, clone) + { + } + + internal X509SecurityToken(X509Certificate2 certificate, bool clone, bool disposable) + : this(certificate, SecurityUniqueId.Create().Value, clone, disposable) + { + } + + internal X509SecurityToken(X509Certificate2 certificate, string id, bool clone) + : this(certificate, id, clone, true) + { + } + + internal X509SecurityToken(X509Certificate2 certificate, string id, bool clone, bool disposable) + { + if (certificate == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("certificate"); + if (id == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("id"); + + this.id = id; + this.certificate = clone ? new X509Certificate2(certificate) : certificate; + // if the cert needs to be cloned then the token owns the clone and should dispose it + this.disposable = clone || disposable; + } + + public override string Id + { + get { return id; } + } + + public override ReadOnlyCollection SecurityKeys + { + get + { + // I believe this is only used in MessageSecurity + throw new PlatformNotSupportedException("X509AsymmetricSecurityKey"); + } + } + + public override DateTime ValidFrom + { + get + { + ThrowIfDisposed(); + if (effectiveTime == SecurityUtils.MaxUtcDateTime) + effectiveTime = certificate.NotBefore.ToUniversalTime(); + return effectiveTime; + } + } + + public override DateTime ValidTo + { + get + { + ThrowIfDisposed(); + if (expirationTime == SecurityUtils.MinUtcDateTime) + expirationTime = certificate.NotAfter.ToUniversalTime(); + return expirationTime; + } + } + + public X509Certificate2 Certificate + { + get + { + ThrowIfDisposed(); + return certificate; + } + } + + public virtual void Dispose() + { + if (disposable && !disposed) + { + disposed = true; + certificate.Reset(); + } + } + + protected void ThrowIfDisposed() + { + if (disposed) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ObjectDisposedException(GetType().FullName)); + } + } + } + +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/X509WindowsSecurityToken.cs b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/X509WindowsSecurityToken.cs new file mode 100644 index 000000000..eb5f30991 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/X509WindowsSecurityToken.cs @@ -0,0 +1,82 @@ +using CoreWCF; +using System; +using System.Collections.Generic; +using System.Security.Cryptography.X509Certificates; +using System.Security.Principal; +using System.Text; + +namespace CoreWCF.IdentityModel.Tokens +{ + internal class X509WindowsSecurityToken : X509SecurityToken + { + WindowsIdentity windowsIdentity; + bool disposed = false; + string authenticationType; + + public X509WindowsSecurityToken(X509Certificate2 certificate, WindowsIdentity windowsIdentity) + : this(certificate, windowsIdentity, null, true) + { + } + + public X509WindowsSecurityToken(X509Certificate2 certificate, WindowsIdentity windowsIdentity, string id) + : this(certificate, windowsIdentity, null, id, true) + { + } + + public X509WindowsSecurityToken(X509Certificate2 certificate, WindowsIdentity windowsIdentity, string authenticationType, string id) + : this(certificate, windowsIdentity, authenticationType, id, true) + { + } + + internal X509WindowsSecurityToken(X509Certificate2 certificate, WindowsIdentity windowsIdentity, string authenticationType, bool clone) + : this(certificate, windowsIdentity, authenticationType, SecurityUniqueId.Create().Value, clone) + { + } + + internal X509WindowsSecurityToken(X509Certificate2 certificate, WindowsIdentity windowsIdentity, string authenticationType, string id, bool clone) + : base(certificate, id, clone) + { + if (windowsIdentity == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("windowsIdentity"); + + this.authenticationType = authenticationType; + this.windowsIdentity = clone ? SecurityUtils.CloneWindowsIdentityIfNecessary(windowsIdentity, authenticationType) : windowsIdentity; + } + + + public WindowsIdentity WindowsIdentity + { + get + { + ThrowIfDisposed(); + return windowsIdentity; + } + } + + public string AuthenticationType + { + get + { + return authenticationType; + } + } + + public override void Dispose() + { + try + { + if (!disposed) + { + disposed = true; + windowsIdentity.Dispose(); + windowsIdentity = null; + } + } + finally + { + base.Dispose(); + } + } + } + +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/XD.cs b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/XD.cs new file mode 100644 index 000000000..46c76aa3c --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/XD.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoreWCF.IdentityModel +{ + static class XD + { + } + + static class SecurityAlgorithmStrings + { + // Main dictionary strings + public const string Aes128Encryption = IdentityModelStringsVersion1.String95; + public const string Aes128KeyWrap = IdentityModelStringsVersion1.String96; + public const string Aes192Encryption = IdentityModelStringsVersion1.String97; + public const string Aes192KeyWrap = IdentityModelStringsVersion1.String98; + public const string Aes256Encryption = IdentityModelStringsVersion1.String99; + public const string Aes256KeyWrap = IdentityModelStringsVersion1.String100; + public const string DesEncryption = IdentityModelStringsVersion1.String101; + public const string DsaSha1Signature = IdentityModelStringsVersion1.String102; + public const string ExclusiveC14n = IdentityModelStringsVersion1.String20; + public const string ExclusiveC14nWithComments = IdentityModelStringsVersion1.String103; + public const string HmacSha1Signature = IdentityModelStringsVersion1.String104; + public const string HmacSha256Signature = IdentityModelStringsVersion1.String105; + public const string Psha1KeyDerivation = IdentityModelStringsVersion1.String106; + public const string Ripemd160Digest = IdentityModelStringsVersion1.String107; + public const string RsaOaepKeyWrap = IdentityModelStringsVersion1.String108; + public const string RsaSha1Signature = IdentityModelStringsVersion1.String109; + public const string RsaSha256Signature = IdentityModelStringsVersion1.String110; + public const string RsaV15KeyWrap = IdentityModelStringsVersion1.String111; + public const string Sha1Digest = IdentityModelStringsVersion1.String112; + public const string Sha256Digest = IdentityModelStringsVersion1.String113; + public const string Sha512Digest = IdentityModelStringsVersion1.String114; + public const string TripleDesEncryption = IdentityModelStringsVersion1.String115; + public const string TripleDesKeyWrap = IdentityModelStringsVersion1.String116; + public const string TlsSspiKeyWrap = IdentityModelStringsVersion1.String117; + public const string WindowsSspiKeyWrap = IdentityModelStringsVersion1.String118; + // String constants + public const string StrTransform = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#STR-Transform"; + } + + static class SecurityAlgorithmDec2005Strings + { + // Main dictionary strings + public const string Psha1KeyDerivationDec2005 = IdentityModelStringsVersion1.String267; + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/InstanceContext.cs b/src/CoreWCF.Primitives/src/CoreWCF/InstanceContext.cs new file mode 100644 index 000000000..ffde49185 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/InstanceContext.cs @@ -0,0 +1,472 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using CoreWCF.Runtime; +using CoreWCF.Channels; +using CoreWCF.Diagnostics; +using CoreWCF.Dispatcher; +using System.Diagnostics; + +namespace CoreWCF +{ + public sealed class InstanceContext : CommunicationObject, IExtensibleObject + { + internal static Action NotifyEmptyCallback = NotifyEmpty; + internal static Action NotifyIdleCallback = NotifyIdle; + + private bool _autoClose; + private InstanceBehavior _behavior; + private ServiceChannelManager _channels; + private ConcurrencyInstanceContextFacet _concurrency; + private ExtensionCollection _extensions; + private readonly ServiceHostBase _host; + //QuotaThrottle quotaThrottle; + //ServiceThrottle serviceThrottle; + private int _instanceContextManagerIndex; + //int instanceContextManagerIndex; + private object _serviceInstanceLock = new object(); + private SynchronizationContext _synchronizationContext; + //TransactionInstanceContextFacet transaction; + private object _userObject; + private bool _wellKnown; + //SynchronizedCollection wmiChannels; + private bool _isUserCreated; + + public InstanceContext(object implementation) + : this(null, implementation) + { + } + + public InstanceContext(ServiceHostBase host, object implementation) + : this(host, implementation, true) + { + } + + internal InstanceContext(ServiceHostBase host, object implementation, bool isUserCreated) + : this(host, implementation, true, isUserCreated) + { + } + + internal InstanceContext(ServiceHostBase host, object implementation, bool wellKnown, bool isUserCreated) + { + _host = host; + if (implementation != null) + { + _userObject = implementation; + _wellKnown = wellKnown; + } + _autoClose = false; + _channels = new ServiceChannelManager(this); + _isUserCreated = isUserCreated; + } + + internal InstanceContext(ServiceHostBase host, bool isUserCreated) + { + // TODO: Why is it required that host != null? + //if (host == null) + //{ + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(host)); + //} + + _host = host; + _autoClose = true; + _channels = new ServiceChannelManager(this, NotifyEmptyCallback); + _isUserCreated = isUserCreated; + } + + internal bool IsUserCreated + { + get { return _isUserCreated; } + set { _isUserCreated = value; } + } + + internal bool IsWellKnown + { + get { return _wellKnown; } + } + + internal bool AutoClose + { + get { return _autoClose; } + set { _autoClose = value; } + } + + internal InstanceBehavior Behavior + { + get { return _behavior; } + set + { + if (_behavior == null) + { + _behavior = value; + } + } + } + + internal ConcurrencyInstanceContextFacet Concurrency + { + get + { + if (_concurrency == null) + { + lock (ThisLock) + { + if (_concurrency == null) + _concurrency = new ConcurrencyInstanceContextFacet(); + } + } + + return _concurrency; + } + } + + protected override TimeSpan DefaultCloseTimeout + { + get + { + if (_host != null) + { + return _host.CloseTimeout; + } + return ServiceDefaults.CloseTimeout; + } + } + + protected override TimeSpan DefaultOpenTimeout + { + get + { + if (_host != null) + { + return _host.OpenTimeout; + } + return ServiceDefaults.OpenTimeout; + } + } + + public IExtensionCollection Extensions + { + get + { + ThrowIfClosed(); + lock (ThisLock) + { + if (_extensions == null) + _extensions = new ExtensionCollection(this, ThisLock); + return _extensions; + } + } + } + + internal ICollection IncomingChannels + { + get + { + ThrowIfClosed(); + return _channels.IncomingChannels; + } + } + + private bool IsBusy + { + get + { + if (State == CommunicationState.Closed) + return false; + return _channels.IsBusy; + } + } + + bool IsSingleton + { + get + { + return ((_behavior != null) && + InstanceContextProviderBase.IsProviderSingleton(_behavior.InstanceContextProvider)); + } + } + + internal ICollection OutgoingChannels + { + get + { + ThrowIfClosed(); + return _channels.OutgoingChannels; + } + } + + internal ServiceHostBase Host + { + get + { + ThrowIfClosed(); + return _host; + } + } + + internal int InstanceContextManagerIndex + { + get { return _instanceContextManagerIndex; } + set { _instanceContextManagerIndex = value; } + } + + public SynchronizationContext SynchronizationContext + { + get { return _synchronizationContext; } + set + { + ThrowIfClosedOrOpened(); + _synchronizationContext = value; + } + } + + internal new object ThisLock + { + get { return base.ThisLock; } + } + + protected override void OnAbort() + { + _channels.Abort(); + Unload(); + } + + internal Task CloseInputAsync(CancellationToken token) + { + return _channels.CloseInputAsync(token); + } + + internal void BindRpc(ref MessageRpc rpc) + { + ThrowIfClosed(); + _channels.IncrementActivityCount(); + rpc.SuccessfullyBoundInstance = true; + } + + internal void BindIncomingChannel(ServiceChannel channel) + { + ThrowIfDisposed(); + + channel.InstanceContext = this; + IChannel proxy = (IChannel)channel.Proxy; + _channels.AddIncomingChannel(proxy); + + // CSDMain 265783: Memory Leak on Chat Stress test scenario + // There's a race condition while on one thread we received a new request from underlying sessionful channel + // and on another thread we just aborted the channel. So the channel will be added to the IncomingChannels list of + // ServiceChannelManager and never get a chance to be removed. + if (proxy != null) + { + CommunicationState state = channel.State; + if (state == CommunicationState.Closing + || state == CommunicationState.Closed + || state == CommunicationState.Faulted) + { + _channels.RemoveChannel(proxy); + } + } + } + + private void CloseIfNotBusy() + { + if (!(State != CommunicationState.Created && State != CommunicationState.Opening)) + { + Fx.Assert( + "InstanceContext.CloseIfNotBusy: (this.State != CommunicationState.Created && this.State != CommunicationState.Opening)"); + } + + if (State != CommunicationState.Opened) + return; + + if (IsBusy) + return; + + if (_behavior.CanUnload(this) == false) + return; + + try + { + // TODO: Make this call and it's chain async + if (State == CommunicationState.Opened) + CloseAsync().GetAwaiter().GetResult(); + } + catch (ObjectDisposedException e) + { + DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); + } + catch (InvalidOperationException e) + { + DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); + } + catch (CommunicationException e) + { + DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); + } + catch (TimeoutException e) + { + //if (TD.CloseTimeoutIsEnabled()) + //{ + // TD.CloseTimeout(e.Message); + //} + DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); + } + } + + internal void FaultInternal() + { + Fault(); + } + + public object GetServiceInstance() + { + return GetServiceInstance(null); + } + + public object GetServiceInstance(Message message) + { + lock (_serviceInstanceLock) + { + ThrowIfClosedOrNotOpen(); + + object current = _userObject; + + if (current != null) + { + return current; + } + + if (_behavior == null) + { + Exception error = new InvalidOperationException(SR.SFxInstanceNotInitialized); + if (message != null) + { + throw TraceUtility.ThrowHelperError(error, message); + } + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(error); + } + + object newUserObject; + if (message != null) + { + newUserObject = _behavior.GetInstance(this, message); + } + else + { + newUserObject = _behavior.GetInstance(this); + } + if (newUserObject != null) + { + SetUserObject(newUserObject); + } + + return newUserObject; + } + } + + private void Load() + { + if (_behavior != null) + { + _behavior.Initialize(this); + } + + // TODO: Re-home InstanceContextManager and have a reference to it here. + //if (_host != null) + //{ + // _host.BindInstance(this); + //} + } + + private static void NotifyEmpty(InstanceContext instanceContext) + { + if (instanceContext._autoClose) + { + instanceContext.CloseIfNotBusy(); + } + } + + private static void NotifyIdle(InstanceContext instanceContext) + { + instanceContext.CloseIfNotBusy(); + } + + protected override async Task OnCloseAsync(CancellationToken token) + { + await _channels.CloseAsync(token); + Unload(); + } + + protected override void OnClosed() + { + base.OnClosed(); + } + + protected override void OnFaulted() + { + base.OnFaulted(); + + if (IsSingleton && (_host != null)) + { + // TODO: Create new mechanism to register fault event handlers + //_host.FaultInternal(); + } + } + + protected override Task OnOpenAsync(CancellationToken token) + { + return Task.CompletedTask; + } + + protected override void OnOpened() + { + base.OnOpened(); + } + + protected override void OnOpening() + { + Load(); + base.OnOpening(); + } + + + internal void ReleaseServiceInstance() + { + ThrowIfDisposedOrNotOpen(); + SetUserObject(null); + } + + private void SetUserObject(object newUserObject) + { + if (_behavior != null && !_wellKnown) + { + object oldUserObject = Interlocked.Exchange(ref _userObject, newUserObject); + + // TODO: Make DisposableInstance accessible via some other mechanism + //if ((oldUserObject != null) && (_host != null) && !Equals(oldUserObject, _host.DisposableInstance)) + //{ + // _behavior.ReleaseInstance(this, oldUserObject); + //} + } + } + + internal void UnbindRpc(ref MessageRpc rpc) + { + if (rpc.InstanceContext == this && rpc.SuccessfullyBoundInstance) + { + _channels.DecrementActivityCount(); + } + } + + void Unload() + { + SetUserObject(null); + + // TODO: Re-home InstanceContextManager and have a reference to it here. + //if (_host != null) + //{ + // _host.UnbindInstance(this); + //} + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/InstanceContextMode.cs b/src/CoreWCF.Primitives/src/CoreWCF/InstanceContextMode.cs new file mode 100644 index 000000000..23b7fb803 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/InstanceContextMode.cs @@ -0,0 +1,20 @@ +namespace CoreWCF +{ + public enum InstanceContextMode + { + PerSession, + PerCall, + Single, + } + + static class InstanceContextModeHelper + { + static public bool IsDefined(InstanceContextMode x) + { + return + x == InstanceContextMode.PerCall || + x == InstanceContextMode.PerSession || + x == InstanceContextMode.Single; + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/InvalidMessageContractException.cs b/src/CoreWCF.Primitives/src/CoreWCF/InvalidMessageContractException.cs new file mode 100644 index 000000000..25864a3e1 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/InvalidMessageContractException.cs @@ -0,0 +1,28 @@ +using System; + +namespace CoreWCF +{ + //[Serializable] + internal class InvalidMessageContractException : Exception //SystemException + { + public InvalidMessageContractException() + : base() + { + } + + public InvalidMessageContractException(string message) + : base(message) + { + } + + public InvalidMessageContractException(string message, Exception innerException) + : base(message, innerException) + { + } + + //protected InvalidMessageContractException(SerializationInfo info, StreamingContext context) + // : base(info, context) + //{ + //} + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/MessageBodyMemberAttribute.cs b/src/CoreWCF.Primitives/src/CoreWCF/MessageBodyMemberAttribute.cs new file mode 100644 index 000000000..b5a1350ea --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/MessageBodyMemberAttribute.cs @@ -0,0 +1,25 @@ +using System; + +namespace CoreWCF +{ + // TODO: Make this public + [AttributeUsage(ServiceModelAttributeTargets.MessageMember, Inherited = false)] + internal class MessageBodyMemberAttribute : MessageContractMemberAttribute + { + int _order = -1; + internal const string OrderPropertyName = "Order"; + public int Order + { + get { return _order; } + set + { + if (value < 0) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(value), value, + SR.ValueMustBeNonNegative)); + + _order = value; + } + } + + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/MessageContractAttribute.cs b/src/CoreWCF.Primitives/src/CoreWCF/MessageContractAttribute.cs new file mode 100644 index 000000000..74b90784a --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/MessageContractAttribute.cs @@ -0,0 +1,74 @@ +using System; +using CoreWCF.Description; + +namespace CoreWCF +{ + [AttributeUsage(ServiceModelAttributeTargets.MessageContract, AllowMultiple = false)] + internal sealed class MessageContractAttribute : Attribute + { + bool isWrapped = true; + string wrappedName; + string wrappedNs; + //ProtectionLevel protectionLevel = ProtectionLevel.None; + //bool hasProtectionLevel = false; + + //internal const string ProtectionLevelPropertyName = "ProtectionLevel"; + //public ProtectionLevel ProtectionLevel + //{ + // get + // { + // return this.protectionLevel; + // } + // set + // { + // if (!ProtectionLevelHelper.IsDefined(value)) + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value")); + // this.protectionLevel = value; + // this.hasProtectionLevel = true; + // } + //} + + //public bool HasProtectionLevel + //{ + // get { return this.hasProtectionLevel; } + //} + + public bool IsWrapped + { + get { return isWrapped; } + set { isWrapped = value; } + } + + public string WrapperName + { + get + { + return wrappedName; + } + set + { + if (value == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value"); + if (value == string.Empty) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(value), + SR.SFxWrapperNameCannotBeEmpty)); + wrappedName = value; + } + } + + public string WrapperNamespace + { + get + { + return wrappedNs; + } + set + { + if (!string.IsNullOrEmpty(value)) + NamingHelper.CheckUriProperty(value, "WrapperNamespace"); + wrappedNs = value; + } + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/MessageContractMemberAttribute.cs b/src/CoreWCF.Primitives/src/CoreWCF/MessageContractMemberAttribute.cs new file mode 100644 index 000000000..75d4dbd15 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/MessageContractMemberAttribute.cs @@ -0,0 +1,89 @@ +using System; +using CoreWCF.Description; + +namespace CoreWCF +{ + // TODO: Make this public + internal abstract class MessageContractMemberAttribute : Attribute + { + string _name; + string _ns; + bool _isNameSetExplicit; + bool _isNamespaceSetExplicit; + //ProtectionLevel protectionLevel = ProtectionLevel.None; + //bool hasProtectionLevel = false; + + internal const string NamespacePropertyName = "Namespace"; + public string Namespace + { + get { return _ns; } + set + { + if (value == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(value)); + } + + if (value.Length > 0) + { + NamingHelper.CheckUriProperty(value, "Namespace"); + } + _ns = value; + _isNamespaceSetExplicit = true; + } + } + + internal bool IsNamespaceSetExplicit + { + get { return _isNamespaceSetExplicit; } + } + + internal const string NamePropertyName = "Name"; + public string Name + { + get { return _name; } + set + { + if (value == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(value)); + } + + if (value == string.Empty) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(value), + SR.SFxNameCannotBeEmpty)); + } + + _name = value; _isNameSetExplicit = true; + } + } + + internal bool IsNameSetExplicit + { + get { return _isNameSetExplicit; } + } + + //internal const string ProtectionLevelPropertyName = "ProtectionLevel"; + //public ProtectionLevel ProtectionLevel + //{ + // get + // { + // return this.protectionLevel; + // } + // set + // { + // if (!ProtectionLevelHelper.IsDefined(value)) + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(value))); + // this.protectionLevel = value; + // this.hasProtectionLevel = true; + // } + //} + + //public bool HasProtectionLevel + //{ + // get { return this.hasProtectionLevel; } + //} + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/MessageHeaderArrayAttribute.cs b/src/CoreWCF.Primitives/src/CoreWCF/MessageHeaderArrayAttribute.cs new file mode 100644 index 000000000..d5e00c994 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/MessageHeaderArrayAttribute.cs @@ -0,0 +1,9 @@ +using System; + +namespace CoreWCF +{ + [AttributeUsage(ServiceModelAttributeTargets.MessageMember, AllowMultiple = false, Inherited = false)] + internal sealed class MessageHeaderArrayAttribute : MessageHeaderAttribute + { + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/MessageHeaderAttribute.cs b/src/CoreWCF.Primitives/src/CoreWCF/MessageHeaderAttribute.cs new file mode 100644 index 000000000..d6b0f4406 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/MessageHeaderAttribute.cs @@ -0,0 +1,43 @@ +using System; + +namespace CoreWCF +{ + // TODO: Investigate making public + [AttributeUsage(ServiceModelAttributeTargets.MessageMember, AllowMultiple = false, Inherited = false)] + internal class MessageHeaderAttribute : MessageContractMemberAttribute + { + bool _mustUnderstand; + bool _isMustUnderstandSet; + bool _relay; + bool _isRelaySet; + string _actor; + + public bool MustUnderstand + { + get { return _mustUnderstand; } + set { _mustUnderstand = value; _isMustUnderstandSet = true; } + } + + public bool Relay + { + get { return _relay; } + set { _relay = value; _isRelaySet = true; } + } + + public string Actor + { + get { return _actor; } + set { _actor = value; } + } + + internal bool IsMustUnderstandSet + { + get { return _isMustUnderstandSet; } + } + + internal bool IsRelaySet + { + get { return _isRelaySet; } + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/MessageHeaderException.cs b/src/CoreWCF.Primitives/src/CoreWCF/MessageHeaderException.cs new file mode 100644 index 000000000..4a61e35bd --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/MessageHeaderException.cs @@ -0,0 +1,70 @@ +using System; +using CoreWCF.Runtime; +using CoreWCF.Channels; + +namespace CoreWCF +{ + //[Serializable] + public class MessageHeaderException : ProtocolException + { + //[NonSerialized] + string headerName; + //[NonSerialized] + string headerNamespace; + //[NonSerialized] + bool isDuplicate; + + public MessageHeaderException(string message) + : this(message, null, null) + { + } + public MessageHeaderException(string message, bool isDuplicate) + : this(message, null, null) + { + } + public MessageHeaderException(string message, Exception innerException) + : this(message, null, null, innerException) + { + } + public MessageHeaderException(string message, string headerName, string ns) + : this(message, headerName, ns, null) + { + } + public MessageHeaderException(string message, string headerName, string ns, bool isDuplicate) + : this(message, headerName, ns, isDuplicate, null) + { + } + public MessageHeaderException(string message, string headerName, string ns, Exception innerException) + : this(message, headerName, ns, false, innerException) + { + } + public MessageHeaderException(string message, string headerName, string ns, bool isDuplicate, Exception innerException) + : base(message, innerException) + { + this.headerName = headerName; + headerNamespace = ns; + this.isDuplicate = isDuplicate; + } + + public string HeaderName { get { return headerName; } } + + public string HeaderNamespace { get { return headerNamespace; } } + + // IsDuplicate==true means there was more than one; IsDuplicate==false means there were zero + public bool IsDuplicate { get { return isDuplicate; } } + + internal Message ProvideFault(MessageVersion messageVersion) + { + Fx.Assert(messageVersion.Addressing == AddressingVersion.WSAddressing10, ""); + WSAddressing10ProblemHeaderQNameFault phf = new WSAddressing10ProblemHeaderQNameFault(this); + Message message = CoreWCF.Channels.Message.CreateMessage(messageVersion, phf, AddressingVersion.WSAddressing10.FaultAction); + phf.AddHeaders(message.Headers); + return message; + } + + // for serialization + public MessageHeaderException() { } + //protected MessageHeaderException(SerializationInfo info, StreamingContext context) : base(info, context) { } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/MessageHeaderT.cs b/src/CoreWCF.Primitives/src/CoreWCF/MessageHeaderT.cs new file mode 100644 index 000000000..1df048d55 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/MessageHeaderT.cs @@ -0,0 +1,184 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Threading; +using CoreWCF.Channels; + +namespace CoreWCF +{ + public class MessageHeader + { + string actor; + bool mustUnderstand; + bool relay; + T content; + + public MessageHeader() + { + } + + public MessageHeader(T content) + : this(content, false, "", false) + { + } + + public MessageHeader(T content, bool mustUnderstand, string actor, bool relay) + { + this.content = content; + this.mustUnderstand = mustUnderstand; + this.actor = actor; + this.relay = relay; + } + + public string Actor + { + get { return actor; } + set { actor = value; } + } + + public T Content + { + get { return content; } + set { content = value; } + } + + public bool MustUnderstand + { + get { return mustUnderstand; } + set { mustUnderstand = value; } + } + + public bool Relay + { + get { return relay; } + set { relay = value; } + } + + internal Type GetGenericArgument() + { + return typeof(T); + } + + public MessageHeader GetUntypedHeader(string name, string ns) + { + return MessageHeader.CreateHeader(name, ns, content, mustUnderstand, actor, relay); + } + } + + // problem: creating / getting content / settings content on a MessageHeader given the type at runtime + // require reflection. + // solution: This class creates a cache of adapters that provide an untyped wrapper over a particular + // MessageHeader instantiation. + // better solution: implement something like "IUntypedTypedHeader" that has a "object Content" property, + // privately implement this on TypedHeader, and then just use that iface to operation on the header (actually + // you'd still have the creation problem...). the issue with that is you now have a new public interface + internal abstract class TypedHeaderManager + { + static Dictionary cache = new Dictionary(); + static ReaderWriterLockSlim cacheLock = new ReaderWriterLockSlim(); + static Type GenericAdapterType = typeof(GenericAdapter<>); + + internal static object Create(Type t, object content, bool mustUnderstand, bool relay, string actor) + { + return GetTypedHeaderManager(t).Create(content, mustUnderstand, relay, actor); + } + + internal static object GetContent(Type t, object typedHeaderInstance, out bool mustUnderstand, out bool relay, out string actor) + { + return GetTypedHeaderManager(t).GetContent(typedHeaderInstance, out mustUnderstand, out relay, out actor); + } + + internal static Type GetMessageHeaderType(Type contentType) + { + return GetTypedHeaderManager(contentType).GetMessageHeaderType(); + } + internal static Type GetHeaderType(Type headerParameterType) + { + if (headerParameterType.GetTypeInfo().IsGenericType && headerParameterType.GetGenericTypeDefinition() == typeof(MessageHeader<>)) + return headerParameterType.GetGenericArguments()[0]; + return headerParameterType; + } + + static TypedHeaderManager GetTypedHeaderManager(Type t) + { + TypedHeaderManager result = null; + + bool readerLockHeld = false; + bool writerLockHeld = false; + try + { + try + { + } + finally + { + cacheLock.TryEnterUpgradeableReadLock(Timeout.Infinite); + readerLockHeld = true; + } + if (!cache.TryGetValue(t, out result)) + { + cacheLock.TryEnterWriteLock(Timeout.Infinite); + writerLockHeld = true; + if (!cache.TryGetValue(t, out result)) + { + result = (TypedHeaderManager) Activator.CreateInstance(GenericAdapterType.MakeGenericType(t)); + cache.Add(t, result); + } + } + } + finally + { + if (writerLockHeld) + { + cacheLock.ExitWriteLock(); + } + if (readerLockHeld) + { + cacheLock.ExitUpgradeableReadLock(); + } + } + + return result; + } + + protected abstract object Create(object content, bool mustUnderstand, bool relay, string actor); + protected abstract object GetContent(object typedHeaderInstance, out bool mustUnderstand, out bool relay, out string actor); + protected abstract Type GetMessageHeaderType(); + + class GenericAdapter : TypedHeaderManager + { + protected override object Create(object content, bool mustUnderstand, bool relay, string actor) + { + MessageHeader header = new MessageHeader(); + header.Content = (T)content; + header.MustUnderstand = mustUnderstand; + header.Relay = relay; + header.Actor = actor; + return header; + } + + protected override object GetContent(object typedHeaderInstance, out bool mustUnderstand, out bool relay, out string actor) + { + mustUnderstand = false; + relay = false; + actor = null; + if (typedHeaderInstance == null) + return null; + + MessageHeader header = typedHeaderInstance as MessageHeader; + if (header == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException("typedHeaderInstance")); + mustUnderstand = header.MustUnderstand; + relay = header.Relay; + actor = header.Actor; + return header.Content; + } + + protected override Type GetMessageHeaderType() + { + return typeof(MessageHeader); + } + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/MessageParameterAttribute.cs b/src/CoreWCF.Primitives/src/CoreWCF/MessageParameterAttribute.cs new file mode 100644 index 000000000..f8dfeb235 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/MessageParameterAttribute.cs @@ -0,0 +1,34 @@ +using System; + +namespace CoreWCF +{ + [AttributeUsage(ServiceModelAttributeTargets.Parameter, Inherited = false)] + internal sealed class MessageParameterAttribute : Attribute + { + string name; + bool isNameSetExplicit; + internal const string NamePropertyName = "Name"; + public string Name + { + get { return name; } + set + { + if (value == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(value)); + } + if (value == string.Empty) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(value), + SR.SFxNameCannotBeEmpty)); + } + name = value; isNameSetExplicit = true; + } + } + + internal bool IsNameSetExplicit + { + get { return isNameSetExplicit; } + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/MustUnderstandSoapException.cs b/src/CoreWCF.Primitives/src/CoreWCF/MustUnderstandSoapException.cs new file mode 100644 index 000000000..ca06a6d61 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/MustUnderstandSoapException.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.ObjectModel; +using System.Globalization; +using System.Runtime.Serialization; +using System.Xml; +using CoreWCF.Channels; + +namespace CoreWCF +{ + //[Serializable] + internal class MustUnderstandSoapException : CommunicationException + { + // for serialization + //public MustUnderstandSoapException() { } + //protected MustUnderstandSoapException(SerializationInfo info, StreamingContext context) : base(info, context) { } + + + Collection notUnderstoodHeaders; + EnvelopeVersion envelopeVersion; + + public MustUnderstandSoapException(Collection notUnderstoodHeaders, EnvelopeVersion envelopeVersion) + { + this.notUnderstoodHeaders = notUnderstoodHeaders; + this.envelopeVersion = envelopeVersion; + } + + public Collection NotUnderstoodHeaders { get { return notUnderstoodHeaders; } } + public EnvelopeVersion EnvelopeVersion { get { return envelopeVersion; } } + + internal Message ProvideFault(MessageVersion messageVersion) + { + string name = notUnderstoodHeaders[0].Name; + string ns = notUnderstoodHeaders[0].Namespace; + FaultCode code = new FaultCode(MessageStrings.MustUnderstandFault, envelopeVersion.Namespace); + FaultReason reason = new FaultReason(SR.Format(SR.SFxHeaderNotUnderstood, name, ns), CultureInfo.CurrentCulture); + MessageFault fault = MessageFault.CreateFault(code, reason); + string faultAction = messageVersion.Addressing.DefaultFaultAction; + Message message = CoreWCF.Channels.Message.CreateMessage(messageVersion, fault, faultAction); + if (envelopeVersion == EnvelopeVersion.Soap12) + { + AddNotUnderstoodHeaders(message.Headers); + } + return message; + } + + void AddNotUnderstoodHeaders(MessageHeaders headers) + { + for (int i = 0; i < notUnderstoodHeaders.Count; ++i) + { + headers.Add(new NotUnderstoodHeader(notUnderstoodHeaders[i].Name, notUnderstoodHeaders[i].Namespace)); + } + } + + class NotUnderstoodHeader : MessageHeader + { + string notUnderstoodName; + string notUnderstoodNs; + + public NotUnderstoodHeader(string name, string ns) + { + notUnderstoodName = name; + notUnderstoodNs = ns; + } + + public override string Name + { + get { return Message12Strings.NotUnderstood; } + } + + public override string Namespace + { + get { return Message12Strings.Namespace; } + } + + protected override void OnWriteStartHeader(XmlDictionaryWriter writer, MessageVersion messageVersion) + { + writer.WriteStartElement(Name, Namespace); + writer.WriteXmlnsAttribute(null, notUnderstoodNs); + writer.WriteStartAttribute(Message12Strings.QName); + writer.WriteQualifiedName(notUnderstoodName, notUnderstoodNs); + writer.WriteEndAttribute(); + } + + protected override void OnWriteHeaderContents(XmlDictionaryWriter writer, MessageVersion messageVersion) + { + // empty + } + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/OperationBehaviorAttribute.cs b/src/CoreWCF.Primitives/src/CoreWCF/OperationBehaviorAttribute.cs new file mode 100644 index 000000000..87aa5b448 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/OperationBehaviorAttribute.cs @@ -0,0 +1,49 @@ +using System; +using CoreWCF.Channels; +using CoreWCF.Description; +using CoreWCF.Dispatcher; + +namespace CoreWCF +{ + public sealed class OperationBehaviorAttribute : Attribute, IOperationBehavior + { + private bool _autoDisposeParameters; + + public bool AutoDisposeParameters + { + get { return _autoDisposeParameters; } + set { _autoDisposeParameters = value; } + } + + void IOperationBehavior.AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters) { } + void IOperationBehavior.Validate(OperationDescription operationDescription) { } + + void IOperationBehavior.ApplyDispatchBehavior(OperationDescription description, DispatchOperation dispatch) + { + if (description == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(description)); + } + if (dispatch == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(dispatch)); + } + //if (description.IsServerInitiated() && this.releaseInstance != ReleaseInstanceMode.None) + //{ + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException( + // SR.Format(SR.SFxOperationBehaviorAttributeReleaseInstanceModeDoesNotApplyToCallback, + // description.Name))); + //} + //dispatch.TransactionRequired = this.autoEnlistTransaction; + //dispatch.TransactionAutoComplete = this.autoCompleteTransaction; + dispatch.AutoDisposeParameters = _autoDisposeParameters; + //dispatch.ReleaseInstanceBeforeCall = (this.releaseInstance & ReleaseInstanceMode.BeforeCall) != 0; + //dispatch.ReleaseInstanceAfterCall = (this.releaseInstance & ReleaseInstanceMode.AfterCall) != 0; + //dispatch.Impersonation = this.Impersonation; + } + + void IOperationBehavior.ApplyClientBehavior(OperationDescription description, ClientOperation proxy) + { + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/OperationContext.cs b/src/CoreWCF.Primitives/src/CoreWCF/OperationContext.cs new file mode 100644 index 000000000..104ca7c66 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/OperationContext.cs @@ -0,0 +1,336 @@ +using System; +using System.Threading; +using CoreWCF.Runtime; +using CoreWCF.Channels; +using CoreWCF.Dispatcher; + +namespace CoreWCF +{ + public sealed class OperationContext : IExtensibleObject + { + private static AsyncLocal currentContext = new AsyncLocal(); + + ServiceChannel channel; + Message clientReply; + bool closeClientReply; + ExtensionCollection extensions; + ServiceHostBase host; + RequestContext requestContext; + Message request; + InstanceContext instanceContext; + bool isServiceReentrant = false; + MessageProperties outgoingMessageProperties; + MessageHeaders outgoingMessageHeaders; + MessageVersion outgoingMessageVersion; + EndpointDispatcher endpointDispatcher; + + public event EventHandler OperationCompleted; + + public OperationContext(IContextChannel channel) + { + if (channel == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(channel)); + + ServiceChannel serviceChannel = channel as ServiceChannel; + + //Could be a TransparentProxy + if (serviceChannel == null) + { + serviceChannel = ServiceChannelFactory.GetServiceChannel(channel); + } + + if (serviceChannel != null) + { + outgoingMessageVersion = serviceChannel.MessageVersion; + this.channel = serviceChannel; + } + else + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.SFxInvalidChannelToOperationContext)); + } + } + + internal OperationContext(ServiceHostBase host) + : this(host, MessageVersion.Soap12WSAddressing10) + { + } + + internal OperationContext(ServiceHostBase host, MessageVersion outgoingMessageVersion) + { + if (outgoingMessageVersion == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(outgoingMessageVersion)); + + this.host = host; + this.outgoingMessageVersion = outgoingMessageVersion; + } + + internal OperationContext(RequestContext requestContext, Message request, ServiceChannel channel, ServiceHostBase host) + { + this.channel = channel; + this.host = host; + this.requestContext = requestContext; + this.request = request; + outgoingMessageVersion = channel.MessageVersion; + } + + // TODO: Probably want to revert this to public + internal IContextChannel Channel + { + get { return GetCallbackChannel(); } + } + + public static OperationContext Current + { + get + { + return CurrentHolder.Context; + } + + set + { + CurrentHolder.Context = value; + } + } + + internal static Holder CurrentHolder + { + get + { + Holder holder = OperationContext.currentContext.Value; + if (holder == null) + { + holder = new Holder(); + OperationContext.currentContext.Value = holder; + } + return holder; + } + } + public EndpointDispatcher EndpointDispatcher + { + get + { + return endpointDispatcher; + } + set + { + endpointDispatcher = value; + } + } + public bool IsUserContext + { + get + { + return (request == null); + } + } + + public IExtensionCollection Extensions + { + get + { + if (extensions == null) + { + extensions = new ExtensionCollection(this); + } + return extensions; + } + } + + internal bool IsServiceReentrant + { + get { return isServiceReentrant; } + set { isServiceReentrant = value; } + } + + internal Message IncomingMessage + { + get { return clientReply ?? request; } + } + + internal ServiceChannel InternalServiceChannel + { + get { return channel; } + set { channel = value; } + } + + internal bool HasOutgoingMessageHeaders + { + get { return (outgoingMessageHeaders != null); } + } + + public MessageHeaders OutgoingMessageHeaders + { + get + { + if (outgoingMessageHeaders == null) + outgoingMessageHeaders = new MessageHeaders(OutgoingMessageVersion); + + return outgoingMessageHeaders; + } + } + + internal bool HasOutgoingMessageProperties + { + get { return (outgoingMessageProperties != null); } + } + + public MessageProperties OutgoingMessageProperties + { + get + { + if (outgoingMessageProperties == null) + outgoingMessageProperties = new MessageProperties(); + + return outgoingMessageProperties; + } + } + + internal MessageVersion OutgoingMessageVersion + { + get { return outgoingMessageVersion; } + } + + public MessageHeaders IncomingMessageHeaders + { + get + { + Message message = clientReply ?? request; + if (message != null) + return message.Headers; + else + return null; + } + } + + public MessageProperties IncomingMessageProperties + { + get + { + Message message = clientReply ?? request; + if (message != null) + return message.Properties; + else + return null; + } + } + + public MessageVersion IncomingMessageVersion + { + get + { + Message message = clientReply ?? request; + if (message != null) + return message.Version; + else + return null; + } + } + + public InstanceContext InstanceContext + { + get { return instanceContext; } + } + + public RequestContext RequestContext + { + get { return requestContext; } + set { requestContext = value; } + } + + internal void ClearClientReplyNoThrow() + { + clientReply = null; + } + + internal void FireOperationCompleted() + { + try + { + EventHandler handler = OperationCompleted; + + if (handler != null) + { + handler(this, EventArgs.Empty); + } + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + throw; + + throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(e); + } + } + + public T GetCallbackChannel() + { + if (channel == null || IsUserContext) + return default(T); + + // yes, we might throw InvalidCastException here. Is it really + // better to check and throw something else instead? + return (T)channel.Proxy; + } + + internal void ReInit(RequestContext requestContext, Message request, ServiceChannel channel) + { + this.requestContext = requestContext; + this.request = request; + this.channel = channel; + } + + internal void Recycle() + { + requestContext = null; + request = null; + extensions = null; + instanceContext = null; + SetClientReply(null, false); + } + + internal void SetClientReply(Message message, bool closeMessage) + { + Message oldClientReply = null; + + if (!object.Equals(message, clientReply)) + { + if (closeClientReply && (clientReply != null)) + { + oldClientReply = clientReply; + } + + clientReply = message; + } + + closeClientReply = closeMessage; + + if (oldClientReply != null) + { + oldClientReply.Close(); + } + } + + internal void SetInstanceContext(InstanceContext instanceContext) + { + this.instanceContext = instanceContext; + } + + internal class Holder + { + OperationContext context; + + public OperationContext Context + { + get + { + return context; + } + + set + { + context = value; + } + } + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/OperationContractAttribute.cs b/src/CoreWCF.Primitives/src/CoreWCF/OperationContractAttribute.cs new file mode 100644 index 000000000..4cc31c439 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/OperationContractAttribute.cs @@ -0,0 +1,100 @@ +using System; +using System.Reflection; +using CoreWCF.Description; + +namespace CoreWCF +{ + [AttributeUsage(ServiceModelAttributeTargets.OperationContract)] + public sealed class OperationContractAttribute : Attribute + { + string _name; + string _action; + string _replyAction; + bool _asyncPattern; + bool _isOneWay; + + public string Name + { + get { return _name; } + set + { + if (value == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(value)); + } + if (value == "") + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(value), + SR.SFxNameCannotBeEmpty)); + } + + _name = value; + } + } + + internal const string ActionPropertyName = "Action"; + public string Action + { + get { return _action; } + set + { + if (value == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(value)); + } + + _action = value; + } + } + + internal const string ReplyActionPropertyName = "ReplyAction"; + public string ReplyAction + { + get { return _replyAction; } + set + { + if (value == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(value)); + } + + _replyAction = value; + } + } + + public bool AsyncPattern + { + get { return _asyncPattern; } + set { _asyncPattern = value; } + } + + public bool IsOneWay + { + get { return _isOneWay; } + set { _isOneWay = value; } + } + + internal bool IsSessionOpenNotificationEnabled + { + get + { + return Action == OperationDescription.SessionOpenedAction; + } + } + + internal void EnsureInvariants(MethodInfo methodInfo, string operationName) + { + // This code is used for WebSockets open notification + //if (IsSessionOpenNotificationEnabled) + //{ + // if (!IsOneWay + // || !this.IsInitiating + // || methodInfo.GetParameters().Length > 0) + // { + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException( + // SR.Format(SR.ContractIsNotSelfConsistentWhenIsSessionOpenNotificationEnabled, operationName, "Action", OperationDescription.SessionOpenedAction, "IsOneWay", "IsInitiating"))); + // } + //} + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/OperationFormatStyle.cs b/src/CoreWCF.Primitives/src/CoreWCF/OperationFormatStyle.cs new file mode 100644 index 000000000..9d8742117 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/OperationFormatStyle.cs @@ -0,0 +1,19 @@ +namespace CoreWCF +{ + internal enum OperationFormatStyle + { + Document, + Rpc, + } + + static class OperationFormatStyleHelper + { + static public bool IsDefined(OperationFormatStyle x) + { + return + x == OperationFormatStyle.Document || + x == OperationFormatStyle.Rpc || + false; + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/OperationFormatUse.cs b/src/CoreWCF.Primitives/src/CoreWCF/OperationFormatUse.cs new file mode 100644 index 000000000..d2ed3cc02 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/OperationFormatUse.cs @@ -0,0 +1,19 @@ +namespace CoreWCF +{ + internal enum OperationFormatUse + { + Literal, + Encoded, + } + + static class OperationFormatUseHelper + { + static public bool IsDefined(OperationFormatUse x) + { + return + x == OperationFormatUse.Literal || + x == OperationFormatUse.Encoded || + false; + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Pool.cs b/src/CoreWCF.Primitives/src/CoreWCF/Pool.cs new file mode 100644 index 000000000..ef28077bc --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Pool.cs @@ -0,0 +1,52 @@ +namespace CoreWCF +{ + class Pool where T : class + { + T[] items; + int count; + + public Pool(int maxCount) + { + items = new T[maxCount]; + } + + public int Count + { + get { return count; } + } + + public T Take() + { + if (count > 0) + { + T item = items[--count]; + items[count] = null; + return item; + } + else + { + return null; + } + } + + public bool Return(T item) + { + if (count < items.Length) + { + items[count++] = item; + return true; + } + else + { + return false; + } + } + + public void Clear() + { + for (int i = 0; i < count; i++) + items[i] = null; + count = 0; + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/ProtocolException.cs b/src/CoreWCF.Primitives/src/CoreWCF/ProtocolException.cs new file mode 100644 index 000000000..1f9df88a2 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/ProtocolException.cs @@ -0,0 +1,40 @@ +using System; +using System.Globalization; +using System.Runtime.Serialization; +using CoreWCF.Channels; + +namespace CoreWCF +{ + [Serializable] + public class ProtocolException : CommunicationException + { + public ProtocolException() { } + public ProtocolException(string message) : base(message) { } + public ProtocolException(string message, Exception innerException) : base(message, innerException) { } + protected ProtocolException(SerializationInfo info, StreamingContext context) : base(info, context) { } + + internal static ProtocolException ReceiveShutdownReturnedNonNull(Message message) + { + if (message.IsFault) + { + try + { + MessageFault fault = MessageFault.CreateFault(message, 64 * 1024); + FaultReasonText reason = fault.Reason.GetMatchingTranslation(CultureInfo.CurrentCulture); + string text = SR.Format(SR.ReceiveShutdownReturnedFault, reason.Text); + return new ProtocolException(text); + } + catch (QuotaExceededException) + { + string text = SR.Format(SR.ReceiveShutdownReturnedLargeFault, message.Headers.Action); + return new ProtocolException(text); + } + } + else + { + string text = SR.Format(SR.ReceiveShutdownReturnedMessage, message.Headers.Action); + return new ProtocolException(text); + } + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/QuotaExceededException.cs b/src/CoreWCF.Primitives/src/CoreWCF/QuotaExceededException.cs new file mode 100644 index 000000000..12025123e --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/QuotaExceededException.cs @@ -0,0 +1,23 @@ +using System; +using System.Runtime.Serialization; + +namespace CoreWCF +{ + public class QuotaExceededException : Exception //SystemException + { + public QuotaExceededException() + : base() + { + } + + public QuotaExceededException(string message) + : base(message) + { + } + + public QuotaExceededException(string message, Exception innerException) + : base(message, innerException) + { + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Runtime/ActionItem.cs b/src/CoreWCF.Primitives/src/CoreWCF/Runtime/ActionItem.cs new file mode 100644 index 000000000..cb563789d --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Runtime/ActionItem.cs @@ -0,0 +1,331 @@ +using System; +using System.Security; +using System.Threading; +using CoreWCF.Runtime.Diagnostics; +using System.Threading.Tasks; +using System.Collections.Generic; +using System.Collections.Concurrent; + +namespace CoreWCF.Runtime +{ + // TODO: Make internal again. I had to expose this for cross assembly usage + public abstract class ActionItem + { + //SecurityContext context; + bool isScheduled; + + bool lowPriority; + + protected ActionItem() + { + } + + public bool LowPriority + { + get + { + return lowPriority; + } + protected set + { + lowPriority = value; + } + } + + public static void Schedule(Action callback, object state) + { + Schedule(callback, state, false); + } + + public static void Schedule(Action callback, object state, bool lowPriority) + { + Fx.Assert(callback != null, "A null callback was passed for Schedule!"); + + //if (ActionActionItem.ShouldUseActivity || + // Fx.Trace.IsEnd2EndActivityTracingEnabled) + //{ + // new DefaultActionItem(callback, state, lowPriority).Schedule(); + //} + //else + //{ + ScheduleCallback(callback, state, lowPriority); + //} + } + + + protected abstract void Invoke(); + + protected void Schedule() + { + if (isScheduled) + { + throw Fx.Exception.AsError(new InvalidOperationException(SR.ActionItemIsAlreadyScheduled)); + } + + isScheduled = true; + //if (this.context != null) + //{ + // ScheduleCallback(CallbackHelper.InvokeWithContextCallback); + //} + //else + //{ + ScheduleCallback(CallbackHelper.InvokeWithoutContextCallback); + //} + } + + //protected void ScheduleWithContext(SecurityContext context) + //{ + // if (context == null) + // { + // throw Fx.Exception.ArgumentNull("context"); + // } + // if (isScheduled) + // { + // throw Fx.Exception.AsError(new InvalidOperationException(InternalSR.ActionItemIsAlreadyScheduled)); + // } + + // this.isScheduled = true; + // this.context = context.CreateCopy(); + // ScheduleCallback(CallbackHelper.InvokeWithContextCallback); + //} + + protected void ScheduleWithoutContext() + { + if (isScheduled) + { + throw Fx.Exception.AsError(new InvalidOperationException(SR.ActionItemIsAlreadyScheduled)); + } + + isScheduled = true; + ScheduleCallback(CallbackHelper.InvokeWithoutContextCallback); + } + + static void ScheduleCallback(Action callback, object state, bool lowPriority) + { + Fx.Assert(callback != null, "Cannot schedule a null callback"); + if (lowPriority) + { + IOThreadScheduler.ScheduleCallbackLowPriNoFlow(callback, state); + } + else + { + IOThreadScheduler.ScheduleCallbackNoFlow(callback, state); + } + } + + //SecurityContext ExtractContext() + //{ + // Fx.Assert(this.context != null, "Cannot bind to a null context; context should have been set by now"); + // Fx.Assert(this.isScheduled, "Context is extracted only while the object is scheduled"); + // SecurityContext result = this.context; + // this.context = null; + // return result; + //} + + void ScheduleCallback(Action callback) + { + ScheduleCallback(callback, this, lowPriority); + } + + static class CallbackHelper + { + static Action invokeWithContextCallback; + static Action invokeWithoutContextCallback; + static ContextCallback onContextAppliedCallback; + + public static Action InvokeWithContextCallback + { + get + { + if (invokeWithContextCallback == null) + { + invokeWithContextCallback = InvokeWithContext; + } + return invokeWithContextCallback; + } + } + + public static Action InvokeWithoutContextCallback + { + get + { + if (invokeWithoutContextCallback == null) + { + invokeWithoutContextCallback = InvokeWithoutContext; + } + return invokeWithoutContextCallback; + } + } + + public static ContextCallback OnContextAppliedCallback + { + get + { + if (onContextAppliedCallback == null) + { + onContextAppliedCallback = new ContextCallback(OnContextApplied); + } + return onContextAppliedCallback; + } + } + + static void InvokeWithContext(object state) + { + throw new PlatformNotSupportedException(); + //SecurityContext context = ((ActionItem)state).ExtractContext(); + //SecurityContext.Run(context, OnContextAppliedCallback, state); + } + + static void InvokeWithoutContext(object state) + { + ((ActionItem)state).Invoke(); + ((ActionItem)state).isScheduled = false; + } + + static void OnContextApplied(object o) + { + ((ActionItem)o).Invoke(); + ((ActionItem)o).isScheduled = false; + } + } + + class DefaultActionItem : ActionItem + { + + Action callback; + object state; + + //bool flowLegacyActivityId; + //Guid activityId; + //EventTraceActivity eventTraceActivity; + + public DefaultActionItem(Action callback, object state, bool isLowPriority) + { + Fx.Assert(callback != null, "Shouldn't instantiate an object to wrap a null callback"); + base.LowPriority = isLowPriority; + this.callback = callback; + this.state = state; + //if (ActionActionItem.ShouldUseActivity) + //{ + // this.flowLegacyActivityId = true; + // this.activityId = EtwDiagnosticTrace.ActivityId; + //} + //if (Fx.Trace.IsEnd2EndActivityTracingEnabled) + //{ + // this.eventTraceActivity = EventTraceActivity.GetFromThreadOrCreate(); + // if (TraceCore.ActionItemScheduledIsEnabled(Fx.Trace)) + // { + // TraceCore.ActionItemScheduled(Fx.Trace, this.eventTraceActivity); + // } + //} + + } + + protected override void Invoke() + { + //if (this.flowLegacyActivityId || Fx.Trace.IsEnd2EndActivityTracingEnabled) + //{ + // TraceAndInvoke(); + //} + //else + //{ + callback(state); + //} + } + + void TraceAndInvoke() + { + //TODO: Consider merging these since they go through the same codepath. + //if (this.flowLegacyActivityId) + //{ + // Guid currentActivityId = EtwDiagnosticTrace.ActivityId; + // try + // { + // EtwDiagnosticTrace.ActivityId = this.activityId; + // this.callback(this.state); + // } + // finally + // { + // EtwDiagnosticTrace.ActivityId = currentActivityId; + // } + //} + //else + //{ + Guid previous = Guid.Empty; + bool restoreActivityId = false; + try + { + //if (this.eventTraceActivity != null) + //{ + // previous = Trace.CorrelationManager.ActivityId; + // restoreActivityId = true; + // Trace.CorrelationManager.ActivityId = this.eventTraceActivity.ActivityId; + // if (TraceCore.ActionItemCallbackInvokedIsEnabled(Fx.Trace)) + // { + // TraceCore.ActionItemCallbackInvoked(Fx.Trace, this.eventTraceActivity); + // } + //} + callback(state); + } + finally + { + if (restoreActivityId) + { + //Trace.CorrelationManager.ActivityId = previous; + } + } + //} + } + } + + public static TaskScheduler IOTaskScheduler = new IOThreadTaskScheduler(); + + internal class IOThreadTaskScheduler : TaskScheduler + { + [ThreadStatic] + private static bool _onSchedulerThread; + + internal IOThreadTaskScheduler() + { + _tasks = new ConcurrentQueue(); + } + + // The queue of tasks to execute, maintained for debugging purposes + // An alternative implementation would be to pass the Task directly + // to the OnTaskQueued method. Using an intermediate queue might cause + // a performance bottleneck. Profiling will be needed to determine. + // Unless this is discovered to be a problem, using an intermediate + // queue to aid in debugging. + private readonly ConcurrentQueue _tasks; + + private static void OnTaskQueued(object obj) + { + var thisPtr = obj as IOThreadTaskScheduler; + Task nextTask; + if (thisPtr._tasks.TryDequeue(out nextTask)) + { + _onSchedulerThread = true; + thisPtr.TryExecuteTask(nextTask); + _onSchedulerThread = false; + } + } + + protected override IEnumerable GetScheduledTasks() + { + return _tasks; + } + + protected override void QueueTask(Task task) + { + _tasks.Enqueue(task); + Schedule(OnTaskQueued, this); + } + + protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) + { + return _onSchedulerThread && TryExecuteTask(task); + } + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Runtime/AsyncManualResetEvent.cs b/src/CoreWCF.Primitives/src/CoreWCF/Runtime/AsyncManualResetEvent.cs new file mode 100644 index 000000000..54bce85c6 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Runtime/AsyncManualResetEvent.cs @@ -0,0 +1,86 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace CoreWCF.Runtime +{ + // This class is based on the blog post https://blogs.msdn.microsoft.com/pfxteam/2012/02/11/building-async-coordination-primitives-part-1-asyncmanualresetevent/ + internal class AsyncManualResetEvent : IDisposable + { + private TaskCompletionSource _tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + + public Task WaitAsync() + { + CheckDisposed(); + return _tcs.Task; + } + + public async Task WaitAsync(CancellationToken token) + { + CheckDisposed(); + + if (!token.CanBeCanceled) + { + await WaitAsync(); + return true; + } + + if (token.IsCancellationRequested) + { + return false; + } + + var localTcs = new TaskCompletionSource(); + using (token.Register(TokenCancelledCallback, localTcs)) + { + var tcs = _tcs; + CheckDisposed(); + return await await Task.WhenAny(localTcs.Task, tcs.Task); + } + + } + + private static void TokenCancelledCallback(object obj) + { + var localTcs = obj as TaskCompletionSource; + localTcs?.TrySetResult(false); + } + + public void Set() + { + CheckDisposed(); + _tcs?.TrySetResult(true); + } + + public void Reset() + { + CheckDisposed(); + while (true) + { + var tcs = _tcs; + if (tcs == null) + { + return; // Disposed + } + + if (!tcs.Task.IsCompleted || + Interlocked.CompareExchange(ref _tcs, new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously), tcs) == tcs) + return; + } + } + + private void CheckDisposed() + { + if (_tcs == null) + { + throw new ObjectDisposedException(nameof(AsyncManualResetEvent)); + } + } + + public void Dispose() + { + var tcs = Interlocked.Exchange(ref _tcs, null); + tcs?.TrySetResult(false); + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Runtime/BackoffTimeoutHelper.cs b/src/CoreWCF.Primitives/src/CoreWCF/Runtime/BackoffTimeoutHelper.cs new file mode 100644 index 000000000..f79c3fdcf --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Runtime/BackoffTimeoutHelper.cs @@ -0,0 +1,134 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; + +namespace CoreWCF.Runtime +{ + internal sealed class BackoffTimeoutHelper + { + readonly static int maxSkewMilliseconds = 15; + readonly static long maxDriftTicks = maxSkewMilliseconds * 2 * TimeSpan.TicksPerMillisecond; + readonly static TimeSpan defaultInitialWaitTime = TimeSpan.FromMilliseconds(1); + readonly static TimeSpan defaultMaxWaitTime = TimeSpan.FromMinutes(1); + + DateTime deadline; + TimeSpan maxWaitTime; + TimeSpan waitTime; + IOThreadTimer backoffTimer; + Action backoffCallback; + object backoffState; + Random random; + TimeSpan originalTimeout; + + internal BackoffTimeoutHelper(TimeSpan timeout) + : this(timeout, BackoffTimeoutHelper.defaultMaxWaitTime) + { + } + + internal BackoffTimeoutHelper(TimeSpan timeout, TimeSpan maxWaitTime) + : this(timeout, maxWaitTime, BackoffTimeoutHelper.defaultInitialWaitTime) + { + } + + internal BackoffTimeoutHelper(TimeSpan timeout, TimeSpan maxWaitTime, TimeSpan initialWaitTime) + { + random = new Random(GetHashCode()); + this.maxWaitTime = maxWaitTime; + originalTimeout = timeout; + Reset(timeout, initialWaitTime); + } + + public TimeSpan OriginalTimeout + { + get + { + return originalTimeout; + } + } + + void Reset(TimeSpan timeout, TimeSpan initialWaitTime) + { + if (timeout == TimeSpan.MaxValue) + { + deadline = DateTime.MaxValue; + } + else + { + deadline = DateTime.UtcNow + timeout; + } + waitTime = initialWaitTime; + } + + public bool IsExpired() + { + if (deadline == DateTime.MaxValue) + { + return false; + } + else + { + return (DateTime.UtcNow >= deadline); + } + } + + public void WaitAndBackoff(Action callback, object state) + { + if (backoffCallback != callback || backoffState != state) + { + if (backoffTimer != null) + { + backoffTimer.Cancel(); + } + backoffCallback = callback; + backoffState = state; + backoffTimer = new IOThreadTimer(callback, state, false, BackoffTimeoutHelper.maxSkewMilliseconds); + } + + TimeSpan backoffTime = WaitTimeWithDrift(); + Backoff(); + backoffTimer.Set(backoffTime); + } + + // TODO: Consider making Async + public void WaitAndBackoff() + { + Thread.Sleep(WaitTimeWithDrift()); + Backoff(); + } + + TimeSpan WaitTimeWithDrift() + { + return Ticks.ToTimeSpan(Math.Max( + Ticks.FromTimeSpan(BackoffTimeoutHelper.defaultInitialWaitTime), + Ticks.Add(Ticks.FromTimeSpan(waitTime), + (long)(uint)random.Next() % (2 * BackoffTimeoutHelper.maxDriftTicks + 1) - BackoffTimeoutHelper.maxDriftTicks))); + } + + void Backoff() + { + if (waitTime.Ticks >= (maxWaitTime.Ticks / 2)) + { + waitTime = maxWaitTime; + } + else + { + waitTime = TimeSpan.FromTicks(waitTime.Ticks * 2); + } + + if (deadline != DateTime.MaxValue) + { + TimeSpan remainingTime = deadline - DateTime.UtcNow; + if (waitTime > remainingTime) + { + waitTime = remainingTime; + if (waitTime < TimeSpan.Zero) + { + waitTime = TimeSpan.Zero; + } + } + } + } + } + +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Runtime/BufferedOutputStream.cs b/src/CoreWCF.Primitives/src/CoreWCF/Runtime/BufferedOutputStream.cs new file mode 100644 index 000000000..64434724e --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Runtime/BufferedOutputStream.cs @@ -0,0 +1,324 @@ +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace CoreWCF.Runtime +{ + internal class BufferedOutputStream : Stream + { + InternalBufferManager bufferManager; + + byte[][] chunks; + + int chunkCount; + byte[] currentChunk; + int currentChunkSize; + int maxSize; + int maxSizeQuota; + int totalSize; + bool callerReturnsBuffer; + bool bufferReturned; + bool initialized; + + // requires an explicit call to Init() by the caller + public BufferedOutputStream() + { + chunks = new byte[4][]; + } + + public BufferedOutputStream(int initialSize, int maxSize, InternalBufferManager bufferManager) + : this() + { + Reinitialize(initialSize, maxSize, bufferManager); + } + + public BufferedOutputStream(int maxSize) + : this(0, maxSize, InternalBufferManager.Create(0, int.MaxValue)) + { + } + + public override bool CanRead + { + get + { + return false; + } + } + + public override bool CanSeek + { + get + { + return false; + } + } + + public override bool CanWrite + { + get + { + return true; + } + } + + public override long Length + { + get + { + return totalSize; + } + } + + public override long Position + { + get + { + throw Fx.Exception.AsError(new NotSupportedException(SR.SeekNotSupported)); + } + set + { + throw Fx.Exception.AsError(new NotSupportedException(SR.SeekNotSupported)); + } + } + + public void Reinitialize(int initialSize, int maxSizeQuota, InternalBufferManager bufferManager) + { + Reinitialize(initialSize, maxSizeQuota, maxSizeQuota, bufferManager); + } + + public void Reinitialize(int initialSize, int maxSizeQuota, int effectiveMaxSize, InternalBufferManager bufferManager) + { + Fx.Assert(!initialized, "Clear must be called before re-initializing stream"); + this.maxSizeQuota = maxSizeQuota; + maxSize = effectiveMaxSize; + this.bufferManager = bufferManager; + currentChunk = bufferManager.TakeBuffer(initialSize); + currentChunkSize = 0; + totalSize = 0; + chunkCount = 1; + chunks[0] = currentChunk; + initialized = true; + } + + void AllocNextChunk(int minimumChunkSize) + { + int newChunkSize; + if (currentChunk.Length > (int.MaxValue / 2)) + { + newChunkSize = int.MaxValue; + } + else + { + newChunkSize = currentChunk.Length * 2; + } + if (minimumChunkSize > newChunkSize) + { + newChunkSize = minimumChunkSize; + } + byte[] newChunk = bufferManager.TakeBuffer(newChunkSize); + if (chunkCount == chunks.Length) + { + byte[][] newChunks = new byte[chunks.Length * 2][]; + Array.Copy(chunks, newChunks, chunks.Length); + chunks = newChunks; + } + chunks[chunkCount++] = newChunk; + currentChunk = newChunk; + currentChunkSize = 0; + } + + public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + return Task.FromException(Fx.Exception.AsError(new NotSupportedException(SR.ReadNotSupported))); + } + + //public override IAsyncResult BeginRead(byte[] buffer, int offset, int size, AsyncCallback callback, object state) + //{ + // throw Fx.Exception.AsError(new NotSupportedException(SR.ReadNotSupported)); + //} + + //public override int EndRead(IAsyncResult result) + //{ + // throw Fx.Exception.AsError(new NotSupportedException(SR.ReadNotSupported)); + //} + + public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + Write(buffer, offset, count); + return Task.CompletedTask; + } + + //public override IAsyncResult BeginWrite(byte[] buffer, int offset, int size, AsyncCallback callback, object state) + //{ + // Write(buffer, offset, size); + // return new CompletedAsyncResult(callback, state); + //} + + //public override void EndWrite(IAsyncResult result) + //{ + // CompletedAsyncResult.End(result); + //} + + public void Clear() + { + if (!callerReturnsBuffer) + { + for (int i = 0; i < chunkCount; i++) + { + bufferManager.ReturnBuffer(chunks[i]); + chunks[i] = null; + } + } + + callerReturnsBuffer = false; + initialized = false; + bufferReturned = false; + chunkCount = 0; + currentChunk = null; + } + + //public override void Close() + //{ + //} + + public override void Flush() + { + } + + public override int Read(byte[] buffer, int offset, int size) + { + throw Fx.Exception.AsError(new NotSupportedException(SR.ReadNotSupported)); + } + + public override int ReadByte() + { + throw Fx.Exception.AsError(new NotSupportedException(SR.ReadNotSupported)); + } + + public override long Seek(long offset, SeekOrigin origin) + { + throw Fx.Exception.AsError(new NotSupportedException(SR.SeekNotSupported)); + } + + public override void SetLength(long value) + { + throw Fx.Exception.AsError(new NotSupportedException(SR.SeekNotSupported)); + } + + public MemoryStream ToMemoryStream() + { + int bufferSize; + byte[] buffer = ToArray(out bufferSize); + return new MemoryStream(buffer, 0, bufferSize); + } + + public byte[] ToArray(out int bufferSize) + { + Fx.Assert(initialized, "No data to return from uninitialized stream"); + Fx.Assert(!bufferReturned, "ToArray cannot be called more than once"); + + byte[] buffer; + if (chunkCount == 1) + { + buffer = currentChunk; + bufferSize = currentChunkSize; + callerReturnsBuffer = true; + } + else + { + buffer = bufferManager.TakeBuffer(totalSize); + int offset = 0; + int count = chunkCount - 1; + for (int i = 0; i < count; i++) + { + byte[] chunk = chunks[i]; + Buffer.BlockCopy(chunk, 0, buffer, offset, chunk.Length); + offset += chunk.Length; + } + Buffer.BlockCopy(currentChunk, 0, buffer, offset, currentChunkSize); + bufferSize = totalSize; + } + + bufferReturned = true; + return buffer; + } + + public void Skip(int size) + { + WriteCore(null, 0, size); + } + + public override void Write(byte[] buffer, int offset, int size) + { + WriteCore(buffer, offset, size); + } + + protected virtual Exception CreateQuotaExceededException(int maxSizeQuota) + { + return new InvalidOperationException(SR.Format(SR.BufferedOutputStreamQuotaExceeded, maxSizeQuota)); + } + + void WriteCore(byte[] buffer, int offset, int size) + { + Fx.Assert(initialized, "Cannot write to uninitialized stream"); + Fx.Assert(!bufferReturned, "Cannot write to stream once ToArray has been called."); + + if (size < 0) + { + throw Fx.Exception.ArgumentOutOfRange("size", size, SR.ValueMustBeNonNegative); + } + + if ((int.MaxValue - size) < totalSize) + { + throw Fx.Exception.AsError(CreateQuotaExceededException(maxSizeQuota)); + } + + int newTotalSize = totalSize + size; + if (newTotalSize > maxSize) + { + throw Fx.Exception.AsError(CreateQuotaExceededException(maxSizeQuota)); + } + + int remainingSizeInChunk = currentChunk.Length - currentChunkSize; + if (size > remainingSizeInChunk) + { + if (remainingSizeInChunk > 0) + { + if (buffer != null) + { + Buffer.BlockCopy(buffer, offset, currentChunk, currentChunkSize, remainingSizeInChunk); + } + currentChunkSize = currentChunk.Length; + offset += remainingSizeInChunk; + size -= remainingSizeInChunk; + } + AllocNextChunk(size); + } + + if (buffer != null) + { + Buffer.BlockCopy(buffer, offset, currentChunk, currentChunkSize, size); + } + totalSize = newTotalSize; + currentChunkSize += size; + } + + public override void WriteByte(byte value) + { + Fx.Assert(initialized, "Cannot write to uninitialized stream"); + Fx.Assert(!bufferReturned, "Cannot write to stream once ToArray has been called."); + + if (totalSize == maxSize) + { + throw Fx.Exception.AsError(CreateQuotaExceededException(maxSize)); + } + if (currentChunkSize == currentChunk.Length) + { + AllocNextChunk(1); + } + currentChunk[currentChunkSize++] = value; + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Runtime/CallbackException.cs b/src/CoreWCF.Primitives/src/CoreWCF/Runtime/CallbackException.cs new file mode 100644 index 000000000..df147d6d5 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Runtime/CallbackException.cs @@ -0,0 +1,23 @@ +using System; + +namespace CoreWCF.Runtime +{ + //[Serializable] + class CallbackException : FatalException + { + public CallbackException() + { + } + + public CallbackException(string message, Exception innerException) : base(message, innerException) + { + // This can't throw something like ArgumentException because that would be worse than + // throwing the callback exception that was requested. + Fx.Assert(innerException != null, "CallbackException requires an inner exception."); + Fx.Assert(!Fx.IsFatal(innerException), "CallbackException can't be used to wrap fatal exceptions."); + } + //protected CallbackException(SerializationInfo info, StreamingContext context) : base(info, context) + //{ + //} + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Runtime/Diagnostics/EtwDiagnosticTrace.cs b/src/CoreWCF.Primitives/src/CoreWCF/Runtime/Diagnostics/EtwDiagnosticTrace.cs new file mode 100644 index 000000000..dcab9bbc3 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Runtime/Diagnostics/EtwDiagnosticTrace.cs @@ -0,0 +1,74 @@ +using System; + +namespace CoreWCF.Runtime.Diagnostics +{ + internal sealed class EtwDiagnosticTrace + { + static readonly public Guid ImmutableDefaultEtwProviderId = new Guid("{c651f5f6-1c0d-492e-8ae1-b4efd7c9d503}"); + private static Guid s_defaultEtwProviderId = ImmutableDefaultEtwProviderId; + + static public Guid DefaultEtwProviderId + { + get + { + return s_defaultEtwProviderId; + } + set + { + s_defaultEtwProviderId = value; + } + } + + public EtwDiagnosticTrace(string traceSourceName, Guid etwProviderId) + //: base(traceSourceName) + { +// try +// { +// this.TraceSourceName = traceSourceName; +// this.EventSourceName = string.Concat(this.TraceSourceName, " ", EventSourceVersion); +// CreateTraceSource(); +// } +// catch (Exception exception) +// { +// if (Fx.IsFatal(exception)) +// { +// throw; +// } + +//#pragma warning disable 618 +// EventLogger logger = new EventLogger(this.EventSourceName, null); +// logger.LogEvent(TraceEventType.Error, TracingEventLogCategory, (uint)System.Runtime.Diagnostics.EventLogEventId.FailedToSetupTracing, false, +// exception.ToString()); +//#pragma warning restore 618 +// } + +// try +// { +// CreateEtwProvider(etwProviderId); +// } +// catch (Exception exception) +// { +// if (Fx.IsFatal(exception)) +// { +// throw; +// } + +// this.etwProvider = null; +//#pragma warning disable 618 +// EventLogger logger = new EventLogger(this.EventSourceName, null); +// logger.LogEvent(TraceEventType.Error, TracingEventLogCategory, (uint)System.Runtime.Diagnostics.EventLogEventId.FailedToSetupTracing, false, +// exception.ToString()); +//#pragma warning restore 618 + +// } + +// if (this.TracingEnabled || this.EtwTracingEnabled) +// { +//#pragma warning disable 618 +// this.AddDomainEventHandlersForCleanup(); +//#pragma warning restore 618 +// } + } + + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Runtime/Diagnostics/SecurityTraceRecordHelper.cs b/src/CoreWCF.Primitives/src/CoreWCF/Runtime/Diagnostics/SecurityTraceRecordHelper.cs new file mode 100644 index 000000000..65cdfc4e2 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Runtime/Diagnostics/SecurityTraceRecordHelper.cs @@ -0,0 +1,29 @@ +using System; +using CoreWCF.IdentityModel.Claims; +using CoreWCF.IdentityModel.Policy; +using CoreWCF; +using CoreWCF.Diagnostics; + +namespace CoreWCF.Runtime.Diagnostics +{ + internal static class SecurityTraceRecordHelper + { + internal static void TraceIdentityDeterminationFailure(EndpointAddress epr, Type identityVerifier) + { + //if (DiagnosticUtility.ShouldTraceInformation) + // TraceUtility.TraceEvent(TraceEventType.Information, TraceCode.SecurityIdentityDeterminationFailure, SR.Format(SR.TraceCodeSecurityIdentityDeterminationFailure), new IdentityDeterminationFailureTraceRecord(epr, identityVerifier)); + } + + internal static void TraceIdentityDeterminationSuccess(EndpointAddress epr, EndpointIdentity identity, Type identityVerifier) + { + //if (DiagnosticUtility.ShouldTraceInformation) + // TraceUtility.TraceEvent(TraceEventType.Information, TraceCode.SecurityIdentityDeterminationSuccess, SR.Format(SR.TraceCodeSecurityIdentityDeterminationSuccess), new IdentityDeterminationSuccessTraceRecord(epr, identity, identityVerifier)); + } + + internal static void TraceIdentityVerificationFailure(EndpointIdentity identity, AuthorizationContext authContext, Type identityVerifier) + { + //if (DiagnosticUtility.ShouldTraceInformation) + // TraceUtility.TraceEvent(TraceEventType.Information, TraceCode.SecurityIdentityVerificationFailure, SR.Format(SR.TraceCodeSecurityIdentityVerificationFailure), new IdentityVerificationFailureTraceRecord(identity, authContext, identityVerifier)); + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Runtime/InputQueue.cs b/src/CoreWCF.Primitives/src/CoreWCF/Runtime/InputQueue.cs new file mode 100644 index 000000000..7fb360c73 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Runtime/InputQueue.cs @@ -0,0 +1,863 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using CoreWCF; + +namespace CoreWCF.Runtime +{ + sealed class InputQueue : IDisposable where T : class + { + static Action completeOutstandingReadersCallback; + static Action completeWaitersFalseCallback; + static Action completeWaitersTrueCallback; + static Action onDispatchCallback; + static Action onInvokeDequeuedCallback; + + QueueState queueState; + + ItemQueue itemQueue; + Queue readerQueue; + List waiterList; + + public InputQueue() + { + itemQueue = new ItemQueue(); + readerQueue = new Queue(); + waiterList = new List(); + queueState = QueueState.Open; + } + + public InputQueue(Func> asyncCallbackGenerator) + : this() + { + Fx.Assert(asyncCallbackGenerator != null, "use default ctor if you don't have a generator"); + AsyncCallbackGenerator = asyncCallbackGenerator; + } + + public int PendingCount + { + get + { + lock (ThisLock) + { + return itemQueue.ItemCount; + } + } + } + + // Users like ServiceModel can hook this abort ICommunicationObject or handle other non-IDisposable objects + public Action DisposeItemCallback + { + get; + set; + } + + // Users like ServiceModel can hook this to wrap the AsyncQueueReader callback functionality for tracing, etc + Func> AsyncCallbackGenerator + { + get; + set; + } + + object ThisLock + { + get { return itemQueue; } + } + + public void Close() + { + Dispose(); + } + + public async Task DequeueAsync(CancellationToken token) + { + TryAsyncResult result = await TryDequeueAsync(token); + + if (!result.Success) + { + // TODO: Create derived CancellationToken which carries original timeout with it + throw Fx.Exception.AsError(new TimeoutException(SR.Format(SR.TimeoutInputQueueDequeue, null))); + } + + return result.Result; + + } + + public async Task> TryDequeueAsync(CancellationToken token) + { + WaitQueueReader reader = null; + Item item = new Item(); + + lock (ThisLock) + { + if (queueState == QueueState.Open) + { + if (itemQueue.HasAvailableItem) + { + item = itemQueue.DequeueAvailableItem(); + } + else + { + reader = new WaitQueueReader(this); + readerQueue.Enqueue(reader); + } + } + else if (queueState == QueueState.Shutdown) + { + if (itemQueue.HasAvailableItem) + { + item = itemQueue.DequeueAvailableItem(); + } + else if (itemQueue.HasAnyItem) + { + reader = new WaitQueueReader(this); + readerQueue.Enqueue(reader); + } + else + { + return TryAsyncResult.FromResult(default(T)); + } + } + else // queueState == QueueState.Closed + { + return TryAsyncResult.FromResult(default(T)); + } + } + + if (reader != null) + { + return await reader.WaitAsync(token); + } + else + { + InvokeDequeuedCallback(item.DequeuedCallback); + return TryAsyncResult.FromResult(item.GetValue()); + } + } + + public void Dispatch() + { + IQueueReader reader = null; + Item item = new Item(); + IQueueReader[] outstandingReaders = null; + IQueueWaiter[] waiters = null; + bool itemAvailable = true; + + lock (ThisLock) + { + itemAvailable = !((queueState == QueueState.Closed) || (queueState == QueueState.Shutdown)); + GetWaiters(out waiters); + + if (queueState != QueueState.Closed) + { + itemQueue.MakePendingItemAvailable(); + + if (readerQueue.Count > 0) + { + item = itemQueue.DequeueAvailableItem(); + reader = readerQueue.Dequeue(); + + if (queueState == QueueState.Shutdown && readerQueue.Count > 0 && itemQueue.ItemCount == 0) + { + outstandingReaders = new IQueueReader[readerQueue.Count]; + readerQueue.CopyTo(outstandingReaders, 0); + readerQueue.Clear(); + + itemAvailable = false; + } + } + } + } + + if (outstandingReaders != null) + { + if (completeOutstandingReadersCallback == null) + { + completeOutstandingReadersCallback = CompleteOutstandingReadersCallback; + } + + ActionItem.Schedule(completeOutstandingReadersCallback, outstandingReaders); + } + + if (waiters != null) + { + CompleteWaitersLater(itemAvailable, waiters); + } + + if (reader != null) + { + InvokeDequeuedCallback(item.DequeuedCallback); + reader.Set(item); + } + } + + public void EnqueueAndDispatch(T item) + { + EnqueueAndDispatch(item, null); + } + + // dequeuedCallback is called as an item is dequeued from the InputQueue. The + // InputQueue lock is not held during the callback. However, the user code will + // not be notified of the item being available until the callback returns. If you + // are not sure if the callback will block for a long time, then first call + // IOThreadScheduler.ScheduleCallback to get to a "safe" thread. + public void EnqueueAndDispatch(T item, Action dequeuedCallback) + { + EnqueueAndDispatch(item, dequeuedCallback, true); + } + + public void EnqueueAndDispatch(Exception exception, Action dequeuedCallback, bool canDispatchOnThisThread) + { + Fx.Assert(exception != null, "EnqueueAndDispatch: exception parameter should not be null"); + EnqueueAndDispatch(new Item(exception, dequeuedCallback), canDispatchOnThisThread); + } + + public void EnqueueAndDispatch(T item, Action dequeuedCallback, bool canDispatchOnThisThread) + { + Fx.Assert(item != null, "EnqueueAndDispatch: item parameter should not be null"); + EnqueueAndDispatch(new Item(item, dequeuedCallback), canDispatchOnThisThread); + } + + public bool EnqueueWithoutDispatch(T item, Action dequeuedCallback) + { + Fx.Assert(item != null, "EnqueueWithoutDispatch: item parameter should not be null"); + return EnqueueWithoutDispatch(new Item(item, dequeuedCallback)); + } + + public bool EnqueueWithoutDispatch(Exception exception, Action dequeuedCallback) + { + Fx.Assert(exception != null, "EnqueueWithoutDispatch: exception parameter should not be null"); + return EnqueueWithoutDispatch(new Item(exception, dequeuedCallback)); + } + + + public void Shutdown() + { + Shutdown(null); + } + + // Don't let any more items in. Differs from Close in that we keep around + // existing items in our itemQueue for possible future calls to Dequeue + public void Shutdown(Func pendingExceptionGenerator) + { + IQueueReader[] outstandingReaders = null; + lock (ThisLock) + { + if (queueState == QueueState.Shutdown) + { + return; + } + + if (queueState == QueueState.Closed) + { + return; + } + + queueState = QueueState.Shutdown; + + if (readerQueue.Count > 0 && itemQueue.ItemCount == 0) + { + outstandingReaders = new IQueueReader[readerQueue.Count]; + readerQueue.CopyTo(outstandingReaders, 0); + readerQueue.Clear(); + } + } + + if (outstandingReaders != null) + { + for (int i = 0; i < outstandingReaders.Length; i++) + { + Exception exception = (pendingExceptionGenerator != null) ? pendingExceptionGenerator() : null; + outstandingReaders[i].Set(new Item(exception, null)); + } + } + } + + + public Task WaitForItemAsync(CancellationToken token) + { + WaitQueueWaiter waiter = null; + bool itemAvailable = false; + + lock (ThisLock) + { + if (queueState == QueueState.Open) + { + if (itemQueue.HasAvailableItem) + { + itemAvailable = true; + } + else + { + waiter = new WaitQueueWaiter(); + waiterList.Add(waiter); + } + } + else if (queueState == QueueState.Shutdown) + { + if (itemQueue.HasAvailableItem) + { + itemAvailable = true; + } + else if (itemQueue.HasAnyItem) + { + waiter = new WaitQueueWaiter(); + waiterList.Add(waiter); + } + else + { + return Task.FromResult(true); + } + } + else // queueState == QueueState.Closed + { + return Task.FromResult(true); + } + } + + if (waiter != null) + { + return waiter.WaitAsync(token); + } + else + { + return Task.FromResult(itemAvailable); + } + } + + public void Dispose() + { + bool dispose = false; + + lock (ThisLock) + { + if (queueState != QueueState.Closed) + { + queueState = QueueState.Closed; + dispose = true; + } + } + + if (dispose) + { + while (readerQueue.Count > 0) + { + IQueueReader reader = readerQueue.Dequeue(); + reader.Set(default(Item)); + } + + while (itemQueue.HasAnyItem) + { + Item item = itemQueue.DequeueAnyItem(); + DisposeItem(item); + InvokeDequeuedCallback(item.DequeuedCallback); + } + } + } + + void DisposeItem(Item item) + { + T value = item.Value; + if (value != null) + { + if (value is IDisposable) + { + ((IDisposable)value).Dispose(); + } + else + { + Action disposeItemCallback = DisposeItemCallback; + if (disposeItemCallback != null) + { + disposeItemCallback(value); + } + } + } + } + + static void CompleteOutstandingReadersCallback(object state) + { + IQueueReader[] outstandingReaders = (IQueueReader[])state; + + for (int i = 0; i < outstandingReaders.Length; i++) + { + outstandingReaders[i].Set(default(Item)); + } + } + + static void CompleteWaiters(bool itemAvailable, IQueueWaiter[] waiters) + { + for (int i = 0; i < waiters.Length; i++) + { + waiters[i].Set(itemAvailable); + } + } + + static void CompleteWaitersFalseCallback(object state) + { + CompleteWaiters(false, (IQueueWaiter[])state); + } + + static void CompleteWaitersLater(bool itemAvailable, IQueueWaiter[] waiters) + { + if (itemAvailable) + { + if (completeWaitersTrueCallback == null) + { + completeWaitersTrueCallback = CompleteWaitersTrueCallback; + } + + ActionItem.Schedule(completeWaitersTrueCallback, waiters); + } + else + { + if (completeWaitersFalseCallback == null) + { + completeWaitersFalseCallback = CompleteWaitersFalseCallback; + } + + ActionItem.Schedule(completeWaitersFalseCallback, waiters); + } + } + + static void CompleteWaitersTrueCallback(object state) + { + CompleteWaiters(true, (IQueueWaiter[])state); + } + + static void InvokeDequeuedCallback(Action dequeuedCallback) + { + if (dequeuedCallback != null) + { + dequeuedCallback(); + } + } + + static void InvokeDequeuedCallbackLater(Action dequeuedCallback) + { + if (dequeuedCallback != null) + { + if (onInvokeDequeuedCallback == null) + { + onInvokeDequeuedCallback = OnInvokeDequeuedCallback; + } + + ActionItem.Schedule(onInvokeDequeuedCallback, dequeuedCallback); + } + } + + static void OnDispatchCallback(object state) + { + ((InputQueue)state).Dispatch(); + } + + static void OnInvokeDequeuedCallback(object state) + { + Fx.Assert(state != null, "InputQueue.OnInvokeDequeuedCallback: (state != null)"); + + Action dequeuedCallback = (Action)state; + dequeuedCallback(); + } + + void EnqueueAndDispatch(Item item, bool canDispatchOnThisThread) + { + bool disposeItem = false; + IQueueReader reader = null; + bool dispatchLater = false; + IQueueWaiter[] waiters = null; + bool itemAvailable = true; + + lock (ThisLock) + { + itemAvailable = !((queueState == QueueState.Closed) || (queueState == QueueState.Shutdown)); + GetWaiters(out waiters); + + if (queueState == QueueState.Open) + { + if (canDispatchOnThisThread) + { + if (readerQueue.Count == 0) + { + itemQueue.EnqueueAvailableItem(item); + } + else + { + reader = readerQueue.Dequeue(); + } + } + else + { + if (readerQueue.Count == 0) + { + itemQueue.EnqueueAvailableItem(item); + } + else + { + itemQueue.EnqueuePendingItem(item); + dispatchLater = true; + } + } + } + else // queueState == QueueState.Closed || queueState == QueueState.Shutdown + { + disposeItem = true; + } + } + + if (waiters != null) + { + if (canDispatchOnThisThread) + { + CompleteWaiters(itemAvailable, waiters); + } + else + { + CompleteWaitersLater(itemAvailable, waiters); + } + } + + if (reader != null) + { + InvokeDequeuedCallback(item.DequeuedCallback); + reader.Set(item); + } + + if (dispatchLater) + { + if (onDispatchCallback == null) + { + onDispatchCallback = OnDispatchCallback; + } + + ActionItem.Schedule(onDispatchCallback, this); + } + else if (disposeItem) + { + InvokeDequeuedCallback(item.DequeuedCallback); + DisposeItem(item); + } + } + + // This will not block, however, Dispatch() must be called later if this function + // returns true. + bool EnqueueWithoutDispatch(Item item) + { + lock (ThisLock) + { + // Open + if (queueState != QueueState.Closed && queueState != QueueState.Shutdown) + { + if (readerQueue.Count == 0 && waiterList.Count == 0) + { + itemQueue.EnqueueAvailableItem(item); + return false; + } + else + { + itemQueue.EnqueuePendingItem(item); + return true; + } + } + } + + DisposeItem(item); + InvokeDequeuedCallbackLater(item.DequeuedCallback); + return false; + } + + void GetWaiters(out IQueueWaiter[] waiters) + { + if (waiterList.Count > 0) + { + waiters = waiterList.ToArray(); + waiterList.Clear(); + } + else + { + waiters = null; + } + } + + // Used for timeouts. The InputQueue must remove readers from its reader queue to prevent + // dispatching items to timed out readers. + bool RemoveReader(IQueueReader reader) + { + Fx.Assert(reader != null, "InputQueue.RemoveReader: (reader != null)"); + + lock (ThisLock) + { + if (queueState == QueueState.Open || queueState == QueueState.Shutdown) + { + bool removed = false; + + for (int i = readerQueue.Count; i > 0; i--) + { + IQueueReader temp = readerQueue.Dequeue(); + if (object.ReferenceEquals(temp, reader)) + { + removed = true; + } + else + { + readerQueue.Enqueue(temp); + } + } + + return removed; + } + } + + return false; + } + + enum QueueState + { + Open, + Shutdown, + Closed + } + + interface IQueueReader + { + void Set(Item item); + } + + interface IQueueWaiter + { + void Set(bool itemAvailable); + } + + struct Item + { + Action dequeuedCallback; + Exception exception; + T value; + + public Item(T value, Action dequeuedCallback) + : this(value, null, dequeuedCallback) + { + } + + public Item(Exception exception, Action dequeuedCallback) + : this(null, exception, dequeuedCallback) + { + } + + Item(T value, Exception exception, Action dequeuedCallback) + { + this.value = value; + this.exception = exception; + this.dequeuedCallback = dequeuedCallback; + } + + public Action DequeuedCallback + { + get { return dequeuedCallback; } + } + + public Exception Exception + { + get { return exception; } + } + + public T Value + { + get { return value; } + } + + public T GetValue() + { + if (exception != null) + { + throw Fx.Exception.AsError(exception); + } + + return value; + } + } + + class ItemQueue + { + int head; + Item[] items; + int pendingCount; + int totalCount; + + public ItemQueue() + { + items = new Item[1]; + } + + public bool HasAnyItem + { + get { return totalCount > 0; } + } + + public bool HasAvailableItem + { + get { return totalCount > pendingCount; } + } + + public int ItemCount + { + get { return totalCount; } + } + + public Item DequeueAnyItem() + { + if (pendingCount == totalCount) + { + pendingCount--; + } + return DequeueItemCore(); + } + + public Item DequeueAvailableItem() + { + Fx.AssertAndThrow(totalCount != pendingCount, "ItemQueue does not contain any available items"); + return DequeueItemCore(); + } + + public void EnqueueAvailableItem(Item item) + { + EnqueueItemCore(item); + } + + public void EnqueuePendingItem(Item item) + { + EnqueueItemCore(item); + pendingCount++; + } + + public void MakePendingItemAvailable() + { + Fx.AssertAndThrow(pendingCount != 0, "ItemQueue does not contain any pending items"); + pendingCount--; + } + + Item DequeueItemCore() + { + Fx.AssertAndThrow(totalCount != 0, "ItemQueue does not contain any items"); + Item item = items[head]; + items[head] = new Item(); + totalCount--; + head = (head + 1) % items.Length; + return item; + } + + void EnqueueItemCore(Item item) + { + if (totalCount == items.Length) + { + Item[] newItems = new Item[items.Length * 2]; + for (int i = 0; i < totalCount; i++) + { + newItems[i] = items[(head + i) % items.Length]; + } + head = 0; + items = newItems; + } + int tail = (head + totalCount) % items.Length; + items[tail] = item; + totalCount++; + } + } + + class WaitQueueReader : IQueueReader + { + Exception exception; + InputQueue inputQueue; + T item; + + AsyncManualResetEvent waitEvent; + + public WaitQueueReader(InputQueue inputQueue) + { + this.inputQueue = inputQueue; + waitEvent = new AsyncManualResetEvent(); + } + + public void Set(Item item) + { + lock (this) + { + Fx.Assert(this.item == null, "InputQueue.WaitQueueReader.Set: (this.item == null)"); + Fx.Assert(exception == null, "InputQueue.WaitQueueReader.Set: (this.exception == null)"); + + exception = item.Exception; + this.item = item.Value; + waitEvent.Set(); + } + } + + public async Task> WaitAsync(CancellationToken token) + { + bool isSafeToClose = false; + try + { + if (!await waitEvent.WaitAsync(token)) + { + if (inputQueue.RemoveReader(this)) + { + isSafeToClose = true; + return TryAsyncResult.FailedResult; + } + else + { + await waitEvent.WaitAsync(); + } + } + + isSafeToClose = true; + } + finally + { + if (isSafeToClose) + { + waitEvent.Dispose(); + } + } + + if (exception != null) + { + throw Fx.Exception.AsError(exception); + } + + return TryAsyncResult.FromResult(item); + } + } + + class WaitQueueWaiter : IQueueWaiter + { + bool itemAvailable; + + AsyncManualResetEvent waitEvent; + + public WaitQueueWaiter() + { + waitEvent = new AsyncManualResetEvent(); + } + + public void Set(bool itemAvailable) + { + lock (this) + { + this.itemAvailable = itemAvailable; + waitEvent.Set(); + } + } + + public async Task WaitAsync(CancellationToken token) + { + if (!await waitEvent.WaitAsync(token)) + { + return false; + } + + return itemAvailable; + } + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Runtime/InternalBufferManager.cs b/src/CoreWCF.Primitives/src/CoreWCF/Runtime/InternalBufferManager.cs new file mode 100644 index 000000000..1b1cd6e03 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Runtime/InternalBufferManager.cs @@ -0,0 +1,519 @@ +using System; +using System.Collections.Generic; +using System.Threading; + +namespace CoreWCF.Runtime +{ + abstract class InternalBufferManager + { + protected InternalBufferManager() + { + } + + public abstract byte[] TakeBuffer(int bufferSize); + public abstract void ReturnBuffer(byte[] buffer); + public abstract void Clear(); + + public static InternalBufferManager Create(long maxBufferPoolSize, int maxBufferSize) + { + if (maxBufferPoolSize == 0) + { + return GCBufferManager.Value; + } + else + { + Fx.Assert(maxBufferPoolSize > 0 && maxBufferSize >= 0, "bad params, caller should verify"); + return new PooledBufferManager(maxBufferPoolSize, maxBufferSize); + } + } + + class PooledBufferManager : InternalBufferManager + { + const int minBufferSize = 128; + const int maxMissesBeforeTuning = 8; + const int initialBufferCount = 1; + readonly object tuningLock; + + int[] bufferSizes; + BufferPool[] bufferPools; + long memoryLimit; + long remainingMemory; + bool areQuotasBeingTuned; + int totalMisses; + + public PooledBufferManager(long maxMemoryToPool, int maxBufferSize) + { + tuningLock = new object(); + memoryLimit = maxMemoryToPool; + remainingMemory = maxMemoryToPool; + List bufferPoolList = new List(); + + for (int bufferSize = minBufferSize; ;) + { + long bufferCountLong = remainingMemory / bufferSize; + + int bufferCount = bufferCountLong > int.MaxValue ? int.MaxValue : (int)bufferCountLong; + + if (bufferCount > initialBufferCount) + { + bufferCount = initialBufferCount; + } + + bufferPoolList.Add(BufferPool.CreatePool(bufferSize, bufferCount)); + + remainingMemory -= (long)bufferCount * bufferSize; + + if (bufferSize >= maxBufferSize) + { + break; + } + + long newBufferSizeLong = (long)bufferSize * 2; + + if (newBufferSizeLong > (long)maxBufferSize) + { + bufferSize = maxBufferSize; + } + else + { + bufferSize = (int)newBufferSizeLong; + } + } + + bufferPools = bufferPoolList.ToArray(); + bufferSizes = new int[bufferPools.Length]; + for (int i = 0; i < bufferPools.Length; i++) + { + bufferSizes[i] = bufferPools[i].BufferSize; + } + } + + public override void Clear() + { + + for (int i = 0; i < bufferPools.Length; i++) + { + BufferPool bufferPool = bufferPools[i]; + bufferPool.Clear(); + } + } + + void ChangeQuota(ref BufferPool bufferPool, int delta) + { + + //if (TraceCore.BufferPoolChangeQuotaIsEnabled(Fx.Trace)) + //{ + // TraceCore.BufferPoolChangeQuota(Fx.Trace, bufferPool.BufferSize, delta); + //} + + BufferPool oldBufferPool = bufferPool; + int newLimit = oldBufferPool.Limit + delta; + BufferPool newBufferPool = BufferPool.CreatePool(oldBufferPool.BufferSize, newLimit); + for (int i = 0; i < newLimit; i++) + { + byte[] buffer = oldBufferPool.Take(); + if (buffer == null) + { + break; + } + newBufferPool.Return(buffer); + newBufferPool.IncrementCount(); + } + remainingMemory -= oldBufferPool.BufferSize * delta; + bufferPool = newBufferPool; + } + + void DecreaseQuota(ref BufferPool bufferPool) + { + ChangeQuota(ref bufferPool, -1); + } + + int FindMostExcessivePool() + { + long maxBytesInExcess = 0; + int index = -1; + + for (int i = 0; i < bufferPools.Length; i++) + { + BufferPool bufferPool = bufferPools[i]; + + if (bufferPool.Peak < bufferPool.Limit) + { + long bytesInExcess = (bufferPool.Limit - bufferPool.Peak) * (long)bufferPool.BufferSize; + + if (bytesInExcess > maxBytesInExcess) + { + index = i; + maxBytesInExcess = bytesInExcess; + } + } + } + + return index; + } + + int FindMostStarvedPool() + { + long maxBytesMissed = 0; + int index = -1; + + for (int i = 0; i < bufferPools.Length; i++) + { + BufferPool bufferPool = bufferPools[i]; + + if (bufferPool.Peak == bufferPool.Limit) + { + long bytesMissed = bufferPool.Misses * (long)bufferPool.BufferSize; + + if (bytesMissed > maxBytesMissed) + { + index = i; + maxBytesMissed = bytesMissed; + } + } + } + + return index; + } + + BufferPool FindPool(int desiredBufferSize) + { + for (int i = 0; i < bufferSizes.Length; i++) + { + if (desiredBufferSize <= bufferSizes[i]) + { + return bufferPools[i]; + } + } + + return null; + } + + void IncreaseQuota(ref BufferPool bufferPool) + { + ChangeQuota(ref bufferPool, 1); + } + + public override void ReturnBuffer(byte[] buffer) + { + Fx.Assert(buffer != null, "caller must verify"); + BufferPool bufferPool = FindPool(buffer.Length); + if (bufferPool != null) + { + if (buffer.Length != bufferPool.BufferSize) + { + throw Fx.Exception.Argument(nameof(buffer), SR.BufferIsNotRightSizeForBufferManager); + } + + if (bufferPool.Return(buffer)) + { + bufferPool.IncrementCount(); + } + } + } + + public override byte[] TakeBuffer(int bufferSize) + { + Fx.Assert(bufferSize >= 0, "caller must ensure a non-negative argument"); + + BufferPool bufferPool = FindPool(bufferSize); + byte[] returnValue; + if (bufferPool != null) + { + byte[] buffer = bufferPool.Take(); + if (buffer != null) + { + bufferPool.DecrementCount(); + returnValue = buffer; + } + else + { + if (bufferPool.Peak == bufferPool.Limit) + { + bufferPool.Misses++; + if (++totalMisses >= maxMissesBeforeTuning) + { + TuneQuotas(); + } + } + + //if (TraceCore.BufferPoolAllocationIsEnabled(Fx.Trace)) + //{ + // TraceCore.BufferPoolAllocation(Fx.Trace, bufferPool.BufferSize); + //} + + returnValue = Fx.AllocateByteArray(bufferPool.BufferSize); + } + } + else + { + //if (TraceCore.BufferPoolAllocationIsEnabled(Fx.Trace)) + //{ + // TraceCore.BufferPoolAllocation(Fx.Trace, bufferSize); + //} + + returnValue = Fx.AllocateByteArray(bufferSize); + } + + return returnValue; + } + + void TuneQuotas() + { + if (areQuotasBeingTuned) + { + return; + } + + bool lockHeld = false; + try + { + Monitor.TryEnter(tuningLock, ref lockHeld); + + // Don't bother if another thread already has the lock + if (!lockHeld || areQuotasBeingTuned) + { + return; + } + + areQuotasBeingTuned = true; + } + finally + { + if (lockHeld) + { + Monitor.Exit(tuningLock); + } + } + + // find the "poorest" pool + int starvedIndex = FindMostStarvedPool(); + if (starvedIndex >= 0) + { + BufferPool starvedBufferPool = bufferPools[starvedIndex]; + + if (remainingMemory < starvedBufferPool.BufferSize) + { + // find the "richest" pool + int excessiveIndex = FindMostExcessivePool(); + if (excessiveIndex >= 0) + { + // steal from the richest + DecreaseQuota(ref bufferPools[excessiveIndex]); + } + } + + if (remainingMemory >= starvedBufferPool.BufferSize) + { + // give to the poorest + IncreaseQuota(ref bufferPools[starvedIndex]); + } + } + + // reset statistics + for (int i = 0; i < bufferPools.Length; i++) + { + BufferPool bufferPool = bufferPools[i]; + bufferPool.Misses = 0; + } + + totalMisses = 0; + areQuotasBeingTuned = false; + } + + abstract class BufferPool + { + int bufferSize; + int count; + int limit; + int misses; + int peak; + + public BufferPool(int bufferSize, int limit) + { + this.bufferSize = bufferSize; + this.limit = limit; + } + + public int BufferSize + { + get { return bufferSize; } + } + + public int Limit + { + get { return limit; } + } + + public int Misses + { + get { return misses; } + set { misses = value; } + } + + public int Peak + { + get { return peak; } + } + + public void Clear() + { + OnClear(); + count = 0; + } + + public void DecrementCount() + { + int newValue = count - 1; + if (newValue >= 0) + { + count = newValue; + } + } + + public void IncrementCount() + { + int newValue = count + 1; + if (newValue <= limit) + { + count = newValue; + if (newValue > peak) + { + peak = newValue; + } + } + } + + internal abstract byte[] Take(); + internal abstract bool Return(byte[] buffer); + internal abstract void OnClear(); + + internal static BufferPool CreatePool(int bufferSize, int limit) + { + // To avoid many buffer drops during training of large objects which + // get allocated on the LOH, we use the LargeBufferPool and for + // bufferSize < 85000, the SynchronizedPool. However if bufferSize < 85000 + // and (bufferSize + array-overhead) > 85000, this would still use + // the SynchronizedPool even though it is allocated on the LOH. + if (bufferSize < 85000) + { + return new SynchronizedBufferPool(bufferSize, limit); + } + else + { + return new LargeBufferPool(bufferSize, limit); + } + } + + class SynchronizedBufferPool : BufferPool + { + SynchronizedPool innerPool; + + internal SynchronizedBufferPool(int bufferSize, int limit) + : base(bufferSize, limit) + { + innerPool = new SynchronizedPool(limit); + } + + internal override void OnClear() + { + innerPool.Clear(); + } + + internal override byte[] Take() + { + return innerPool.Take(); + } + + internal override bool Return(byte[] buffer) + { + return innerPool.Return(buffer); + } + } + + class LargeBufferPool : BufferPool + { + Stack items; + + internal LargeBufferPool(int bufferSize, int limit) + : base(bufferSize, limit) + { + items = new Stack(limit); + } + + object ThisLock + { + get + { + return items; + } + } + + internal override void OnClear() + { + lock (ThisLock) + { + items.Clear(); + } + } + + internal override byte[] Take() + { + lock (ThisLock) + { + if (items.Count > 0) + { + return items.Pop(); + } + } + + return null; + } + + internal override bool Return(byte[] buffer) + { + lock (ThisLock) + { + if (items.Count < Limit) + { + items.Push(buffer); + return true; + } + } + + return false; + } + } + } + } + + class GCBufferManager : InternalBufferManager + { + static GCBufferManager value = new GCBufferManager(); + + GCBufferManager() + { + } + + public static GCBufferManager Value + { + get { return value; } + } + + public override void Clear() + { + } + + public override byte[] TakeBuffer(int bufferSize) + { + return Fx.AllocateByteArray(bufferSize); + } + + public override void ReturnBuffer(byte[] buffer) + { + // do nothing, GC will reclaim this buffer + } + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Runtime/ReflectionExtensions.cs b/src/CoreWCF.Primitives/src/CoreWCF/Runtime/ReflectionExtensions.cs new file mode 100644 index 000000000..bc4b0006c --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Runtime/ReflectionExtensions.cs @@ -0,0 +1,64 @@ +using System; +using System.Reflection; + +namespace CoreWCF.Runtime +{ + public static class ReflectionExtensions + { + public static TypeCode GetTypeCode(this Type type) + { + if (type == null) + return TypeCode.Empty; + + if (type == typeof(bool)) + return TypeCode.Boolean; + + if (type == typeof(char)) + return TypeCode.Char; + + if (type == typeof(sbyte)) + return TypeCode.SByte; + + if (type == typeof(byte)) + return TypeCode.Byte; + + if (type == typeof(short)) + return TypeCode.Int16; + + if (type == typeof(ushort)) + return TypeCode.UInt16; + + if (type == typeof(int)) + return TypeCode.Int32; + + if (type == typeof(uint)) + return TypeCode.UInt32; + + if (type == typeof(long)) + return TypeCode.Int64; + + if (type == typeof(ulong)) + return TypeCode.UInt64; + + if (type == typeof(float)) + return TypeCode.Single; + + if (type == typeof(double)) + return TypeCode.Double; + + if (type == typeof(decimal)) + return TypeCode.Decimal; + + if (type == typeof(DateTime)) + return TypeCode.DateTime; + + if (type == typeof(string)) + return TypeCode.String; + + if (type.GetTypeInfo().IsEnum) + return GetTypeCode(Enum.GetUnderlyingType(type)); + + return TypeCode.Object; + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Runtime/Serialization/XmlMappingTypes.cs b/src/CoreWCF.Primitives/src/CoreWCF/Runtime/Serialization/XmlMappingTypes.cs new file mode 100644 index 000000000..c34f8abaf --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Runtime/Serialization/XmlMappingTypes.cs @@ -0,0 +1,446 @@ +using System; +using System.Diagnostics.Contracts; +using System.Reflection; +using System.Xml.Serialization; + +namespace CoreWCF.Runtime.Serialization +{ + // The classes in this file are used for invoking APIs that exist in System.Xml.XmlSerializer + // via reflection. Those APIs are not supposed to be used by application developers, thus we + // cannot add them into public contract. + //internal interface IXmlMappingTypeInnerObject + //{ + // IXmlMappingTypeWrapperObject Object { get; } + //} + + //internal class XmlReflectionImporter + //{ + // private static readonly MethodInfo s_importMembersMapping; + // private static readonly MethodInfo s_importTypeMapping; + // private static readonly MethodInfo s_includeType; + + // private IXmlMappingTypeWrapperObject _wrapperObject; + + // public XmlReflectionImporter() : this(null) + // { + // } + + // public XmlReflectionImporter(string defaultNs) + // { + // _wrapperObject = + // XmlMappingTypeWrapperFactory.GetWrapper( + // XmlMappingTypesHelper.XmlReflectionImporterType, new object[] {defaultNs}); + // } + + // static XmlReflectionImporter() + // { + // IXmlMappingTypeWrapperObject wrapperObject = + // XmlMappingTypeWrapperFactory.GetWrapper( + // XmlMappingTypesHelper.XmlReflectionImporterType, new object[] {"defaultNs"}); + + // Type[] types; + + // if (XmlMappingTypesHelper.XmlReflectionMemberType != null) + // { + // types = new Type[] + // { + // typeof (string), + // typeof (string), + // XmlMappingTypesHelper.XmlReflectionMemberType.MakeArrayType(), + // typeof (bool), + // typeof (bool) + // }; + + // s_importMembersMapping = wrapperObject.GetMethod("ImportMembersMapping", types); + // } + // else + // { + // // XmlMappingTypesHelper.XmlReflectionMemberType is null only at pure Net Native runtime. + // // In that case, we don't really need to set the following method. + // s_importMembersMapping = null; + // } + + // types = new Type[] {typeof (Type)}; + // s_importTypeMapping = wrapperObject.GetMethod("ImportTypeMapping", types); + + // s_includeType = wrapperObject.GetMethod("IncludeType", types); + // } + + // public XmlMembersMapping ImportMembersMapping(string mappingName, string ns, XmlReflectionMember[] members, bool hasWrapperElement, bool rpc) + // { + // Array membersObjects = + // _wrapperObject.InitializeArray(XmlMappingTypesHelper.XmlReflectionMemberType, members); + + // object[] parameters = new object[] {mappingName, ns, membersObjects, hasWrapperElement, rpc}; + // object o = _wrapperObject.CallMethod(s_importMembersMapping, parameters); + // return new XmlMembersMapping(o); + // } + + // public XmlTypeMapping ImportTypeMapping(Type type) + // { + // Type[] types = new Type[] {typeof (Type)}; + // object[] parameters = new object[] {type}; + // object o = _wrapperObject.CallMethod(s_importTypeMapping, parameters); + // return new XmlTypeMapping(o); + // } + + // public void IncludeType(Type knownType) + // { + // Type[] types = new Type[] {typeof (Type)}; + // object[] parameters = new object[] {knownType}; + // _wrapperObject.CallMethod(s_includeType, parameters); + // } + //} + + //internal abstract class XmlMapping : IXmlMappingTypeInnerObject + //{ + // protected IXmlMappingTypeWrapperObject _wrapperObject; + + // private static MethodInfo s_setKey; + // private static bool s_setKeyInitialized; + + // public XmlMapping(object thisObject) + // { + // this._wrapperObject = XmlMappingTypeWrapperFactory.GetWrapper(thisObject); + // } + + // public IXmlMappingTypeWrapperObject Object => _wrapperObject; + + // public string ElementName => (string) _wrapperObject.GetProperty("ElementName"); + + // public string Namespace => (string) _wrapperObject.GetProperty("Namespace"); + + // public string XsdElementName => (string) _wrapperObject.GetProperty("XsdElementName"); + + // private string _key; + // internal string Key => _key; + + // public void SetKey(string key) + // { + // if (!s_setKeyInitialized) + // { + // Type[] types = new Type[] {typeof (string)}; + // s_setKey = _wrapperObject.GetMethod("SetKey", types); + // s_setKeyInitialized = true; + // } + + // object[] parameters = new object[] {key}; + // _wrapperObject.CallMethod(s_setKey, parameters); + // _key = key; + // } + //} + + //internal class XmlTypeMapping : XmlMapping + //{ + // public XmlTypeMapping(object o) + // : base(o) + // { + // } + //} + + //internal class XmlMembersMapping : XmlMapping + //{ + // public XmlMembersMapping(object o) + // : base(o) + // { + // } + + // public XmlMemberMapping this[int index] + // { + // get + // { + // object o = _wrapperObject.GetIndexerProperty( + // XmlMappingTypesHelper.XmlMemberMappingType, index); + // return new XmlMemberMapping(o); + // } + // } + //} + + //internal class XmlMemberMapping + //{ + // private IXmlMappingTypeWrapperObject _wrapperObject; + + // public XmlMemberMapping(object o) + // { + // _wrapperObject = XmlMappingTypeWrapperFactory.GetWrapper(o); + // } + + // public string XsdElementName => (string) _wrapperObject.GetProperty("XsdElementName"); + + // public string Namespace => (string) _wrapperObject.GetProperty("Namespace"); + + // public string TypeName => (string) _wrapperObject.GetProperty("TypeName"); + + // public string TypeNamespace => (string) _wrapperObject.GetProperty("TypeNamespace"); + //} + + //internal class XmlReflectionMember : IXmlMappingTypeInnerObject + //{ + // private IXmlMappingTypeWrapperObject _wrapperObject; + + // public XmlReflectionMember() + // { + // _wrapperObject = XmlMappingTypeWrapperFactory.GetWrapper(XmlMappingTypesHelper.XmlReflectionMemberType); + // } + + // public IXmlMappingTypeWrapperObject Object => _wrapperObject; + + // public string MemberName + // { + // get { return (string) _wrapperObject.GetProperty("MemberName"); } + + // set { _wrapperObject.SetProperty("MemberName", value); } + // } + + // public Type MemberType + // { + // get { return (Type) _wrapperObject.GetProperty("MemberType"); } + + // set { _wrapperObject.SetProperty("MemberType", value); } + // } + + // public XmlAttributes XmlAttributes + // { + // get { return (XmlAttributes) _wrapperObject.GetProperty("XmlAttributes"); } + + // set { _wrapperObject.SetProperty("XmlAttributes", value); } + // } + //} + + //internal class XmlMappingTypeWrapperFactory + //{ + // private static IXmlMappingTypeWrapperFactory s_instance; + + // private static IXmlMappingTypeWrapperFactory Instance + // { + // get + // { + // if (s_instance == null) + // { + // s_instance = new XmlMappingTypeReflectionWrapperFactory(); + // } + + // return s_instance; + // } + // } + + // public static IXmlMappingTypeWrapperObject GetWrapper(object innerObject) + // { + // return Instance.GetWrapper(innerObject); + // } + + // public static IXmlMappingTypeWrapperObject GetWrapper(Type type) + // { + // return Instance.GetWrapper(type); + // } + + // public static IXmlMappingTypeWrapperObject GetWrapper(Type type, object[] parameters) + // { + // return Instance.GetWrapper(type, parameters); + // } + //} + + //internal interface IXmlMappingTypeWrapperFactory + //{ + // IXmlMappingTypeWrapperObject GetWrapper(object innerObject); + + // IXmlMappingTypeWrapperObject GetWrapper(Type type); + + // IXmlMappingTypeWrapperObject GetWrapper(Type type, object[] parameters); + //} + + //internal class XmlMappingTypeReflectionWrapperFactory : IXmlMappingTypeWrapperFactory + //{ + // public IXmlMappingTypeWrapperObject GetWrapper(object innerObject) + // { + // return new XmlMappingTypeReflectionWrapper(innerObject); + // } + + // public IXmlMappingTypeWrapperObject GetWrapper(Type type) + // { + // return new XmlMappingTypeReflectionWrapper(type); + // } + + // public IXmlMappingTypeWrapperObject GetWrapper(Type type, object[] parameters) + // { + // return new XmlMappingTypeReflectionWrapper(type, parameters); + // } + //} + + //internal interface IXmlMappingTypeWrapperObject + //{ + // object InnerObject { get; } + + // Type InnerObjectType { get; } + + // object GetProperty(string propertyName); + + // void SetProperty(string propertyName, object value); + + // object GetIndexerProperty(Type returnType, object parameter); + + // object CallMethod(MethodInfo method, object[] parameters); + + // MethodInfo GetMethod(string methodName, Type[] types); + + // Array InitializeArray(Type type, IXmlMappingTypeInnerObject[] objects); + //} + + //internal class XmlMappingTypeReflectionWrapper : IXmlMappingTypeWrapperObject + //{ + // private object _innerObject; + // private Type _innerObjectType; + + // public XmlMappingTypeReflectionWrapper(object innerObject) + // { + // _innerObject = innerObject; + // } + + // public XmlMappingTypeReflectionWrapper(Type type) + // : this(type, null) + // { + // } + + // public XmlMappingTypeReflectionWrapper(Type type, object[] parameters) + // { + // _innerObject = Activator.CreateInstance(type, parameters); + // } + + // public object InnerObject => _innerObject; + + // public Type InnerObjectType + // { + // get + // { + // if (_innerObjectType == null) + // { + // _innerObjectType = _innerObject.GetType(); + // } + + // return _innerObjectType; + // } + // } + + // public object GetProperty(string propertyName) + // { + // PropertyInfo property = this.InnerObjectType.GetProperty(propertyName); + // Contract.Assert(property != null, "Cannot find property: " + propertyName); + + // MethodInfo method = property.GetGetMethod(false); + // Contract.Assert(method != null, "The property does not have Get method: " + propertyName); + + // return CallMethod(method, new object[0]); + // } + + // public void SetProperty(string propertyName, object value) + // { + // PropertyInfo property = this.InnerObjectType.GetProperty(propertyName); + // Contract.Assert(property != null, "Cannot find property: " + propertyName); + + // MethodInfo method = property.GetSetMethod(false); + // Contract.Assert(method != null, "The property does not have Set method: " + propertyName); + + // CallMethod(method, new object[] {value}); + // } + + // public object GetIndexerProperty(Type returnType, object parameter) + // { + // MethodInfo method = this.InnerObjectType.GetProperty("Item", returnType).GetGetMethod(false); + // Contract.Assert(method != null, "Cannot find the indexer property with return type: " + returnType); + + // return CallMethod(method, new object[] {parameter}); + // } + + // public MethodInfo GetMethod(string methodName, Type[] types) + // { + // MethodInfo method = this.InnerObjectType.GetMethod(methodName, types); + // Contract.Assert(method != null, "Cannot find method: " + methodName); + + // return method; + // } + + // public Array InitializeArray(Type type, IXmlMappingTypeInnerObject[] objects) + // { + // return XmlMappingTypesHelper.InitializeArray(type, objects); + // } + + // public object CallMethod(MethodInfo method, object[] parameters) + // { + // Contract.Assert(method != null, "method"); + + // object o = method.Invoke(this.InnerObject, parameters); + + // return o; + // } + //} + + //internal class XmlAttributesHelper + //{ + // internal static XmlAttributes CreateXmlAttributes(MemberInfo member) + // { + // return (XmlAttributes) Activator.CreateInstance(typeof (XmlAttributes), new object[] {member}); + // } + //} + + //internal static class XmlMappingTypesHelper + //{ + // private static string s_xmlReflectionImporterTypeName = + // XmlMappingTypesHelper.GetAssemblyQualifiedTypeName("System.Xml.Serialization.XmlReflectionImporter"); + + // private static string s_xmlMappingTypeName = + // XmlMappingTypesHelper.GetAssemblyQualifiedTypeName("System.Xml.Serialization.XmlMapping"); + + // private static string s_xmlTypeMappingTypeName = + // XmlMappingTypesHelper.GetAssemblyQualifiedTypeName("System.Xml.Serialization.XmlTypeMapping"); + + // private static string s_xmlMembersMappingTypeName = + // XmlMappingTypesHelper.GetAssemblyQualifiedTypeName("System.Xml.Serialization.XmlMembersMapping"); + + // private static string s_xmlMemberMappingTypeName = + // XmlMappingTypesHelper.GetAssemblyQualifiedTypeName("System.Xml.Serialization.XmlMemberMapping"); + + // private static string s_xmlReflectionMemberTypeName = + // XmlMappingTypesHelper.GetAssemblyQualifiedTypeName("System.Xml.Serialization.XmlReflectionMember"); + + // private static string s_xmlSerializerAssemblyName; + + // public static Type XmlReflectionImporterType = Type.GetType(s_xmlReflectionImporterTypeName); + // public static Type XmlMappingType = Type.GetType(s_xmlMappingTypeName); + // public static Type XmlTypeMappingType = Type.GetType(s_xmlTypeMappingTypeName); + // public static Type XmlMembersMappingType = Type.GetType(s_xmlMembersMappingTypeName); + // public static Type XmlMemberMappingType = Type.GetType(s_xmlMemberMappingTypeName); + // public static Type XmlReflectionMemberType = Type.GetType(s_xmlReflectionMemberTypeName); + + // public static Array InitializeArray(Type type, IXmlMappingTypeInnerObject[] objects) + // { + // Array array = Array.CreateInstance(type, objects.Length); + // for (int i = 0; i < objects.Length; i++) + // { + // array.SetValue(objects[i].Object.InnerObject, i); + // } + + // return array; + // } + + // private static string XmlSerializerAssemblyName + // { + // get + // { + // if (s_xmlSerializerAssemblyName == null) + // { + // Type type = typeof (XmlSerializer); + // string asmQualifiedName = type.AssemblyQualifiedName; + // int index = asmQualifiedName.IndexOf(',') + 1; + // s_xmlSerializerAssemblyName = asmQualifiedName.Substring(index); + // } + + // return s_xmlSerializerAssemblyName; + // } + // } + + // private static string GetAssemblyQualifiedTypeName(string typeName) + // { + // return typeName + ", " + XmlSerializerAssemblyName; + // } + //} +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Runtime/SignalGate.cs b/src/CoreWCF.Primitives/src/CoreWCF/Runtime/SignalGate.cs new file mode 100644 index 000000000..a58e57312 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Runtime/SignalGate.cs @@ -0,0 +1,119 @@ +using System; +using System.Threading; + +namespace CoreWCF.Runtime +{ + class SignalGate + { + int state; + + public SignalGate() + { + } + + internal bool IsLocked + { + get + { + return state == GateState.Locked; + } + } + + internal bool IsSignalled + { + get + { + return state == GateState.Signalled; + } + } + + // Returns true if this brings the gate to the Signalled state. + // Transitions - Locked -> SignalPending | Completed before it was unlocked + // Unlocked -> Signaled + public bool Signal() + { + int lastState = state; + if (lastState == GateState.Locked) + { + lastState = Interlocked.CompareExchange(ref state, GateState.SignalPending, GateState.Locked); + } + if (lastState == GateState.Unlocked) + { + state = GateState.Signalled; + return true; + } + + if (lastState != GateState.Locked) + { + ThrowInvalidSignalGateState(); + } + return false; + } + + // Returns true if this brings the gate to the Signalled state. + // Transitions - SignalPending -> Signaled | return the AsyncResult since the callback already + // | completed and provided the result on its thread + // Locked -> Unlocked + public bool Unlock() + { + int lastState = state; + if (lastState == GateState.Locked) + { + lastState = Interlocked.CompareExchange(ref state, GateState.Unlocked, GateState.Locked); + } + if (lastState == GateState.SignalPending) + { + state = GateState.Signalled; + return true; + } + + if (lastState != GateState.Locked) + { + ThrowInvalidSignalGateState(); + } + return false; + } + + // This is factored out to allow Signal and Unlock to be inlined. + void ThrowInvalidSignalGateState() + { + throw Fx.Exception.AsError(new InvalidOperationException(SR.InvalidSemaphoreExit)); + } + + static class GateState + { + public const int Locked = 0; + public const int SignalPending = 1; + public const int Unlocked = 2; + public const int Signalled = 3; + } + } + + class SignalGate : SignalGate + { + T result; + + public SignalGate() + : base() + { + } + + public bool Signal(T result) + { + this.result = result; + return Signal(); + } + + public bool Unlock(out T result) + { + if (Unlock()) + { + result = this.result; + return true; + } + + result = default(T); + return false; + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Runtime/SynchronizedPool.cs b/src/CoreWCF.Primitives/src/CoreWCF/Runtime/SynchronizedPool.cs new file mode 100644 index 000000000..5086982b9 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Runtime/SynchronizedPool.cs @@ -0,0 +1,429 @@ +using System; +using System.Collections.Generic; +using System.Threading; + +namespace CoreWCF.Runtime +{ + // A simple synchronized pool would simply lock a stack and push/pop on return/take. + // + // This implementation tries to reduce locking by exploiting the case where an item + // is taken and returned by the same thread, which turns out to be common in our + // scenarios. + // + // Initially, all the quota is allocated to a global (non-thread-specific) pool, + // which takes locks. As different threads take and return values, we record their IDs, + // and if we detect that a thread is taking and returning "enough" on the same thread, + // then we decide to "promote" the thread. When a thread is promoted, we decrease the + // quota of the global pool by one, and allocate a thread-specific entry for the thread + // to store it's value. Once this entry is allocated, the thread can take and return + // it's value from that entry without taking any locks. Not only does this avoid + // locks, but it affinitizes pooled items to a particular thread. + // + // There are a couple of additional things worth noting: + // + // It is possible for a thread that we have reserved an entry for to exit. This means + // we will still have a entry allocated for it, but the pooled item stored there + // will never be used. After a while, we could end up with a number of these, and + // as a result we would begin to exhaust the quota of the overall pool. To mitigate this + // case, we throw away the entire per-thread pool, and return all the quota back to + // the global pool if we are unable to promote a thread (due to lack of space). Then + // the set of active threads will be re-promoted as they take and return items. + // + // You may notice that the code does not immediately promote a thread, and does not + // immediately throw away the entire per-thread pool when it is unable to promote a + // thread. Instead, it uses counters (based on the number of calls to the pool) + // and a threshold to figure out when to do these operations. In the case where the + // pool to misconfigured to have too few items for the workload, this avoids constant + // promoting and rebuilding of the per thread entries. + // + // You may also notice that we do not use interlocked methods when adjusting statistics. + // Since the statistics are a heuristic as to how often something is happening, they + // do not need to be perfect. + // + class SynchronizedPool where T : class + { + const int maxPendingEntries = 128; + const int maxPromotionFailures = 64; + const int maxReturnsBeforePromotion = 64; + const int maxThreadItemsPerProcessor = 16; + Entry[] _entries; + readonly GlobalPool _globalPool; + readonly int _maxCount; + PendingEntry[] _pending; + int _promotionFailures; + + public SynchronizedPool(int maxCount) + { + int threadCount = maxCount; + int maxThreadCount = maxThreadItemsPerProcessor + SynchronizedPoolHelper.ProcessorCount; + if (threadCount > maxThreadCount) + { + threadCount = maxThreadCount; + } + _maxCount = maxCount; + _entries = new Entry[threadCount]; + _pending = new PendingEntry[4]; + _globalPool = new GlobalPool(maxCount); + } + + object ThisLock + { + get + { + return this; + } + } + + public void Clear() + { + Entry[] entries = _entries; + + for (int i = 0; i < entries.Length; i++) + { + entries[i].value = null; + } + + _globalPool.Clear(); + } + + void HandlePromotionFailure(int thisThreadID) + { + int newPromotionFailures = _promotionFailures + 1; + + if (newPromotionFailures >= maxPromotionFailures) + { + lock (ThisLock) + { + _entries = new Entry[_entries.Length]; + + _globalPool.MaxCount = _maxCount; + } + + PromoteThread(thisThreadID); + } + else + { + _promotionFailures = newPromotionFailures; + } + } + + bool PromoteThread(int thisThreadID) + { + lock (ThisLock) + { + for (int i = 0; i < _entries.Length; i++) + { + int threadID = _entries[i].threadID; + + if (threadID == thisThreadID) + { + return true; + } + else if (threadID == 0) + { + _globalPool.DecrementMaxCount(); + _entries[i].threadID = thisThreadID; + return true; + } + } + } + + return false; + } + + void RecordReturnToGlobalPool(int thisThreadID) + { + PendingEntry[] localPending = _pending; + + for (int i = 0; i < localPending.Length; i++) + { + int threadID = localPending[i].threadID; + + if (threadID == thisThreadID) + { + int newReturnCount = localPending[i].returnCount + 1; + + if (newReturnCount >= maxReturnsBeforePromotion) + { + localPending[i].returnCount = 0; + + if (!PromoteThread(thisThreadID)) + { + HandlePromotionFailure(thisThreadID); + } + } + else + { + localPending[i].returnCount = newReturnCount; + } + break; + } + else if (threadID == 0) + { + break; + } + } + } + + void RecordTakeFromGlobalPool(int thisThreadID) + { + PendingEntry[] localPending = _pending; + + for (int i = 0; i < localPending.Length; i++) + { + int threadID = localPending[i].threadID; + + if (threadID == thisThreadID) + { + return; + } + else if (threadID == 0) + { + lock (localPending) + { + if (localPending[i].threadID == 0) + { + localPending[i].threadID = thisThreadID; + return; + } + } + } + } + + if (localPending.Length >= maxPendingEntries) + { + _pending = new PendingEntry[localPending.Length]; + } + else + { + PendingEntry[] newPending = new PendingEntry[localPending.Length * 2]; + Array.Copy(localPending, newPending, localPending.Length); + _pending = newPending; + } + } + + public bool Return(T value) + { + int thisThreadID = Thread.CurrentThread.ManagedThreadId; + + if (thisThreadID == 0) + { + return false; + } + + if (ReturnToPerThreadPool(thisThreadID, value)) + { + return true; + } + + return ReturnToGlobalPool(thisThreadID, value); + } + + bool ReturnToPerThreadPool(int thisThreadID, T value) + { + Entry[] entries = _entries; + + for (int i = 0; i < entries.Length; i++) + { + int threadID = entries[i].threadID; + + if (threadID == thisThreadID) + { + if (entries[i].value == null) + { + entries[i].value = value; + return true; + } + else + { + return false; + } + } + else if (threadID == 0) + { + break; + } + } + + return false; + } + + bool ReturnToGlobalPool(int thisThreadID, T value) + { + RecordReturnToGlobalPool(thisThreadID); + + return _globalPool.Return(value); + } + + public T Take() + { + int thisThreadID = Thread.CurrentThread.ManagedThreadId; + + if (thisThreadID == 0) + { + return null; + } + + T value = TakeFromPerThreadPool(thisThreadID); + + if (value != null) + { + return value; + } + + return TakeFromGlobalPool(thisThreadID); + } + + T TakeFromPerThreadPool(int thisThreadID) + { + Entry[] entries = _entries; + + for (int i = 0; i < entries.Length; i++) + { + int threadID = entries[i].threadID; + + if (threadID == thisThreadID) + { + T value = entries[i].value; + + if (value != null) + { + entries[i].value = null; + return value; + } + else + { + return null; + } + } + else if (threadID == 0) + { + break; + } + } + + return null; + } + + T TakeFromGlobalPool(int thisThreadID) + { + RecordTakeFromGlobalPool(thisThreadID); + + return _globalPool.Take(); + } + + struct Entry + { + public int threadID; + public T value; + } + + struct PendingEntry + { + public int returnCount; + public int threadID; + } + + static class SynchronizedPoolHelper + { + public static readonly int ProcessorCount = GetProcessorCount(); + + static int GetProcessorCount() + { + return Environment.ProcessorCount; + } + } + + class GlobalPool + { + readonly Stack _items; + + int _maxCount; + + public GlobalPool(int maxCount) + { + _items = new Stack(); + _maxCount = maxCount; + } + + public int MaxCount + { + get + { + return _maxCount; + } + set + { + lock (ThisLock) + { + while (_items.Count > value) + { + _items.Pop(); + } + _maxCount = value; + } + } + } + + object ThisLock + { + get + { + return this; + } + } + + public void DecrementMaxCount() + { + lock (ThisLock) + { + if (_items.Count == _maxCount) + { + _items.Pop(); + } + _maxCount--; + } + } + + public T Take() + { + if (_items.Count > 0) + { + lock (ThisLock) + { + if (_items.Count > 0) + { + return _items.Pop(); + } + } + } + return null; + } + + public bool Return(T value) + { + if (_items.Count < MaxCount) + { + lock (ThisLock) + { + if (_items.Count < MaxCount) + { + _items.Push(value); + return true; + } + } + } + return false; + } + + public void Clear() + { + lock (ThisLock) + { + _items.Clear(); + } + } + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Security/ChannelProtectionRequirements.cs b/src/CoreWCF.Primitives/src/CoreWCF/Security/ChannelProtectionRequirements.cs new file mode 100644 index 000000000..d99b47aea --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Security/ChannelProtectionRequirements.cs @@ -0,0 +1,328 @@ +using System; +using System.Xml; +using CoreWCF.Runtime; +using CoreWCF.Channels; +using CoreWCF.Description; +using System.Net.Security; + +namespace CoreWCF.Security +{ + // This class is used to describe the message security requirements. It's only here as a placeholder + // to allow minimal changes to ported code. + internal class ChannelProtectionRequirements + { + bool _isReadOnly; + + public ChannelProtectionRequirements() + { + IncomingSignatureParts = new ScopedMessagePartSpecification(); + IncomingEncryptionParts = new ScopedMessagePartSpecification(); + OutgoingSignatureParts = new ScopedMessagePartSpecification(); + OutgoingEncryptionParts = new ScopedMessagePartSpecification(); + } + + public bool IsReadOnly => _isReadOnly; + + public ChannelProtectionRequirements(ChannelProtectionRequirements other) + { + if (other == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(other)); + + IncomingSignatureParts = new ScopedMessagePartSpecification(other.IncomingSignatureParts); + IncomingEncryptionParts = new ScopedMessagePartSpecification(other.IncomingEncryptionParts); + OutgoingSignatureParts = new ScopedMessagePartSpecification(other.OutgoingSignatureParts); + OutgoingEncryptionParts = new ScopedMessagePartSpecification(other.OutgoingEncryptionParts); + } + + internal ChannelProtectionRequirements(ChannelProtectionRequirements other, ProtectionLevel newBodyProtectionLevel) + { + if (other == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(other)); + + IncomingSignatureParts = new ScopedMessagePartSpecification(other.IncomingSignatureParts, newBodyProtectionLevel != ProtectionLevel.None); + IncomingEncryptionParts = new ScopedMessagePartSpecification(other.IncomingEncryptionParts, newBodyProtectionLevel == ProtectionLevel.EncryptAndSign); + OutgoingSignatureParts = new ScopedMessagePartSpecification(other.OutgoingSignatureParts, newBodyProtectionLevel != ProtectionLevel.None); + OutgoingEncryptionParts = new ScopedMessagePartSpecification(other.OutgoingEncryptionParts, newBodyProtectionLevel == ProtectionLevel.EncryptAndSign); + } + + + public ScopedMessagePartSpecification IncomingSignatureParts { get; } + + public ScopedMessagePartSpecification IncomingEncryptionParts { get; } + + public ScopedMessagePartSpecification OutgoingSignatureParts { get; } + + public ScopedMessagePartSpecification OutgoingEncryptionParts { get; } + + public void Add(ChannelProtectionRequirements protectionRequirements) + { + Add(protectionRequirements, false); + } + + public void Add(ChannelProtectionRequirements protectionRequirements, bool channelScopeOnly) + { + if (protectionRequirements == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(protectionRequirements)); + + if (protectionRequirements.IncomingSignatureParts != null) + IncomingSignatureParts.AddParts(protectionRequirements.IncomingSignatureParts.ChannelParts); + if (protectionRequirements.IncomingEncryptionParts != null) + IncomingEncryptionParts.AddParts(protectionRequirements.IncomingEncryptionParts.ChannelParts); + if (protectionRequirements.OutgoingSignatureParts != null) + OutgoingSignatureParts.AddParts(protectionRequirements.OutgoingSignatureParts.ChannelParts); + if (protectionRequirements.OutgoingEncryptionParts != null) + OutgoingEncryptionParts.AddParts(protectionRequirements.OutgoingEncryptionParts.ChannelParts); + + if (!channelScopeOnly) + { + AddActionParts(IncomingSignatureParts, protectionRequirements.IncomingSignatureParts); + AddActionParts(IncomingEncryptionParts, protectionRequirements.IncomingEncryptionParts); + AddActionParts(OutgoingSignatureParts, protectionRequirements.OutgoingSignatureParts); + AddActionParts(OutgoingEncryptionParts, protectionRequirements.OutgoingEncryptionParts); + } + } + + static void AddActionParts(ScopedMessagePartSpecification to, ScopedMessagePartSpecification from) + { + foreach (var action in from.Actions) + { + MessagePartSpecification p; + if (from.TryGetParts(action, true, out p)) + to.AddParts(p, action); + } + } + + public void MakeReadOnly() + { + if (!_isReadOnly) + { + IncomingSignatureParts.MakeReadOnly(); + IncomingEncryptionParts.MakeReadOnly(); + OutgoingSignatureParts.MakeReadOnly(); + OutgoingEncryptionParts.MakeReadOnly(); + _isReadOnly = true; + } + } + + internal static ChannelProtectionRequirements CreateFromContract(ContractDescription contract, ISecurityCapabilities bindingElement) + { + return CreateFromContract(contract, bindingElement.SupportedRequestProtectionLevel, bindingElement.SupportedResponseProtectionLevel); + } + + static MessagePartSpecification UnionMessagePartSpecifications(ScopedMessagePartSpecification actionParts) + { + var result = new MessagePartSpecification(false); + foreach (var action in actionParts.Actions) + { + MessagePartSpecification parts; + if (actionParts.TryGetParts(action, out parts)) + { + if (parts.IsBodyIncluded) + { + result.IsBodyIncluded = true; + } + foreach (var headerType in parts.HeaderTypes) + { + if (!result.IsHeaderIncluded(headerType.Name, headerType.Namespace)) + { + result.HeaderTypes.Add(headerType); + } + } + } + } + return result; + } + + internal static ChannelProtectionRequirements CreateFromContractAndUnionResponseProtectionRequirements(ContractDescription contract, ISecurityCapabilities bindingElement) + { + var contractRequirements = CreateFromContract(contract, bindingElement.SupportedRequestProtectionLevel, bindingElement.SupportedResponseProtectionLevel); + var result = new ChannelProtectionRequirements(); + + result.OutgoingEncryptionParts.AddParts(UnionMessagePartSpecifications(contractRequirements.OutgoingEncryptionParts), MessageHeaders.WildcardAction); + result.OutgoingSignatureParts.AddParts(UnionMessagePartSpecifications(contractRequirements.OutgoingSignatureParts), MessageHeaders.WildcardAction); + contractRequirements.IncomingEncryptionParts.CopyTo(result.IncomingEncryptionParts); + contractRequirements.IncomingSignatureParts.CopyTo(result.IncomingSignatureParts); + return result; + } + + internal static ChannelProtectionRequirements CreateFromContract(ContractDescription contract, ProtectionLevel defaultRequestProtectionLevel, ProtectionLevel defaultResponseProtectionLevel) + { + if (contract == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(contract)); + + var requirements = new ChannelProtectionRequirements(); + + var contractScopeDefaultRequestProtectionLevel = ProtectionLevel.None; + var contractScopeDefaultResponseProtectionLevel = ProtectionLevel.None; + if (contract.HasProtectionLevel) // Currently always false + { + //contractScopeDefaultRequestProtectionLevel = contract.ProtectionLevel; + //contractScopeDefaultResponseProtectionLevel = contract.ProtectionLevel; + } + else + { + contractScopeDefaultRequestProtectionLevel = defaultRequestProtectionLevel; + contractScopeDefaultResponseProtectionLevel = defaultResponseProtectionLevel; + } + + foreach (var operation in contract.Operations) + { + var operationScopeDefaultRequestProtectionLevel = ProtectionLevel.None; + var operationScopeDefaultResponseProtectionLevel = ProtectionLevel.None; + if (operation.HasProtectionLevel) // Currently always false + { + //operationScopeDefaultRequestProtectionLevel = operation.ProtectionLevel; + //operationScopeDefaultResponseProtectionLevel = operation.ProtectionLevel; + } + else + { + operationScopeDefaultRequestProtectionLevel = contractScopeDefaultRequestProtectionLevel; + operationScopeDefaultResponseProtectionLevel = contractScopeDefaultResponseProtectionLevel; + } + foreach (var message in operation.Messages) + { + var messageScopeDefaultProtectionLevel = ProtectionLevel.None; + if (message.HasProtectionLevel) + { + //messageScopeDefaultProtectionLevel = message.ProtectionLevel; + } + else if (message.Direction == MessageDirection.Input) + { + messageScopeDefaultProtectionLevel = operationScopeDefaultRequestProtectionLevel; + } + else + { + messageScopeDefaultProtectionLevel = operationScopeDefaultResponseProtectionLevel; + } + + var signedParts = new MessagePartSpecification(); + var encryptedParts = new MessagePartSpecification(); + + // determine header protection requirements for message + foreach (var header in message.Headers) + { + AddHeaderProtectionRequirements(header, signedParts, encryptedParts, messageScopeDefaultProtectionLevel); + } + + // determine body protection requirements for message + ProtectionLevel bodyProtectionLevel; + if (message.Body.Parts.Count > 0) + { + // initialize the body protection level to none. all the body parts will be + // unioned to get the effective body protection level + bodyProtectionLevel = ProtectionLevel.None; + } + else if (message.Body.ReturnValue != null) + { + if (!(message.Body.ReturnValue.GetType().Equals(typeof(MessagePartDescription)))) + { + Fx.Assert("Only body return values are supported currently"); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.OnlyBodyReturnValuesSupported)); + } + bodyProtectionLevel = messageScopeDefaultProtectionLevel; // MessagePartDescription.HasProtectionLevel currently always false + //MessagePartDescription desc = message.Body.ReturnValue; + //bodyProtectionLevel = desc.HasProtectionLevel ? desc.ProtectionLevel : messageScopeDefaultProtectionLevel; + } + else + { + bodyProtectionLevel = messageScopeDefaultProtectionLevel; + } + + // determine body protection requirements for message + if (message.Body.Parts.Count > 0) + { + foreach (var body in message.Body.Parts) + { + var partProtectionLevel = messageScopeDefaultProtectionLevel; // MessagePartDescription.HasProtectionLevel currently always false + //ProtectionLevel partProtectionLevel = body.HasProtectionLevel ? body.ProtectionLevel : messageScopeDefaultProtectionLevel; + bodyProtectionLevel = ProtectionLevelHelper.Max(bodyProtectionLevel, partProtectionLevel); + if (bodyProtectionLevel == ProtectionLevel.EncryptAndSign) + break; + } + } + if (bodyProtectionLevel != ProtectionLevel.None) + { + signedParts.IsBodyIncluded = true; + if (bodyProtectionLevel == ProtectionLevel.EncryptAndSign) + encryptedParts.IsBodyIncluded = true; + } + + // add requirements for message + if (message.Direction == MessageDirection.Input) + { + requirements.IncomingSignatureParts.AddParts(signedParts, message.Action); + requirements.IncomingEncryptionParts.AddParts(encryptedParts, message.Action); + } + else + { + requirements.OutgoingSignatureParts.AddParts(signedParts, message.Action); + requirements.OutgoingEncryptionParts.AddParts(encryptedParts, message.Action); + } + } + if (operation.Faults != null) + { + if (operation.IsServerInitiated()) + { + AddFaultProtectionRequirements(operation.Faults, requirements, operationScopeDefaultRequestProtectionLevel, true); + } + else + { + AddFaultProtectionRequirements(operation.Faults, requirements, operationScopeDefaultResponseProtectionLevel, false); + } + } + } + + return requirements; + } + + static void AddHeaderProtectionRequirements(MessageHeaderDescription header, MessagePartSpecification signedParts, + MessagePartSpecification encryptedParts, ProtectionLevel defaultProtectionLevel) + { + var p = defaultProtectionLevel; //header.HasProtectionLevel currently is always false; + //ProtectionLevel p = header.HasProtectionLevel ? header.ProtectionLevel : defaultProtectionLevel; + + if (p != ProtectionLevel.None) + { + var headerName = new XmlQualifiedName(header.Name, header.Namespace); + signedParts.HeaderTypes.Add(headerName); + if (p == ProtectionLevel.EncryptAndSign) + encryptedParts.HeaderTypes.Add(headerName); + } + } + + static void AddFaultProtectionRequirements(FaultDescriptionCollection faults, ChannelProtectionRequirements requirements, ProtectionLevel defaultProtectionLevel, bool addToIncoming) + { + if (faults == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(faults)); + if (requirements == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(requirements)); + + foreach (var fault in faults) + { + var signedParts = new MessagePartSpecification(); + var encryptedParts = new MessagePartSpecification(); + var p = defaultProtectionLevel; // FaultDescription.HasProtectionLevel currently is always false + //ProtectionLevel p = fault.HasProtectionLevel ? fault.ProtectionLevel : defaultProtectionLevel; + if (p != ProtectionLevel.None) + { + signedParts.IsBodyIncluded = true; + if (p == ProtectionLevel.EncryptAndSign) + { + encryptedParts.IsBodyIncluded = true; + } + } + if (addToIncoming) + { + requirements.IncomingSignatureParts.AddParts(signedParts, fault.Action); + requirements.IncomingEncryptionParts.AddParts(encryptedParts, fault.Action); + } + else + { + requirements.OutgoingSignatureParts.AddParts(signedParts, fault.Action); + requirements.OutgoingEncryptionParts.AddParts(encryptedParts, fault.Action); + } + } + } + + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Security/IEndpointIdentityProvider.cs b/src/CoreWCF.Primitives/src/CoreWCF/Security/IEndpointIdentityProvider.cs new file mode 100644 index 000000000..6d7d6125b --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Security/IEndpointIdentityProvider.cs @@ -0,0 +1,12 @@ +using CoreWCF.IdentityModel.Selectors; +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoreWCF.Security +{ + internal interface IEndpointIdentityProvider + { + EndpointIdentity GetIdentityOfSelf(SecurityTokenRequirement tokenRequirement); + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Security/IdentityVerifier.cs b/src/CoreWCF.Primitives/src/CoreWCF/Security/IdentityVerifier.cs new file mode 100644 index 000000000..1da2d014a --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Security/IdentityVerifier.cs @@ -0,0 +1,328 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Globalization; +using CoreWCF.IdentityModel.Policy; +using CoreWCF.Channels; +using CoreWCF.IdentityModel.Claims; +using CoreWCF.Runtime.Diagnostics; +using System.Security.Principal; +using CoreWCF.Security.Tokens; + +namespace CoreWCF.Security +{ + internal abstract class IdentityVerifier + { + protected IdentityVerifier() + { + // empty + } + + public static IdentityVerifier CreateDefault() + { + return DefaultIdentityVerifier.Instance; + } + + internal bool CheckAccess(EndpointAddress reference, Message message) + { + if (reference == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reference"); + if (message == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("message"); + + EndpointIdentity identity; + if (!TryGetIdentity(reference, out identity)) + return false; + + //SecurityMessageProperty securityContextProperty = null; + //if (message.Properties != null) + // securityContextProperty = message.Properties.Security; + + //if (securityContextProperty == null || securityContextProperty.ServiceSecurityContext == null) + // return false; + + //return this.CheckAccess(identity, securityContextProperty.ServiceSecurityContext.AuthorizationContext); + return false; + } + + public abstract bool CheckAccess(EndpointIdentity identity, AuthorizationContext authContext); + + public abstract bool TryGetIdentity(EndpointAddress reference, out EndpointIdentity identity); + + static void AdjustAddress(ref EndpointAddress reference, Uri via) + { + // if we don't have an identity and we have differing Uris, we should use the Via + if (reference.Identity == null && reference.Uri != via) + { + reference = new EndpointAddress(via); + } + } + + internal bool TryGetIdentity(EndpointAddress reference, Uri via, out EndpointIdentity identity) + { + AdjustAddress(ref reference, via); + return TryGetIdentity(reference, out identity); + } + + internal void EnsureIncomingIdentity(EndpointAddress serviceReference, AuthorizationContext authorizationContext) + { + EnsureIdentity(serviceReference, authorizationContext, SR.IdentityCheckFailedForIncomingMessage); + } + + internal void EnsureOutgoingIdentity(EndpointAddress serviceReference, Uri via, AuthorizationContext authorizationContext) + { + AdjustAddress(ref serviceReference, via); + EnsureIdentity(serviceReference, authorizationContext, SR.IdentityCheckFailedForOutgoingMessage); + } + + internal void EnsureOutgoingIdentity(EndpointAddress serviceReference, ReadOnlyCollection authorizationPolicies) + { + if (authorizationPolicies == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("authorizationPolicies"); + } + AuthorizationContext ac = AuthorizationContext.CreateDefaultAuthorizationContext(authorizationPolicies); + EnsureIdentity(serviceReference, ac, SR.IdentityCheckFailedForOutgoingMessage); + } + + void EnsureIdentity(EndpointAddress serviceReference, AuthorizationContext authorizationContext, string errorString) + { + if (authorizationContext == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("authorizationContext"); + } + EndpointIdentity identity; + if (!TryGetIdentity(serviceReference, out identity)) + { + //SecurityTraceRecordHelper.TraceIdentityVerificationFailure(identity, authorizationContext, this.GetType()); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new MessageSecurityException(SR.Format(errorString, identity, serviceReference))); + } + else + { + if (!CheckAccess(identity, authorizationContext)) + { + // CheckAccess performs a Trace on failure, no need to do it twice + Exception e = CreateIdentityCheckException(identity, authorizationContext, errorString, serviceReference); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(e); + } + } + } + + Exception CreateIdentityCheckException(EndpointIdentity identity, AuthorizationContext authorizationContext, string errorString, EndpointAddress serviceReference) + { + Exception result; + + if (identity.IdentityClaim != null + && identity.IdentityClaim.ClaimType == ClaimTypes.Dns + && identity.IdentityClaim.Right == Rights.PossessProperty + && identity.IdentityClaim.Resource is string) + { + string expectedDnsName = (string)identity.IdentityClaim.Resource; + string actualDnsName = null; + for (int i = 0; i < authorizationContext.ClaimSets.Count; ++i) + { + ClaimSet claimSet = authorizationContext.ClaimSets[i]; + foreach (Claim claim in claimSet.FindClaims(ClaimTypes.Dns, Rights.PossessProperty)) + { + if (claim.Resource is string) + { + actualDnsName = (string)claim.Resource; + break; + } + } + if (actualDnsName != null) + { + break; + } + } + if (SR.IdentityCheckFailedForIncomingMessage.Equals(errorString)) + { + if (actualDnsName == null) + { + result = new MessageSecurityException(SR.Format(SR.DnsIdentityCheckFailedForIncomingMessageLackOfDnsClaim, expectedDnsName)); + } + else + { + result = new MessageSecurityException(SR.Format(SR.DnsIdentityCheckFailedForIncomingMessage, expectedDnsName, actualDnsName)); + } + } + else if (SR.IdentityCheckFailedForOutgoingMessage.Equals(errorString)) + { + if (actualDnsName == null) + { + result = new MessageSecurityException(SR.Format(SR.DnsIdentityCheckFailedForOutgoingMessageLackOfDnsClaim, expectedDnsName)); + } + else + { + result = new MessageSecurityException(SR.Format(SR.DnsIdentityCheckFailedForOutgoingMessage, expectedDnsName, actualDnsName)); + } + } + else + { + result = new MessageSecurityException(SR.Format(errorString, identity, serviceReference)); + } + } + else + { + result = new MessageSecurityException(SR.Format(errorString, identity, serviceReference)); + } + + return result; + } + + class DefaultIdentityVerifier : IdentityVerifier + { + public static DefaultIdentityVerifier Instance { get; } = new DefaultIdentityVerifier(); + + public override bool TryGetIdentity(EndpointAddress reference, out EndpointIdentity identity) + { + if (reference == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reference"); + + identity = reference.Identity; + + if (identity == null) + { + identity = TryCreateDnsIdentity(reference); + } + + if (identity == null) + { + SecurityTraceRecordHelper.TraceIdentityDeterminationFailure(reference, typeof(DefaultIdentityVerifier)); + return false; + } + else + { + SecurityTraceRecordHelper.TraceIdentityDeterminationSuccess(reference, identity, typeof(DefaultIdentityVerifier)); + return true; + } + } + + EndpointIdentity TryCreateDnsIdentity(EndpointAddress reference) + { + Uri toAddress = reference.Uri; + + if (!toAddress.IsAbsoluteUri) + return null; + + return EndpointIdentity.CreateDnsIdentity(toAddress.DnsSafeHost); + } + + SecurityIdentifier GetSecurityIdentifier(Claim claim) + { + // if the incoming claim is a SID and the EndpointIdentity is UPN/SPN/DNS, try to find the SID corresponding to + // the UPN/SPN/DNS (transactions case) + if (claim.Resource is WindowsIdentity) + return ((WindowsIdentity)claim.Resource).User; + else if (claim.Resource is WindowsSidIdentity) + return ((WindowsSidIdentity)claim.Resource).SecurityIdentifier; + return claim.Resource as SecurityIdentifier; + } + + Claim CheckDnsEquivalence(ClaimSet claimSet, string expectedSpn) + { + // host/ satisfies the DNS identity claim + IEnumerable claims = claimSet.FindClaims(ClaimTypes.Spn, Rights.PossessProperty); + foreach (Claim claim in claims) + { + if (expectedSpn.Equals((string)claim.Resource, StringComparison.OrdinalIgnoreCase)) + { + return claim; + } + } + return null; + } + + Claim CheckSidEquivalence(SecurityIdentifier identitySid, ClaimSet claimSet) + { + foreach (Claim claim in claimSet) + { + SecurityIdentifier sid = GetSecurityIdentifier(claim); + if (sid != null) + { + if (identitySid.Equals(sid)) + { + return claim; + } + } + } + return null; + } + + public override bool CheckAccess(EndpointIdentity identity, AuthorizationContext authContext) + { + //EventTraceActivity eventTraceActivity = null; + + if (identity == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("identity"); + + if (authContext == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("authContext"); + + + //if (FxTrace.Trace.IsEnd2EndActivityTracingEnabled) + //{ + // eventTraceActivity = EventTraceActivityHelper.TryExtractActivity((OperationContext.Current != null) ? OperationContext.Current.IncomingMessage : null); + //} + + for (int i = 0; i < authContext.ClaimSets.Count; ++i) + { + ClaimSet claimSet = authContext.ClaimSets[i]; + if (claimSet.ContainsClaim(identity.IdentityClaim)) + { + //SecurityTraceRecordHelper.TraceIdentityVerificationSuccess(eventTraceActivity, identity, identity.IdentityClaim, this.GetType()); + return true; + } + + // try Claim equivalence + string expectedSpn = null; + if (ClaimTypes.Dns.Equals(identity.IdentityClaim.ClaimType)) + { + expectedSpn = string.Format(CultureInfo.InvariantCulture, "host/{0}", (string)identity.IdentityClaim.Resource); + Claim claim = CheckDnsEquivalence(claimSet, expectedSpn); + if (claim != null) + { + //SecurityTraceRecordHelper.TraceIdentityVerificationSuccess(eventTraceActivity, identity, claim, this.GetType()); + return true; + } + } + // Allow a Sid claim to support UPN, and SPN identities + SecurityIdentifier identitySid = null; + if (ClaimTypes.Sid.Equals(identity.IdentityClaim.ClaimType)) + { + identitySid = GetSecurityIdentifier(identity.IdentityClaim); + } + else if (ClaimTypes.Upn.Equals(identity.IdentityClaim.ClaimType)) + { + identitySid = ((UpnEndpointIdentity)identity).GetUpnSid(); + } + else if (ClaimTypes.Spn.Equals(identity.IdentityClaim.ClaimType)) + { + identitySid = ((SpnEndpointIdentity)identity).GetSpnSid(); + } + else if (ClaimTypes.Dns.Equals(identity.IdentityClaim.ClaimType)) + { + identitySid = new SpnEndpointIdentity(expectedSpn).GetSpnSid(); + } + if (identitySid != null) + { + Claim claim = CheckSidEquivalence(identitySid, claimSet); + if (claim != null) + { + //SecurityTraceRecordHelper.TraceIdentityVerificationSuccess(eventTraceActivity, identity, claim, this.GetType()); + return true; + } + } + } + SecurityTraceRecordHelper.TraceIdentityVerificationFailure(identity, authContext, GetType()); + //if (TD.SecurityIdentityVerificationFailureIsEnabled()) + //{ + // TD.SecurityIdentityVerificationFailure(eventTraceActivity); + //} + + return false; + } + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Security/MessagePartSpecification.cs b/src/CoreWCF.Primitives/src/CoreWCF/Security/MessagePartSpecification.cs new file mode 100644 index 000000000..c6878bd05 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Security/MessagePartSpecification.cs @@ -0,0 +1,227 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Xml; +using CoreWCF.Channels; + +namespace CoreWCF.Security +{ + internal class MessagePartSpecification + { + List _headerTypes; + bool _isBodyIncluded; + bool _isReadOnly; + static MessagePartSpecification _noParts; + + public ICollection HeaderTypes + { + get + { + if (_headerTypes == null) + { + _headerTypes = new List(); + } + + if (_isReadOnly) + { + return new ReadOnlyCollection(_headerTypes); + } + else + { + return _headerTypes; + } + } + } + + internal bool HasHeaders + { + get { return _headerTypes != null && _headerTypes.Count > 0; } + } + + public bool IsBodyIncluded + { + get + { + return _isBodyIncluded; + } + set + { + if (_isReadOnly) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.ObjectIsReadOnly)); + + _isBodyIncluded = value; + } + } + + public bool IsReadOnly + { + get + { + return _isReadOnly; + } + } + + static public MessagePartSpecification NoParts + { + get + { + if (_noParts == null) + { + MessagePartSpecification parts = new MessagePartSpecification(); + parts.MakeReadOnly(); + _noParts = parts; + } + return _noParts; + } + } + + public void Clear() + { + if (_isReadOnly) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.ObjectIsReadOnly)); + + if (_headerTypes != null) + _headerTypes.Clear(); + _isBodyIncluded = false; + } + + public void Union(MessagePartSpecification specification) + { + if (_isReadOnly) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.ObjectIsReadOnly)); + if (specification == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("specification"); + + _isBodyIncluded |= specification.IsBodyIncluded; + + List headerTypes = specification._headerTypes; + if (headerTypes != null && headerTypes.Count > 0) + { + if (_headerTypes == null) + { + _headerTypes = new List(headerTypes.Count); + } + + for (int i = 0; i < headerTypes.Count; i++) + { + XmlQualifiedName qname = headerTypes[i]; + _headerTypes.Add(qname); + } + } + } + + public void MakeReadOnly() + { + if (_isReadOnly) + return; + + if (_headerTypes != null) + { + List noDuplicates = new List(_headerTypes.Count); + for (int i = 0; i < _headerTypes.Count; i++) + { + XmlQualifiedName qname = _headerTypes[i]; + if (qname != null) + { + bool include = true; + for (int j = 0; j < noDuplicates.Count; j++) + { + XmlQualifiedName qname1 = noDuplicates[j]; + + if (qname.Name == qname1.Name && qname.Namespace == qname1.Namespace) + { + include = false; + break; + } + } + + if (include) + noDuplicates.Add(qname); + } + } + + _headerTypes = noDuplicates; + } + + _isReadOnly = true; + } + + public MessagePartSpecification() + { + // empty + } + + public MessagePartSpecification(bool isBodyIncluded) + { + _isBodyIncluded = isBodyIncluded; + } + + public MessagePartSpecification(params XmlQualifiedName[] headerTypes) + : this(false, headerTypes) + { + // empty + } + + public MessagePartSpecification(bool isBodyIncluded, params XmlQualifiedName[] headerTypes) + { + _isBodyIncluded = isBodyIncluded; + if (headerTypes != null && headerTypes.Length > 0) + { + _headerTypes = new List(headerTypes.Length); + for (int i = 0; i < headerTypes.Length; i++) + { + _headerTypes.Add(headerTypes[i]); + } + } + } + + internal bool IsHeaderIncluded(MessageHeader header) + { + if (header == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("header"); + + return IsHeaderIncluded(header.Name, header.Namespace); + } + + internal bool IsHeaderIncluded(string name, string ns) + { + if (name == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("name"); + if (ns == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("ns"); + + if (_headerTypes != null) + { + for (int i = 0; i < _headerTypes.Count; i++) + { + XmlQualifiedName qname = _headerTypes[i]; + // Name is an optional attribute. If not present, compare with only the namespace. + if (string.IsNullOrEmpty(qname.Name)) + { + if (qname.Namespace == ns) + { + return true; + } + } + else + { + if (qname.Name == name && qname.Namespace == ns) + { + return true; + } + } + } + } + + return false; + } + + internal bool IsEmpty() + { + if (_headerTypes != null && _headerTypes.Count > 0) + return false; + + return !IsBodyIncluded; + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Security/MessageSecurityException.cs b/src/CoreWCF.Primitives/src/CoreWCF/Security/MessageSecurityException.cs new file mode 100644 index 000000000..6c7f3026a --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Security/MessageSecurityException.cs @@ -0,0 +1,58 @@ +using System; +using CoreWCF.Channels; + +namespace CoreWCF.Security +{ + [Serializable] + public class MessageSecurityException : CommunicationException + { + MessageFault fault; + bool isReplay = false; + + public MessageSecurityException() + : base() + { + } + + public MessageSecurityException(string message) + : base(message) + { + } + + public MessageSecurityException(string message, Exception innerException) + : base(message, innerException) + { + } + + protected MessageSecurityException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) + : base(info, context) + { + } + + internal MessageSecurityException(string message, Exception innerException, MessageFault fault) + : base(message, innerException) + { + this.fault = fault; + } + + internal MessageSecurityException(string message, bool isReplay) + : base(message) + { + this.isReplay = isReplay; + } + + internal bool ReplayDetected + { + get + { + return isReplay; + } + } + + internal MessageFault Fault + { + get { return fault; } + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Security/ScopedMessagePartSpecification.cs b/src/CoreWCF.Primitives/src/CoreWCF/Security/ScopedMessagePartSpecification.cs new file mode 100644 index 000000000..0efc19f4c --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Security/ScopedMessagePartSpecification.cs @@ -0,0 +1,204 @@ +using System; +using System.Collections.Generic; +using System.Xml; +using CoreWCF.Channels; + +namespace CoreWCF.Security +{ + internal class ScopedMessagePartSpecification + { + MessagePartSpecification channelParts; + Dictionary actionParts; + Dictionary readOnlyNormalizedActionParts; + bool isReadOnly; + + public ScopedMessagePartSpecification() + { + channelParts = new MessagePartSpecification(); + actionParts = new Dictionary(); + } + + public ICollection Actions + { + get + { + return actionParts.Keys; + } + } + + public MessagePartSpecification ChannelParts + { + get + { + return channelParts; + } + } + + public bool IsReadOnly + { + get + { + return isReadOnly; + } + } + + public ScopedMessagePartSpecification(ScopedMessagePartSpecification other) + : this() + { + if (other == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException(nameof(other))); + + channelParts.Union(other.channelParts); + if (other.actionParts != null) + { + foreach (string action in other.actionParts.Keys) + { + MessagePartSpecification p = new MessagePartSpecification(); + p.Union(other.actionParts[action]); + actionParts[action] = p; + } + } + } + + internal ScopedMessagePartSpecification(ScopedMessagePartSpecification other, bool newIncludeBody) + : this(other) + { + channelParts.IsBodyIncluded = newIncludeBody; + foreach (string action in actionParts.Keys) + actionParts[action].IsBodyIncluded = newIncludeBody; + } + + public void AddParts(MessagePartSpecification parts) + { + if (parts == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException(nameof(parts))); + + ThrowIfReadOnly(); + + channelParts.Union(parts); + } + + public void AddParts(MessagePartSpecification parts, string action) + { + if (action == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException(nameof(action))); + if (parts == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException(nameof(parts))); + + ThrowIfReadOnly(); + + if (!actionParts.ContainsKey(action)) + actionParts[action] = new MessagePartSpecification(); + actionParts[action].Union(parts); + } + + internal void AddParts(MessagePartSpecification parts, XmlDictionaryString action) + { + if (action == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException(nameof(action))); + AddParts(parts, action.Value); + } + + internal bool IsEmpty() + { + bool result; + if (!channelParts.IsEmpty()) + { + result = false; + } + else + { + result = true; + foreach (string action in Actions) + { + MessagePartSpecification parts; + if (TryGetParts(action, true, out parts)) + { + if (!parts.IsEmpty()) + { + result = false; + break; + } + } + } + } + + return result; + } + + public bool TryGetParts(string action, bool excludeChannelScope, out MessagePartSpecification parts) + { + if (action == null) + action = MessageHeaders.WildcardAction; + parts = null; + + if (isReadOnly) + { + if (readOnlyNormalizedActionParts.ContainsKey(action)) + if (excludeChannelScope) + parts = actionParts[action]; + else + parts = readOnlyNormalizedActionParts[action]; + } + else if (actionParts.ContainsKey(action)) + { + MessagePartSpecification p = new MessagePartSpecification(); + p.Union(actionParts[action]); + if (!excludeChannelScope) + p.Union(channelParts); + parts = p; + } + + return parts != null; + } + + internal void CopyTo(ScopedMessagePartSpecification target) + { + if (target == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("target"); + } + target.ChannelParts.IsBodyIncluded = ChannelParts.IsBodyIncluded; + foreach (XmlQualifiedName headerType in ChannelParts.HeaderTypes) + { + if (!target.channelParts.IsHeaderIncluded(headerType.Name, headerType.Namespace)) + { + target.ChannelParts.HeaderTypes.Add(headerType); + } + } + foreach (string action in actionParts.Keys) + { + target.AddParts(actionParts[action], action); + } + } + + public bool TryGetParts(string action, out MessagePartSpecification parts) + { + return TryGetParts(action, false, out parts); + } + + public void MakeReadOnly() + { + if (!isReadOnly) + { + readOnlyNormalizedActionParts = new Dictionary(); + foreach (string action in actionParts.Keys) + { + MessagePartSpecification p = new MessagePartSpecification(); + p.Union(actionParts[action]); + p.Union(channelParts); + p.MakeReadOnly(); + readOnlyNormalizedActionParts[action] = p; + } + isReadOnly = true; + } + } + + void ThrowIfReadOnly() + { + if (isReadOnly) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.ObjectIsReadOnly)); + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Security/SecurityAlgorithmSuite.cs b/src/CoreWCF.Primitives/src/CoreWCF/Security/SecurityAlgorithmSuite.cs new file mode 100644 index 000000000..d905de11a --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Security/SecurityAlgorithmSuite.cs @@ -0,0 +1,804 @@ +using CoreWCF.IdentityModel.Tokens; +using System; +using System.Collections.Generic; +using System.Text; +using System.Xml; + +namespace CoreWCF.Security +{ + public abstract class SecurityAlgorithmSuite + { + static SecurityAlgorithmSuite basic256; + static SecurityAlgorithmSuite basic192; + static SecurityAlgorithmSuite basic128; + static SecurityAlgorithmSuite tripleDes; + static SecurityAlgorithmSuite basic256Rsa15; + static SecurityAlgorithmSuite basic192Rsa15; + static SecurityAlgorithmSuite basic128Rsa15; + static SecurityAlgorithmSuite tripleDesRsa15; + static SecurityAlgorithmSuite basic256Sha256; + static SecurityAlgorithmSuite basic192Sha256; + static SecurityAlgorithmSuite basic128Sha256; + static SecurityAlgorithmSuite tripleDesSha256; + static SecurityAlgorithmSuite basic256Sha256Rsa15; + static SecurityAlgorithmSuite basic192Sha256Rsa15; + static SecurityAlgorithmSuite basic128Sha256Rsa15; + static SecurityAlgorithmSuite tripleDesSha256Rsa15; + + static internal SecurityAlgorithmSuite KerberosDefault + { + get + { + return Basic128; + } + } + static public SecurityAlgorithmSuite Default + { + get + { + return Basic256; + } + } + + static public SecurityAlgorithmSuite Basic256 + { + get + { + if (basic256 == null) + basic256 = new Basic256SecurityAlgorithmSuite(); + return basic256; + } + } + static public SecurityAlgorithmSuite Basic192 + { + get + { + if (basic192 == null) + basic192 = new Basic192SecurityAlgorithmSuite(); + return basic192; + } + } + static public SecurityAlgorithmSuite Basic128 + { + get + { + if (basic128 == null) + basic128 = new Basic128SecurityAlgorithmSuite(); + return basic128; + } + } + static public SecurityAlgorithmSuite TripleDes + { + get + { + if (tripleDes == null) + tripleDes = new TripleDesSecurityAlgorithmSuite(); + return tripleDes; + } + } + static public SecurityAlgorithmSuite Basic256Rsa15 + { + get + { + if (basic256Rsa15 == null) + basic256Rsa15 = new Basic256Rsa15SecurityAlgorithmSuite(); + return basic256Rsa15; + } + } + static public SecurityAlgorithmSuite Basic192Rsa15 + { + get + { + if (basic192Rsa15 == null) + basic192Rsa15 = new Basic192Rsa15SecurityAlgorithmSuite(); + return basic192Rsa15; + } + } + static public SecurityAlgorithmSuite Basic128Rsa15 + { + get + { + if (basic128Rsa15 == null) + basic128Rsa15 = new Basic128Rsa15SecurityAlgorithmSuite(); + return basic128Rsa15; + } + } + static public SecurityAlgorithmSuite TripleDesRsa15 + { + get + { + if (tripleDesRsa15 == null) + tripleDesRsa15 = new TripleDesRsa15SecurityAlgorithmSuite(); + return tripleDesRsa15; + } + } + + static public SecurityAlgorithmSuite Basic256Sha256 + { + get + { + if (basic256Sha256 == null) + basic256Sha256 = new Basic256Sha256SecurityAlgorithmSuite(); + return basic256Sha256; + } + } + static public SecurityAlgorithmSuite Basic192Sha256 + { + get + { + if (basic192Sha256 == null) + basic192Sha256 = new Basic192Sha256SecurityAlgorithmSuite(); + return basic192Sha256; + } + } + static public SecurityAlgorithmSuite Basic128Sha256 + { + get + { + if (basic128Sha256 == null) + basic128Sha256 = new Basic128Sha256SecurityAlgorithmSuite(); + return basic128Sha256; + } + } + static public SecurityAlgorithmSuite TripleDesSha256 + { + get + { + if (tripleDesSha256 == null) + tripleDesSha256 = new TripleDesSha256SecurityAlgorithmSuite(); + return tripleDesSha256; + } + } + static public SecurityAlgorithmSuite Basic256Sha256Rsa15 + { + get + { + if (basic256Sha256Rsa15 == null) + basic256Sha256Rsa15 = new Basic256Sha256Rsa15SecurityAlgorithmSuite(); + return basic256Sha256Rsa15; + } + } + static public SecurityAlgorithmSuite Basic192Sha256Rsa15 + { + get + { + if (basic192Sha256Rsa15 == null) + basic192Sha256Rsa15 = new Basic192Sha256Rsa15SecurityAlgorithmSuite(); + return basic192Sha256Rsa15; + } + } + static public SecurityAlgorithmSuite Basic128Sha256Rsa15 + { + get + { + if (basic128Sha256Rsa15 == null) + basic128Sha256Rsa15 = new Basic128Sha256Rsa15SecurityAlgorithmSuite(); + return basic128Sha256Rsa15; + } + } + static public SecurityAlgorithmSuite TripleDesSha256Rsa15 + { + get + { + if (tripleDesSha256Rsa15 == null) + tripleDesSha256Rsa15 = new TripleDesSha256Rsa15SecurityAlgorithmSuite(); + return tripleDesSha256Rsa15; + } + } + + public abstract string DefaultCanonicalizationAlgorithm { get; } + public abstract string DefaultDigestAlgorithm { get; } + public abstract string DefaultEncryptionAlgorithm { get; } + public abstract int DefaultEncryptionKeyDerivationLength { get; } + public abstract string DefaultSymmetricKeyWrapAlgorithm { get; } + public abstract string DefaultAsymmetricKeyWrapAlgorithm { get; } + public abstract string DefaultSymmetricSignatureAlgorithm { get; } + public abstract string DefaultAsymmetricSignatureAlgorithm { get; } + public abstract int DefaultSignatureKeyDerivationLength { get; } + public abstract int DefaultSymmetricKeyLength { get; } + + internal virtual XmlDictionaryString DefaultCanonicalizationAlgorithmDictionaryString { get { return null; } } + internal virtual XmlDictionaryString DefaultDigestAlgorithmDictionaryString { get { return null; } } + internal virtual XmlDictionaryString DefaultEncryptionAlgorithmDictionaryString { get { return null; } } + internal virtual XmlDictionaryString DefaultSymmetricKeyWrapAlgorithmDictionaryString { get { return null; } } + internal virtual XmlDictionaryString DefaultAsymmetricKeyWrapAlgorithmDictionaryString { get { return null; } } + internal virtual XmlDictionaryString DefaultSymmetricSignatureAlgorithmDictionaryString { get { return null; } } + internal virtual XmlDictionaryString DefaultAsymmetricSignatureAlgorithmDictionaryString { get { return null; } } + + protected SecurityAlgorithmSuite() { } + + public virtual bool IsCanonicalizationAlgorithmSupported(string algorithm) { return algorithm == DefaultCanonicalizationAlgorithm; } + public virtual bool IsDigestAlgorithmSupported(string algorithm) { return algorithm == DefaultDigestAlgorithm; } + public virtual bool IsEncryptionAlgorithmSupported(string algorithm) { return algorithm == DefaultEncryptionAlgorithm; } + public virtual bool IsEncryptionKeyDerivationAlgorithmSupported(string algorithm) { return (algorithm == SecurityAlgorithms.Psha1KeyDerivation) || (algorithm == SecurityAlgorithms.Psha1KeyDerivationDec2005); } + public virtual bool IsSymmetricKeyWrapAlgorithmSupported(string algorithm) { return algorithm == DefaultSymmetricKeyWrapAlgorithm; } + public virtual bool IsAsymmetricKeyWrapAlgorithmSupported(string algorithm) { return algorithm == DefaultAsymmetricKeyWrapAlgorithm; } + public virtual bool IsSymmetricSignatureAlgorithmSupported(string algorithm) { return algorithm == DefaultSymmetricSignatureAlgorithm; } + public virtual bool IsAsymmetricSignatureAlgorithmSupported(string algorithm) { return algorithm == DefaultAsymmetricSignatureAlgorithm; } + public virtual bool IsSignatureKeyDerivationAlgorithmSupported(string algorithm) { return (algorithm == SecurityAlgorithms.Psha1KeyDerivation) || (algorithm == SecurityAlgorithms.Psha1KeyDerivationDec2005); } + public abstract bool IsSymmetricKeyLengthSupported(int length); + public abstract bool IsAsymmetricKeyLengthSupported(int length); + + internal static bool IsRsaSHA256(SecurityAlgorithmSuite suite) + { + if (suite == null) + return false; + + return (suite == Basic128Sha256 || suite == Basic128Sha256Rsa15 || suite == Basic192Sha256 || suite == Basic192Sha256Rsa15 || + suite == Basic256Sha256 || suite == Basic256Sha256Rsa15 || suite == TripleDesSha256 || suite == TripleDesSha256Rsa15); + + } + + //internal string GetEncryptionKeyDerivationAlgorithm(SecurityToken token, SecureConversationVersion version) + //{ + // if (token == null) + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("token"); + + // string derivationAlgorithm = SecurityUtils.GetKeyDerivationAlgorithm(version); + // if (SecurityUtils.IsSupportedAlgorithm(derivationAlgorithm, token)) + // return derivationAlgorithm; + // else + // return null; + //} + + //internal int GetEncryptionKeyDerivationLength(SecurityToken token, SecureConversationVersion version) + //{ + // if (token == null) + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("token"); + + // string derivationAlgorithm = SecurityUtils.GetKeyDerivationAlgorithm(version); + // if (SecurityUtils.IsSupportedAlgorithm(derivationAlgorithm, token)) + // { + // if (this.DefaultEncryptionKeyDerivationLength % 8 != 0) + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.Format(SR.Psha1KeyLengthInvalid, this.DefaultEncryptionKeyDerivationLength))); + + // return this.DefaultEncryptionKeyDerivationLength / 8; + // } + // else + // return 0; + //} + + //internal void GetKeyWrapAlgorithm(SecurityToken token, out string keyWrapAlgorithm, out XmlDictionaryString keyWrapAlgorithmDictionaryString) + //{ + // if (token == null) + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("token"); + + // if (SecurityUtils.IsSupportedAlgorithm(this.DefaultSymmetricKeyWrapAlgorithm, token)) + // { + // keyWrapAlgorithm = this.DefaultSymmetricKeyWrapAlgorithm; + // keyWrapAlgorithmDictionaryString = this.DefaultSymmetricKeyWrapAlgorithmDictionaryString; + // } + // else + // { + // keyWrapAlgorithm = this.DefaultAsymmetricKeyWrapAlgorithm; + // keyWrapAlgorithmDictionaryString = this.DefaultAsymmetricKeyWrapAlgorithmDictionaryString; + // } + //} + + //internal void GetSignatureAlgorithmAndKey(SecurityToken token, out string signatureAlgorithm, out SecurityKey key, out XmlDictionaryString signatureAlgorithmDictionaryString) + //{ + // ReadOnlyCollection keys = token.SecurityKeys; + // if (keys == null || keys.Count == 0) + // { + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SigningTokenHasNoKeys, token))); + // } + + // for (int i = 0; i < keys.Count; i++) + // { + // if (keys[i].IsSupportedAlgorithm(this.DefaultSymmetricSignatureAlgorithm)) + // { + // signatureAlgorithm = this.DefaultSymmetricSignatureAlgorithm; + // signatureAlgorithmDictionaryString = this.DefaultSymmetricSignatureAlgorithmDictionaryString; + // key = keys[i]; + // return; + // } + // else if (keys[i].IsSupportedAlgorithm(this.DefaultAsymmetricSignatureAlgorithm)) + // { + // signatureAlgorithm = this.DefaultAsymmetricSignatureAlgorithm; + // signatureAlgorithmDictionaryString = this.DefaultAsymmetricSignatureAlgorithmDictionaryString; + // key = keys[i]; + // return; + // } + // } + + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SigningTokenHasNoKeysSupportingTheAlgorithmSuite, token, this))); + //} + + //internal string GetSignatureKeyDerivationAlgorithm(SecurityToken token, SecureConversationVersion version) + //{ + // if (token == null) + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("token"); + + // string derivationAlgorithm = SecurityUtils.GetKeyDerivationAlgorithm(version); + // if (SecurityUtils.IsSupportedAlgorithm(derivationAlgorithm, token)) + // return derivationAlgorithm; + // else + // return null; + //} + + //internal int GetSignatureKeyDerivationLength(SecurityToken token, SecureConversationVersion version) + //{ + // if (token == null) + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("token"); + + // string derivationAlgorithm = SecurityUtils.GetKeyDerivationAlgorithm(version); + // if (SecurityUtils.IsSupportedAlgorithm(derivationAlgorithm, token)) + // { + // if (this.DefaultSignatureKeyDerivationLength % 8 != 0) + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.Format(SR.Psha1KeyLengthInvalid, this.DefaultSignatureKeyDerivationLength))); + + // return this.DefaultSignatureKeyDerivationLength / 8; + // } + // else + // return 0; + //} + + //internal void EnsureAcceptableSymmetricSignatureAlgorithm(string algorithm) + //{ + // if (!IsSymmetricSignatureAlgorithmSupported(algorithm)) + // { + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageSecurityException(SR.Format(SR.SuiteDoesNotAcceptAlgorithm, + // algorithm, "SymmetricSignature", this))); + // } + //} + + //internal void EnsureAcceptableSignatureKeySize(SecurityKey securityKey, SecurityToken token) + //{ + // AsymmetricSecurityKey asymmetricSecurityKey = securityKey as AsymmetricSecurityKey; + // if (asymmetricSecurityKey != null) + // { + // if (!IsAsymmetricKeyLengthSupported(asymmetricSecurityKey.KeySize)) + // { + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageSecurityException( + // SR.Format(SR.TokenDoesNotMeetKeySizeRequirements, this, token, asymmetricSecurityKey.KeySize))); + // } + // } + // else + // { + // SymmetricSecurityKey symmetricSecurityKey = securityKey as SymmetricSecurityKey; + // if (symmetricSecurityKey == null) + // { + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.UnknownICryptoType, symmetricSecurityKey))); + // } + // EnsureAcceptableSignatureSymmetricKeySize(symmetricSecurityKey, token); + // } + //} + + //// Ensure acceptable signing symmetric key. + //// 1) if derived key, validate derived key against DefaultSignatureKeyDerivationLength and validate + //// source key against DefaultSymmetricKeyLength + //// 2) if not derived key, validate key against DefaultSymmetricKeyLength + //internal void EnsureAcceptableSignatureSymmetricKeySize(SymmetricSecurityKey securityKey, SecurityToken token) + //{ + // int keySize; + // DerivedKeySecurityToken dkt = token as DerivedKeySecurityToken; + // if (dkt != null) + // { + // token = dkt.TokenToDerive; + // keySize = ((SymmetricSecurityKey)token.SecurityKeys[0]).KeySize; + + // // doing special case for derived key token signing length since + // // the sending side doesn't honor the algorithm suite. It used the DefaultSignatureKeyDerivationLength instead + // if (dkt.SecurityKeys[0].KeySize < this.DefaultSignatureKeyDerivationLength) + // { + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageSecurityException( + // SR.Format(SR.TokenDoesNotMeetKeySizeRequirements, this, dkt, dkt.SecurityKeys[0].KeySize))); + // } + // } + // else + // { + // keySize = securityKey.KeySize; + // } + + // if (!IsSymmetricKeyLengthSupported(keySize)) + // { + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageSecurityException( + // SR.Format(SR.TokenDoesNotMeetKeySizeRequirements, this, token, keySize))); + // } + //} + + //// Ensure acceptable decrypting symmetric key. + //// 1) if derived key, validate derived key against DefaultEncryptionKeyDerivationLength and validate + //// source key against DefaultSymmetricKeyLength + //// 2) if not derived key, validate key against DefaultSymmetricKeyLength + //internal void EnsureAcceptableDecryptionSymmetricKeySize(SymmetricSecurityKey securityKey, SecurityToken token) + //{ + // int keySize; + // DerivedKeySecurityToken dkt = token as DerivedKeySecurityToken; + // if (dkt != null) + // { + // token = dkt.TokenToDerive; + // keySize = ((SymmetricSecurityKey)token.SecurityKeys[0]).KeySize; + + // // doing special case for derived key token signing length since + // // the sending side doesn't honor the algorithm suite. It used the DefaultSignatureKeyDerivationLength instead + // if (dkt.SecurityKeys[0].KeySize < this.DefaultEncryptionKeyDerivationLength) + // { + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageSecurityException( + // SR.Format(SR.TokenDoesNotMeetKeySizeRequirements, this, dkt, dkt.SecurityKeys[0].KeySize))); + // } + // } + // else + // { + // keySize = securityKey.KeySize; + // } + + // if (!IsSymmetricKeyLengthSupported(keySize)) + // { + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageSecurityException( + // SR.Format(SR.TokenDoesNotMeetKeySizeRequirements, this, token, keySize))); + // } + //} + + //internal void EnsureAcceptableSignatureAlgorithm(SecurityKey verificationKey, string algorithm) + //{ + // InMemorySymmetricSecurityKey symmeticKey = verificationKey as InMemorySymmetricSecurityKey; + // if (symmeticKey != null) + // { + // this.EnsureAcceptableSymmetricSignatureAlgorithm(algorithm); + // } + // else + // { + // AsymmetricSecurityKey asymmetricKey = verificationKey as AsymmetricSecurityKey; + // if (asymmetricKey == null) + // { + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.UnknownICryptoType, verificationKey))); + // } + + // this.EnsureAcceptableAsymmetricSignatureAlgorithm(algorithm); + // } + //} + + //internal void EnsureAcceptableAsymmetricSignatureAlgorithm(string algorithm) + //{ + // if (!IsAsymmetricSignatureAlgorithmSupported(algorithm)) + // { + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageSecurityException(SR.Format(SR.SuiteDoesNotAcceptAlgorithm, + // algorithm, "AsymmetricSignature", this))); + // } + //} + + //internal void EnsureAcceptableKeyWrapAlgorithm(string algorithm, bool isAsymmetric) + //{ + // if (isAsymmetric) + // { + // if (!IsAsymmetricKeyWrapAlgorithmSupported(algorithm)) + // { + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageSecurityException(SR.Format(SR.SuiteDoesNotAcceptAlgorithm, + // algorithm, "AsymmetricKeyWrap", this))); + // } + // } + // else + // { + // if (!IsSymmetricKeyWrapAlgorithmSupported(algorithm)) + // { + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageSecurityException(SR.Format(SR.SuiteDoesNotAcceptAlgorithm, + // algorithm, "SymmetricKeyWrap", this))); + // } + // } + //} + + //internal void EnsureAcceptableEncryptionAlgorithm(string algorithm) + //{ + // if (!IsEncryptionAlgorithmSupported(algorithm)) + // { + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageSecurityException(SR.Format(SR.SuiteDoesNotAcceptAlgorithm, + // algorithm, "Encryption", this))); + // } + //} + + //internal void EnsureAcceptableSignatureKeyDerivationAlgorithm(string algorithm) + //{ + // if (!IsSignatureKeyDerivationAlgorithmSupported(algorithm)) + // { + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageSecurityException(SR.Format(SR.SuiteDoesNotAcceptAlgorithm, + // algorithm, "SignatureKeyDerivation", this))); + // } + //} + + //internal void EnsureAcceptableEncryptionKeyDerivationAlgorithm(string algorithm) + //{ + // if (!IsEncryptionKeyDerivationAlgorithmSupported(algorithm)) + // { + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageSecurityException(SR.Format(SR.SuiteDoesNotAcceptAlgorithm, + // algorithm, "EncryptionKeyDerivation", this))); + // } + //} + + //internal void EnsureAcceptableDigestAlgorithm(string algorithm) + //{ + // if (!IsDigestAlgorithmSupported(algorithm)) + // { + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageSecurityException(SR.Format(SR.SuiteDoesNotAcceptAlgorithm, + // algorithm, "Digest", this))); + // } + //} + + } + + public class Basic256SecurityAlgorithmSuite : SecurityAlgorithmSuite + { + public Basic256SecurityAlgorithmSuite() : base() { } + + public override string DefaultCanonicalizationAlgorithm { get { return DefaultCanonicalizationAlgorithmDictionaryString.Value; } } + public override string DefaultDigestAlgorithm { get { return DefaultDigestAlgorithmDictionaryString.Value; } } + public override string DefaultEncryptionAlgorithm { get { return DefaultEncryptionAlgorithmDictionaryString.Value; } } + public override int DefaultEncryptionKeyDerivationLength { get { return 256; } } + public override string DefaultSymmetricKeyWrapAlgorithm { get { return DefaultSymmetricKeyWrapAlgorithmDictionaryString.Value; } } + public override string DefaultAsymmetricKeyWrapAlgorithm { get { return DefaultAsymmetricKeyWrapAlgorithmDictionaryString.Value; } } + public override string DefaultSymmetricSignatureAlgorithm { get { return DefaultSymmetricSignatureAlgorithmDictionaryString.Value; } } + public override string DefaultAsymmetricSignatureAlgorithm { get { return DefaultAsymmetricSignatureAlgorithmDictionaryString.Value; } } + public override int DefaultSignatureKeyDerivationLength { get { return 192; } } + public override int DefaultSymmetricKeyLength { get { return 256; } } + public override bool IsSymmetricKeyLengthSupported(int length) { return length == 256; } + public override bool IsAsymmetricKeyLengthSupported(int length) { return length >= 1024 && length <= 4096; } + + internal override XmlDictionaryString DefaultCanonicalizationAlgorithmDictionaryString { get { return XD.SecurityAlgorithmDictionary.ExclusiveC14n; } } + internal override XmlDictionaryString DefaultDigestAlgorithmDictionaryString { get { return XD.SecurityAlgorithmDictionary.Sha1Digest; } } + internal override XmlDictionaryString DefaultEncryptionAlgorithmDictionaryString { get { return XD.SecurityAlgorithmDictionary.Aes256Encryption; } } + internal override XmlDictionaryString DefaultSymmetricKeyWrapAlgorithmDictionaryString { get { return XD.SecurityAlgorithmDictionary.Aes256KeyWrap; } } + internal override XmlDictionaryString DefaultAsymmetricKeyWrapAlgorithmDictionaryString { get { return XD.SecurityAlgorithmDictionary.RsaOaepKeyWrap; } } + internal override XmlDictionaryString DefaultSymmetricSignatureAlgorithmDictionaryString { get { return XD.SecurityAlgorithmDictionary.HmacSha1Signature; } } + internal override XmlDictionaryString DefaultAsymmetricSignatureAlgorithmDictionaryString { get { return XD.SecurityAlgorithmDictionary.RsaSha1Signature; } } + + public override string ToString() + { + return "Basic256"; + } + } + + public class Basic192SecurityAlgorithmSuite : SecurityAlgorithmSuite + { + public Basic192SecurityAlgorithmSuite() : base() { } + + public override string DefaultCanonicalizationAlgorithm { get { return DefaultCanonicalizationAlgorithmDictionaryString.Value; } } + public override string DefaultDigestAlgorithm { get { return DefaultDigestAlgorithmDictionaryString.Value; } } + public override string DefaultEncryptionAlgorithm { get { return DefaultEncryptionAlgorithmDictionaryString.Value; } } + public override int DefaultEncryptionKeyDerivationLength { get { return 192; } } + public override string DefaultSymmetricKeyWrapAlgorithm { get { return DefaultSymmetricKeyWrapAlgorithmDictionaryString.Value; } } + public override string DefaultAsymmetricKeyWrapAlgorithm { get { return DefaultAsymmetricKeyWrapAlgorithmDictionaryString.Value; } } + public override string DefaultSymmetricSignatureAlgorithm { get { return DefaultSymmetricSignatureAlgorithmDictionaryString.Value; } } + public override string DefaultAsymmetricSignatureAlgorithm { get { return DefaultAsymmetricSignatureAlgorithmDictionaryString.Value; } } + public override int DefaultSignatureKeyDerivationLength { get { return 192; } } + public override int DefaultSymmetricKeyLength { get { return 192; } } + public override bool IsSymmetricKeyLengthSupported(int length) { return length >= 192 && length <= 256; } + public override bool IsAsymmetricKeyLengthSupported(int length) { return length >= 1024 && length <= 4096; } + + internal override XmlDictionaryString DefaultCanonicalizationAlgorithmDictionaryString { get { return XD.SecurityAlgorithmDictionary.ExclusiveC14n; } } + internal override XmlDictionaryString DefaultDigestAlgorithmDictionaryString { get { return XD.SecurityAlgorithmDictionary.Sha1Digest; } } + internal override XmlDictionaryString DefaultEncryptionAlgorithmDictionaryString { get { return XD.SecurityAlgorithmDictionary.Aes192Encryption; } } + internal override XmlDictionaryString DefaultSymmetricKeyWrapAlgorithmDictionaryString { get { return XD.SecurityAlgorithmDictionary.Aes192KeyWrap; } } + internal override XmlDictionaryString DefaultAsymmetricKeyWrapAlgorithmDictionaryString { get { return XD.SecurityAlgorithmDictionary.RsaOaepKeyWrap; } } + internal override XmlDictionaryString DefaultSymmetricSignatureAlgorithmDictionaryString { get { return XD.SecurityAlgorithmDictionary.HmacSha1Signature; } } + internal override XmlDictionaryString DefaultAsymmetricSignatureAlgorithmDictionaryString { get { return XD.SecurityAlgorithmDictionary.RsaSha1Signature; } } + + public override string ToString() + { + return "Basic192"; + } + } + + public class Basic128SecurityAlgorithmSuite : SecurityAlgorithmSuite + { + public Basic128SecurityAlgorithmSuite() : base() { } + + public override string DefaultCanonicalizationAlgorithm { get { return DefaultCanonicalizationAlgorithmDictionaryString.Value; } } + public override string DefaultDigestAlgorithm { get { return DefaultDigestAlgorithmDictionaryString.Value; } } + public override string DefaultEncryptionAlgorithm { get { return DefaultEncryptionAlgorithmDictionaryString.Value; } } + public override int DefaultEncryptionKeyDerivationLength { get { return 128; } } + public override string DefaultSymmetricKeyWrapAlgorithm { get { return DefaultSymmetricKeyWrapAlgorithmDictionaryString.Value; } } + public override string DefaultAsymmetricKeyWrapAlgorithm { get { return DefaultAsymmetricKeyWrapAlgorithmDictionaryString.Value; } } + public override string DefaultSymmetricSignatureAlgorithm { get { return DefaultSymmetricSignatureAlgorithmDictionaryString.Value; } } + public override string DefaultAsymmetricSignatureAlgorithm { get { return DefaultAsymmetricSignatureAlgorithmDictionaryString.Value; } } + public override int DefaultSignatureKeyDerivationLength { get { return 128; } } + public override int DefaultSymmetricKeyLength { get { return 128; } } + public override bool IsSymmetricKeyLengthSupported(int length) { return length >= 128 && length <= 256; } + public override bool IsAsymmetricKeyLengthSupported(int length) { return length >= 1024 && length <= 4096; } + + internal override XmlDictionaryString DefaultCanonicalizationAlgorithmDictionaryString { get { return XD.SecurityAlgorithmDictionary.ExclusiveC14n; } } + internal override XmlDictionaryString DefaultDigestAlgorithmDictionaryString { get { return XD.SecurityAlgorithmDictionary.Sha1Digest; } } + internal override XmlDictionaryString DefaultEncryptionAlgorithmDictionaryString { get { return XD.SecurityAlgorithmDictionary.Aes128Encryption; } } + internal override XmlDictionaryString DefaultSymmetricKeyWrapAlgorithmDictionaryString { get { return XD.SecurityAlgorithmDictionary.Aes128KeyWrap; } } + internal override XmlDictionaryString DefaultAsymmetricKeyWrapAlgorithmDictionaryString { get { return XD.SecurityAlgorithmDictionary.RsaOaepKeyWrap; } } + internal override XmlDictionaryString DefaultSymmetricSignatureAlgorithmDictionaryString { get { return XD.SecurityAlgorithmDictionary.HmacSha1Signature; } } + internal override XmlDictionaryString DefaultAsymmetricSignatureAlgorithmDictionaryString { get { return XD.SecurityAlgorithmDictionary.RsaSha1Signature; } } + + public override string ToString() + { + return "Basic128"; + } + } + + public class TripleDesSecurityAlgorithmSuite : SecurityAlgorithmSuite + { + public TripleDesSecurityAlgorithmSuite() : base() { } + + public override string DefaultCanonicalizationAlgorithm { get { return DefaultCanonicalizationAlgorithmDictionaryString.Value; } } + public override string DefaultDigestAlgorithm { get { return DefaultDigestAlgorithmDictionaryString.Value; } } + public override string DefaultEncryptionAlgorithm { get { return DefaultEncryptionAlgorithmDictionaryString.Value; } } + public override int DefaultEncryptionKeyDerivationLength { get { return 192; } } + public override string DefaultSymmetricKeyWrapAlgorithm { get { return DefaultSymmetricKeyWrapAlgorithmDictionaryString.Value; } } + public override string DefaultAsymmetricKeyWrapAlgorithm { get { return DefaultAsymmetricKeyWrapAlgorithmDictionaryString.Value; } } + + public override string DefaultSymmetricSignatureAlgorithm { get { return DefaultSymmetricSignatureAlgorithmDictionaryString.Value; } } + public override string DefaultAsymmetricSignatureAlgorithm { get { return DefaultAsymmetricSignatureAlgorithmDictionaryString.Value; } } + public override int DefaultSignatureKeyDerivationLength { get { return 192; } } + public override int DefaultSymmetricKeyLength { get { return 192; } } + public override bool IsSymmetricKeyLengthSupported(int length) { return length >= 192 && length <= 256; } + public override bool IsAsymmetricKeyLengthSupported(int length) { return length >= 1024 && length <= 4096; } + + internal override XmlDictionaryString DefaultCanonicalizationAlgorithmDictionaryString { get { return XD.SecurityAlgorithmDictionary.ExclusiveC14n; } } + internal override XmlDictionaryString DefaultDigestAlgorithmDictionaryString { get { return XD.SecurityAlgorithmDictionary.Sha1Digest; } } + internal override XmlDictionaryString DefaultEncryptionAlgorithmDictionaryString { get { return XD.SecurityAlgorithmDictionary.TripleDesEncryption; } } + internal override XmlDictionaryString DefaultSymmetricKeyWrapAlgorithmDictionaryString { get { return XD.SecurityAlgorithmDictionary.TripleDesKeyWrap; } } + internal override XmlDictionaryString DefaultAsymmetricKeyWrapAlgorithmDictionaryString { get { return XD.SecurityAlgorithmDictionary.RsaOaepKeyWrap; } } + internal override XmlDictionaryString DefaultSymmetricSignatureAlgorithmDictionaryString { get { return XD.SecurityAlgorithmDictionary.HmacSha1Signature; } } + internal override XmlDictionaryString DefaultAsymmetricSignatureAlgorithmDictionaryString { get { return XD.SecurityAlgorithmDictionary.RsaSha1Signature; } } + + public override string ToString() + { + return "TripleDes"; + } + } + + class Basic128Rsa15SecurityAlgorithmSuite : Basic128SecurityAlgorithmSuite + { + public Basic128Rsa15SecurityAlgorithmSuite() : base() { } + + internal override XmlDictionaryString DefaultAsymmetricKeyWrapAlgorithmDictionaryString { get { return XD.SecurityAlgorithmDictionary.RsaV15KeyWrap; } } + + public override string ToString() + { + return "Basic128Rsa15"; + } + } + + class Basic192Rsa15SecurityAlgorithmSuite : Basic192SecurityAlgorithmSuite + { + public Basic192Rsa15SecurityAlgorithmSuite() : base() { } + + internal override XmlDictionaryString DefaultAsymmetricKeyWrapAlgorithmDictionaryString { get { return XD.SecurityAlgorithmDictionary.RsaV15KeyWrap; } } + + public override string ToString() + { + return "Basic192Rsa15"; + } + } + + class Basic256Rsa15SecurityAlgorithmSuite : Basic256SecurityAlgorithmSuite + { + public Basic256Rsa15SecurityAlgorithmSuite() : base() { } + + internal override XmlDictionaryString DefaultAsymmetricKeyWrapAlgorithmDictionaryString { get { return XD.SecurityAlgorithmDictionary.RsaV15KeyWrap; } } + + public override string ToString() + { + return "Basic256Rsa15"; + } + } + + class TripleDesRsa15SecurityAlgorithmSuite : TripleDesSecurityAlgorithmSuite + { + public TripleDesRsa15SecurityAlgorithmSuite() : base() { } + + internal override XmlDictionaryString DefaultAsymmetricKeyWrapAlgorithmDictionaryString { get { return XD.SecurityAlgorithmDictionary.RsaV15KeyWrap; } } + + public override string ToString() + { + return "TripleDesRsa15"; + } + } + + class Basic256Sha256SecurityAlgorithmSuite : Basic256SecurityAlgorithmSuite + { + public Basic256Sha256SecurityAlgorithmSuite() : base() { } + + internal override XmlDictionaryString DefaultDigestAlgorithmDictionaryString { get { return XD.SecurityAlgorithmDictionary.Sha256Digest; } } + internal override XmlDictionaryString DefaultSymmetricSignatureAlgorithmDictionaryString { get { return XD.SecurityAlgorithmDictionary.HmacSha256Signature; } } + internal override XmlDictionaryString DefaultAsymmetricSignatureAlgorithmDictionaryString { get { return XD.SecurityAlgorithmDictionary.RsaSha256Signature; } } + + public override string ToString() + { + return "Basic256Sha256"; + } + } + + class Basic192Sha256SecurityAlgorithmSuite : Basic192SecurityAlgorithmSuite + { + public Basic192Sha256SecurityAlgorithmSuite() : base() { } + + internal override XmlDictionaryString DefaultDigestAlgorithmDictionaryString { get { return XD.SecurityAlgorithmDictionary.Sha256Digest; } } + internal override XmlDictionaryString DefaultSymmetricSignatureAlgorithmDictionaryString { get { return XD.SecurityAlgorithmDictionary.HmacSha256Signature; } } + internal override XmlDictionaryString DefaultAsymmetricSignatureAlgorithmDictionaryString { get { return XD.SecurityAlgorithmDictionary.RsaSha256Signature; } } + + public override string ToString() + { + return "Basic192Sha256"; + } + } + + class Basic128Sha256SecurityAlgorithmSuite : Basic128SecurityAlgorithmSuite + { + public Basic128Sha256SecurityAlgorithmSuite() : base() { } + + internal override XmlDictionaryString DefaultDigestAlgorithmDictionaryString { get { return XD.SecurityAlgorithmDictionary.Sha256Digest; } } + internal override XmlDictionaryString DefaultSymmetricSignatureAlgorithmDictionaryString { get { return XD.SecurityAlgorithmDictionary.HmacSha256Signature; } } + internal override XmlDictionaryString DefaultAsymmetricSignatureAlgorithmDictionaryString { get { return XD.SecurityAlgorithmDictionary.RsaSha256Signature; } } + + public override string ToString() + { + return "Basic128Sha256"; + } + } + + class TripleDesSha256SecurityAlgorithmSuite : TripleDesSecurityAlgorithmSuite + { + public TripleDesSha256SecurityAlgorithmSuite() : base() { } + + internal override XmlDictionaryString DefaultDigestAlgorithmDictionaryString { get { return XD.SecurityAlgorithmDictionary.Sha256Digest; } } + internal override XmlDictionaryString DefaultSymmetricSignatureAlgorithmDictionaryString { get { return XD.SecurityAlgorithmDictionary.HmacSha256Signature; } } + internal override XmlDictionaryString DefaultAsymmetricSignatureAlgorithmDictionaryString { get { return XD.SecurityAlgorithmDictionary.RsaSha256Signature; } } + + public override string ToString() + { + return "TripleDesSha256"; + } + } + + class Basic256Sha256Rsa15SecurityAlgorithmSuite : Basic256Rsa15SecurityAlgorithmSuite + { + public Basic256Sha256Rsa15SecurityAlgorithmSuite() : base() { } + + internal override XmlDictionaryString DefaultDigestAlgorithmDictionaryString { get { return XD.SecurityAlgorithmDictionary.Sha256Digest; } } + internal override XmlDictionaryString DefaultSymmetricSignatureAlgorithmDictionaryString { get { return XD.SecurityAlgorithmDictionary.HmacSha256Signature; } } + internal override XmlDictionaryString DefaultAsymmetricSignatureAlgorithmDictionaryString { get { return XD.SecurityAlgorithmDictionary.RsaSha256Signature; } } + + public override string ToString() + { + return "Basic256Sha256Rsa15"; + } + } + + class Basic192Sha256Rsa15SecurityAlgorithmSuite : Basic192Rsa15SecurityAlgorithmSuite + { + public Basic192Sha256Rsa15SecurityAlgorithmSuite() : base() { } + + internal override XmlDictionaryString DefaultDigestAlgorithmDictionaryString { get { return XD.SecurityAlgorithmDictionary.Sha256Digest; } } + internal override XmlDictionaryString DefaultSymmetricSignatureAlgorithmDictionaryString { get { return XD.SecurityAlgorithmDictionary.HmacSha256Signature; } } + internal override XmlDictionaryString DefaultAsymmetricSignatureAlgorithmDictionaryString { get { return XD.SecurityAlgorithmDictionary.RsaSha256Signature; } } + + public override string ToString() + { + return "Basic192Sha256Rsa15"; + } + } + + class Basic128Sha256Rsa15SecurityAlgorithmSuite : Basic128Rsa15SecurityAlgorithmSuite + { + public Basic128Sha256Rsa15SecurityAlgorithmSuite() : base() { } + + internal override XmlDictionaryString DefaultDigestAlgorithmDictionaryString { get { return XD.SecurityAlgorithmDictionary.Sha256Digest; } } + internal override XmlDictionaryString DefaultSymmetricSignatureAlgorithmDictionaryString { get { return XD.SecurityAlgorithmDictionary.HmacSha256Signature; } } + internal override XmlDictionaryString DefaultAsymmetricSignatureAlgorithmDictionaryString { get { return XD.SecurityAlgorithmDictionary.RsaSha256Signature; } } + + public override string ToString() + { + return "Basic128Sha256Rsa15"; + } + } + + class TripleDesSha256Rsa15SecurityAlgorithmSuite : TripleDesRsa15SecurityAlgorithmSuite + { + public TripleDesSha256Rsa15SecurityAlgorithmSuite() : base() { } + + internal override XmlDictionaryString DefaultDigestAlgorithmDictionaryString { get { return XD.SecurityAlgorithmDictionary.Sha256Digest; } } + internal override XmlDictionaryString DefaultSymmetricSignatureAlgorithmDictionaryString { get { return XD.SecurityAlgorithmDictionary.HmacSha256Signature; } } + internal override XmlDictionaryString DefaultAsymmetricSignatureAlgorithmDictionaryString { get { return XD.SecurityAlgorithmDictionary.RsaSha256Signature; } } + + public override string ToString() + { + return "TripleDesSha256Rsa15"; + } + } + +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Security/SecurityCredentialsManager.cs b/src/CoreWCF.Primitives/src/CoreWCF/Security/SecurityCredentialsManager.cs new file mode 100644 index 000000000..108174fb3 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Security/SecurityCredentialsManager.cs @@ -0,0 +1,15 @@ +using CoreWCF.IdentityModel.Selectors; +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoreWCF.Security +{ + public abstract class SecurityCredentialsManager + { + protected SecurityCredentialsManager() { } + + // TODO: Resolve solution to SecurityTokenManager which lives in System.IdentityModel.Selectors not existing on .Net standard + internal abstract SecurityTokenManager CreateSecurityTokenManager(); + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Security/SecurityMessageProperty.cs b/src/CoreWCF.Primitives/src/CoreWCF/Security/SecurityMessageProperty.cs new file mode 100644 index 000000000..a79bdc3ac --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Security/SecurityMessageProperty.cs @@ -0,0 +1,318 @@ +using CoreWCF.IdentityModel.Policy; +using CoreWCF.Channels; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Text; + +namespace CoreWCF.Security +{ + public class SecurityMessageProperty : IMessageProperty, IDisposable + { + //// This is the list of outgoing supporting tokens + //Collection outgoingSupportingTokens; + //Collection incomingSupportingTokens; + SecurityTokenSpecification transportToken; + //SecurityTokenSpecification protectionToken; + //SecurityTokenSpecification initiatorToken; + //SecurityTokenSpecification recipientToken; + + ServiceSecurityContext securityContext; + //ReadOnlyCollection externalAuthorizationPolicies; + //string senderIdPrefix = "_"; + bool disposed = false; + + public SecurityMessageProperty() + { + securityContext = ServiceSecurityContext.Anonymous; + } + + public ServiceSecurityContext ServiceSecurityContext + { + get + { + ThrowIfDisposed(); + return securityContext; + } + set + { + ThrowIfDisposed(); + securityContext = value; + } + } + + //public ReadOnlyCollection ExternalAuthorizationPolicies + //{ + // get + // { + // return this.externalAuthorizationPolicies; + // } + // set + // { + // this.externalAuthorizationPolicies = value; + // } + //} + + //public SecurityTokenSpecification ProtectionToken + //{ + // get + // { + // ThrowIfDisposed(); + // return this.protectionToken; + // } + // set + // { + // ThrowIfDisposed(); + // this.protectionToken = value; + // } + //} + + //public SecurityTokenSpecification InitiatorToken + //{ + // get + // { + // ThrowIfDisposed(); + // return this.initiatorToken; + // } + // set + // { + // ThrowIfDisposed(); + // this.initiatorToken = value; + // } + //} + + //public SecurityTokenSpecification RecipientToken + //{ + // get + // { + // ThrowIfDisposed(); + // return this.recipientToken; + // } + // set + // { + // ThrowIfDisposed(); + // this.recipientToken = value; + // } + //} + + internal SecurityTokenSpecification TransportToken + { + get + { + ThrowIfDisposed(); + return transportToken; + } + set + { + ThrowIfDisposed(); + transportToken = value; + } + } + + + //public string SenderIdPrefix + //{ + // get + // { + // return this.senderIdPrefix; + // } + // set + // { + // XmlHelper.ValidateIdPrefix(value); + // this.senderIdPrefix = value; + // } + //} + + //public bool HasIncomingSupportingTokens + //{ + // get + // { + // ThrowIfDisposed(); + // return ((this.incomingSupportingTokens != null) && (this.incomingSupportingTokens.Count > 0)); + // } + //} + + //public Collection IncomingSupportingTokens + //{ + // get + // { + // ThrowIfDisposed(); + // if (this.incomingSupportingTokens == null) + // { + // this.incomingSupportingTokens = new Collection(); + // } + // return this.incomingSupportingTokens; + // } + //} + + //public Collection OutgoingSupportingTokens + //{ + // get + // { + // if (this.outgoingSupportingTokens == null) + // { + // this.outgoingSupportingTokens = new Collection(); + // } + // return this.outgoingSupportingTokens; + // } + //} + + //internal bool HasOutgoingSupportingTokens + //{ + // get + // { + // return ((this.outgoingSupportingTokens != null) && (this.outgoingSupportingTokens.Count > 0)); + // } + //} + + public IMessageProperty CreateCopy() + { + ThrowIfDisposed(); + SecurityMessageProperty result = new SecurityMessageProperty(); + + // if (this.HasOutgoingSupportingTokens) + // { + // for (int i = 0; i < this.outgoingSupportingTokens.Count; ++i) + // { + // result.OutgoingSupportingTokens.Add(this.outgoingSupportingTokens[i]); + // } + // } + + // if (this.HasIncomingSupportingTokens) + // { + // for (int i = 0; i < this.incomingSupportingTokens.Count; ++i) + // { + // result.IncomingSupportingTokens.Add(this.incomingSupportingTokens[i]); + // } + // } + + result.securityContext = securityContext; + // result.externalAuthorizationPolicies = this.externalAuthorizationPolicies; + // result.senderIdPrefix = this.senderIdPrefix; + + // result.protectionToken = this.protectionToken; + // result.initiatorToken = this.initiatorToken; + // result.recipientToken = this.recipientToken; + result.transportToken = transportToken; + + return result; + } + + //public static SecurityMessageProperty GetOrCreate(Message message) + //{ + // if (message == null) + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("message"); + + // SecurityMessageProperty result = null; + // if (message.Properties != null) + // result = message.Properties.Security; + + // if (result == null) + // { + // result = new SecurityMessageProperty(); + // message.Properties.Security = result; + // } + + // return result; + //} + + //void AddAuthorizationPolicies(SecurityTokenSpecification spec, Collection policies) + //{ + // if (spec != null && spec.SecurityTokenPolicies != null && spec.SecurityTokenPolicies.Count > 0) + // { + // for (int i = 0; i < spec.SecurityTokenPolicies.Count; ++i) + // { + // policies.Add(spec.SecurityTokenPolicies[i]); + // } + // } + //} + + //internal ReadOnlyCollection GetInitiatorTokenAuthorizationPolicies() + //{ + // return GetInitiatorTokenAuthorizationPolicies(true); + //} + + //internal ReadOnlyCollection GetInitiatorTokenAuthorizationPolicies(bool includeTransportToken) + //{ + // return GetInitiatorTokenAuthorizationPolicies(includeTransportToken, null); + //} + + //internal ReadOnlyCollection GetInitiatorTokenAuthorizationPolicies(bool includeTransportToken, SecurityContextSecurityToken supportingSessionTokenToExclude) + //{ + // // fast path + // if (!this.HasIncomingSupportingTokens) + // { + // if (this.transportToken != null && this.initiatorToken == null && this.protectionToken == null) + // { + // if (includeTransportToken && this.transportToken.SecurityTokenPolicies != null) + // { + // return this.transportToken.SecurityTokenPolicies; + // } + // else + // { + // return EmptyReadOnlyCollection.Instance; + // } + // } + // else if (this.transportToken == null && this.initiatorToken != null && this.protectionToken == null) + // { + // return this.initiatorToken.SecurityTokenPolicies ?? EmptyReadOnlyCollection.Instance; + // } + // else if (this.transportToken == null && this.initiatorToken == null && this.protectionToken != null) + // { + // return this.protectionToken.SecurityTokenPolicies ?? EmptyReadOnlyCollection.Instance; + // } + // } + + // Collection policies = new Collection(); + // if (includeTransportToken) + // { + // AddAuthorizationPolicies(this.transportToken, policies); + // } + // AddAuthorizationPolicies(this.initiatorToken, policies); + // AddAuthorizationPolicies(this.protectionToken, policies); + // if (this.HasIncomingSupportingTokens) + // { + // for (int i = 0; i < this.incomingSupportingTokens.Count; ++i) + // { + // if (supportingSessionTokenToExclude != null) + // { + // SecurityContextSecurityToken sct = this.incomingSupportingTokens[i].SecurityToken as SecurityContextSecurityToken; + // if (sct != null && sct.ContextId == supportingSessionTokenToExclude.ContextId) + // { + // continue; + // } + // } + // SecurityTokenAttachmentMode attachmentMode = this.incomingSupportingTokens[i].SecurityTokenAttachmentMode; + // // a safety net in case more attachment modes get added to the product without + // // reviewing this code. + // if (attachmentMode == SecurityTokenAttachmentMode.Endorsing + // || attachmentMode == SecurityTokenAttachmentMode.Signed + // || attachmentMode == SecurityTokenAttachmentMode.SignedEncrypted + // || attachmentMode == SecurityTokenAttachmentMode.SignedEndorsing) + // { + // AddAuthorizationPolicies(this.incomingSupportingTokens[i], policies); + // } + // } + // } + // return new ReadOnlyCollection(policies); + //} + + public void Dispose() + { + // do no-op for future V2 + if (!disposed) + { + disposed = true; + } + } + + void ThrowIfDisposed() + { + if (disposed) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ObjectDisposedException(GetType().FullName)); + } + } + } + +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Security/SecurityNegotiationException.cs b/src/CoreWCF.Primitives/src/CoreWCF/Security/SecurityNegotiationException.cs new file mode 100644 index 000000000..998b61011 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Security/SecurityNegotiationException.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; +using System.Text; + +namespace CoreWCF.Security +{ + [Serializable] + public class SecurityNegotiationException : CommunicationException + { + public SecurityNegotiationException() + : base() + { + } + + public SecurityNegotiationException(string message) + : base(message) + { + } + + public SecurityNegotiationException(string message, Exception innerException) + : base(message, innerException) + { + } + + protected SecurityNegotiationException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Security/SecurityTokenSpecification.cs b/src/CoreWCF.Primitives/src/CoreWCF/Security/SecurityTokenSpecification.cs new file mode 100644 index 000000000..85ce9a29b --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Security/SecurityTokenSpecification.cs @@ -0,0 +1,35 @@ +using CoreWCF.IdentityModel.Policy; +using CoreWCF.IdentityModel.Tokens; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Text; + +namespace CoreWCF.Security +{ + internal class SecurityTokenSpecification + { + SecurityToken token; + ReadOnlyCollection tokenPolicies; + + public SecurityTokenSpecification(SecurityToken token, ReadOnlyCollection tokenPolicies) + { + this.token = token; + if (tokenPolicies == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("tokenPolicies"); + } + this.tokenPolicies = tokenPolicies; + } + + public SecurityToken SecurityToken + { + get { return token; } + } + + public ReadOnlyCollection SecurityTokenPolicies + { + get { return tokenPolicies; } + } + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Security/SecurityUtils.cs b/src/CoreWCF.Primitives/src/CoreWCF/Security/SecurityUtils.cs new file mode 100644 index 000000000..cd63fd110 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Security/SecurityUtils.cs @@ -0,0 +1,632 @@ +using System; +using System.Security.Authentication; +using System.ComponentModel; +using System.Net.Security; +using System.Security.Cryptography.X509Certificates; +using System.Security.Cryptography; +using CoreWCF.IdentityModel.Claims; +using CoreWCF.IdentityModel.Selectors; +using System.Threading; +using System.Threading.Tasks; +using System.Collections.ObjectModel; +using CoreWCF.IdentityModel.Policy; +using System.Security.Principal; +using System.Diagnostics; +using System.Net; +using CoreWCF.IdentityModel.Tokens; +using System.Globalization; +using CoreWCF.Channels; +using System.DirectoryServices.ActiveDirectory; +using CoreWCF.Runtime; + +namespace CoreWCF.Security +{ + static class ProtectionLevelHelper + { + internal static bool IsDefined(ProtectionLevel value) + { + return (value == ProtectionLevel.None + || value == ProtectionLevel.Sign + || value == ProtectionLevel.EncryptAndSign); + } + + internal static void Validate(ProtectionLevel value) + { + if (!IsDefined(value)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidEnumArgumentException("value", (int)value, + typeof(ProtectionLevel))); + } + } + + internal static bool IsStronger(ProtectionLevel v1, ProtectionLevel v2) + { + return ((v1 == ProtectionLevel.EncryptAndSign && v2 != ProtectionLevel.EncryptAndSign) + || (v1 == ProtectionLevel.Sign && v2 == ProtectionLevel.None)); + } + + internal static bool IsStrongerOrEqual(ProtectionLevel v1, ProtectionLevel v2) + { + return (v1 == ProtectionLevel.EncryptAndSign + || (v1 == ProtectionLevel.Sign && v2 != ProtectionLevel.EncryptAndSign)); + } + + internal static ProtectionLevel Max(ProtectionLevel v1, ProtectionLevel v2) + { + return IsStronger(v1, v2) ? v1 : v2; + } + + internal static int GetOrdinal(Nullable p) + { + if (p.HasValue) + { + switch ((ProtectionLevel)p) + { + default: + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidEnumArgumentException("p", (int)p, typeof(ProtectionLevel))); + case ProtectionLevel.None: + return 2; + case ProtectionLevel.Sign: + return 3; + case ProtectionLevel.EncryptAndSign: + return 4; + } + } + else + return 1; + } + } + + static class SslProtocolsHelper + { + internal static bool IsDefined(SslProtocols value) + { + SslProtocols allValues = SslProtocols.None; + foreach (var protocol in Enum.GetValues(typeof(SslProtocols))) + { + allValues |= (SslProtocols)protocol; + } + return (value & allValues) == value; + } + + internal static void Validate(SslProtocols value) + { + if (!IsDefined(value)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidEnumArgumentException("value", (int)value, + typeof(SslProtocols))); + } + } + } + public class SecurityUtils + { + public const string Identities = "Identities"; + static bool computedDomain; + static string currentDomain; + static IIdentity anonymousIdentity; + static X509SecurityTokenAuthenticator nonValidatingX509Authenticator; + + internal static IIdentity AnonymousIdentity + { + get + { + if (anonymousIdentity == null) + { + anonymousIdentity = SecurityUtils.CreateIdentity(string.Empty); + } + return anonymousIdentity; + } + } + + internal static X509SecurityTokenAuthenticator NonValidatingX509Authenticator + { + get + { + if (nonValidatingX509Authenticator == null) + { + nonValidatingX509Authenticator = new X509SecurityTokenAuthenticator(X509CertificateValidator.None); + } + return nonValidatingX509Authenticator; + } + } + + internal static IIdentity CreateIdentity(string name) + { + return new GenericIdentity(name); + } + + internal static EndpointIdentity CreateWindowsIdentity() + { + return CreateWindowsIdentity(false); + } + + internal static EndpointIdentity CreateWindowsIdentity(NetworkCredential serverCredential) + { + if (serverCredential != null && !NetworkCredentialHelper.IsDefault(serverCredential)) + { + string upn; + if (serverCredential.Domain != null && serverCredential.Domain.Length > 0) + { + upn = serverCredential.UserName + "@" + serverCredential.Domain; + } + else + { + upn = serverCredential.UserName; + } + return EndpointIdentity.CreateUpnIdentity(upn); + } + else + { + return SecurityUtils.CreateWindowsIdentity(); + } + } + + private static bool IsSystemAccount(WindowsIdentity self) + { + SecurityIdentifier sid = self.User; + if (sid == null) + { + return false; + } + // S-1-5-82 is the prefix for the sid that represents the identity that IIS 7.5 Apppool thread runs under. + return (sid.IsWellKnown(WellKnownSidType.LocalSystemSid) + || sid.IsWellKnown(WellKnownSidType.NetworkServiceSid) + || sid.IsWellKnown(WellKnownSidType.LocalServiceSid) + || self.User.Value.StartsWith("S-1-5-82", StringComparison.OrdinalIgnoreCase)); + } + + internal static EndpointIdentity CreateWindowsIdentity(bool spnOnly) + { + EndpointIdentity identity = null; + using (WindowsIdentity self = WindowsIdentity.GetCurrent()) + { + bool isSystemAccount = IsSystemAccount(self); + if (spnOnly || isSystemAccount) + { + identity = EndpointIdentity.CreateSpnIdentity(String.Format(CultureInfo.InvariantCulture, "host/{0}", DnsCache.MachineName)); + } + else + { + // Save windowsIdentity for delay lookup + identity = new UpnEndpointIdentity(CloneWindowsIdentityIfNecessary(self)); + } + } + + return identity; + } + + internal static WindowsIdentity CloneWindowsIdentityIfNecessary(WindowsIdentity wid) + { + return SecurityUtils.CloneWindowsIdentityIfNecessary(wid, null); + } + + internal static WindowsIdentity CloneWindowsIdentityIfNecessary(WindowsIdentity wid, string authType) + { + if (wid != null) + { + IntPtr token = UnsafeGetWindowsIdentityToken(wid); + if (token != IntPtr.Zero) + { + return UnsafeCreateWindowsIdentityFromToken(token, authType); + } + } + return wid; + } + + static IntPtr UnsafeGetWindowsIdentityToken(WindowsIdentity wid) + { + return wid.Token; + } + + static WindowsIdentity UnsafeCreateWindowsIdentityFromToken(IntPtr token, string authType) + { + if (authType != null) + return new WindowsIdentity(token, authType); + else + return new WindowsIdentity(token); + } + + internal static Claim GetPrimaryIdentityClaim(ReadOnlyCollection authorizationPolicies) + { + return GetPrimaryIdentityClaim(AuthorizationContext.CreateDefaultAuthorizationContext(authorizationPolicies)); + } + + internal static Claim GetPrimaryIdentityClaim(AuthorizationContext authContext) + { + if (authContext != null) + { + for (int i = 0; i < authContext.ClaimSets.Count; ++i) + { + ClaimSet claimSet = authContext.ClaimSets[i]; + foreach (Claim claim in claimSet.FindClaims(null, Rights.Identity)) + { + return claim; + } + } + } + return null; + } + + internal static string GetPrimaryDomain() + { + using (WindowsIdentity wid = WindowsIdentity.GetCurrent()) + { + return GetPrimaryDomain(IsSystemAccount(wid)); + } + } + + internal static string GetPrimaryDomain(bool isSystemAccount) + { + if (computedDomain == false) + { + try + { + if (isSystemAccount) + { + currentDomain = Domain.GetComputerDomain().Name; + } + else + { + currentDomain = Domain.GetCurrentDomain().Name; + } + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + DiagnosticUtility.TraceHandledException(e, TraceEventType.Warning); + } + finally + { + computedDomain = true; + } + } + return currentDomain; + } + + internal static void EnsureCertificateCanDoKeyExchange(X509Certificate2 certificate) + { + if (certificate == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(certificate)); + } + bool canDoKeyExchange = false; + Exception innerException = null; + if (certificate.HasPrivateKey) + { + try + { + canDoKeyExchange = CanKeyDoKeyExchange(certificate); + } + // exceptions can be due to ACLs on the key etc + catch (System.Security.SecurityException e) + { + innerException = e; + } + catch (CryptographicException e) + { + innerException = e; + } + } + if (!canDoKeyExchange) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.Format(SR.SslCertMayNotDoKeyExchange, certificate.SubjectName.Name), innerException)); + } + } + + static bool CanKeyDoKeyExchange(X509Certificate2 certificate) + { + bool canDoKeyExchange = false; + + X509KeyUsageExtension keyUsageExtension = null; + for (int i = 0; i < certificate.Extensions.Count; i++) + { + keyUsageExtension = certificate.Extensions[i] as X509KeyUsageExtension; + if (keyUsageExtension != null) + { + break; + } + } + + // No KeyUsage extension means most usages are permitted including key exchange. + // See RFC 5280 section 4.2.1.3 (Key Usage) for details. If the extension is non-critical + // then it's non-enforcing and meant as an aid in choosing the best certificate when + // there are multiple certificates to choose from. + if (keyUsageExtension == null || !keyUsageExtension.Critical) + { + return true; + } + + // One of KeyAgreement, KeyEncipherment or DigitalSignature need to be allowed depending on the cipher + // being used. See RFC 5246 section 7.4.6 for more details. + // Additionally, according to msdn docs for PFXImportCertStore, the key specification is set to AT_KEYEXCHANGE + // when the data encipherment usage is set. + canDoKeyExchange = (keyUsageExtension.KeyUsages & + (X509KeyUsageFlags.KeyAgreement | X509KeyUsageFlags.KeyEncipherment | + X509KeyUsageFlags.DigitalSignature | X509KeyUsageFlags.DataEncipherment)) != X509KeyUsageFlags.None; + + return canDoKeyExchange; + } + + internal static NetworkCredential GetNetworkCredentialsCopy(NetworkCredential networkCredential) + { + NetworkCredential result; + if (networkCredential != null && !NetworkCredentialHelper.IsDefault(networkCredential)) + { + result = new NetworkCredential(NetworkCredentialHelper.UnsafeGetUsername(networkCredential), NetworkCredentialHelper.UnsafeGetPassword(networkCredential), NetworkCredentialHelper.UnsafeGetDomain(networkCredential)); + } + else + { + result = networkCredential; + } + return result; + } + + static class NetworkCredentialHelper + { + static internal bool IsNullOrEmpty(NetworkCredential credential) + { + return credential == null || + ( + String.IsNullOrEmpty(UnsafeGetUsername(credential)) && + String.IsNullOrEmpty(UnsafeGetDomain(credential)) && + String.IsNullOrEmpty(UnsafeGetPassword(credential)) + ); + } + + static internal bool IsDefault(NetworkCredential credential) + { + return UnsafeGetDefaultNetworkCredentials().Equals(credential); + } + + static internal string UnsafeGetUsername(NetworkCredential credential) + { + return credential.UserName; + } + + static internal string UnsafeGetPassword(NetworkCredential credential) + { + return credential.Password; + } + + static internal string UnsafeGetDomain(NetworkCredential credential) + { + return credential.Domain; + } + + static NetworkCredential UnsafeGetDefaultNetworkCredentials() + { + return CredentialCache.DefaultNetworkCredentials; + } + } + + internal static X509Certificate2 GetCertificateFromStore(StoreName storeName, StoreLocation storeLocation, + X509FindType findType, object findValue, EndpointAddress target) + { + X509Certificate2 certificate = GetCertificateFromStoreCore(storeName, storeLocation, findType, findValue, target, true); + if (certificate == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.CannotFindCert, storeName, storeLocation, findType, findValue))); + + return certificate; + } + + static X509Certificate2 GetCertificateFromStoreCore(StoreName storeName, StoreLocation storeLocation, + X509FindType findType, object findValue, EndpointAddress target, bool throwIfMultipleOrNoMatch) + { + if (findValue == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(findValue)); + } + X509Store store = new X509Store(storeName, storeLocation); + X509Certificate2Collection certs = null; + try + { + store.Open(OpenFlags.ReadOnly); + certs = store.Certificates.Find(findType, findValue, false); + if (certs.Count == 1) + { + return new X509Certificate2(certs[0]); + } + if (throwIfMultipleOrNoMatch) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateCertificateLoadException( + storeName, storeLocation, findType, findValue, target, certs.Count)); + } + else + { + return null; + } + } + finally + { + SecurityUtils.ResetAllCertificates(certs); + store.Close(); + } + } + + static Exception CreateCertificateLoadException(StoreName storeName, StoreLocation storeLocation, + X509FindType findType, object findValue, EndpointAddress target, int certCount) + { + if (certCount == 0) + { + if (target == null) + { + return new InvalidOperationException(SR.Format(SR.CannotFindCert, storeName, storeLocation, findType, findValue)); + } + else + { + return new InvalidOperationException(SR.Format(SR.CannotFindCertForTarget, storeName, storeLocation, findType, findValue, target)); + } + } + else + { + if (target == null) + { + return new InvalidOperationException(SR.Format(SR.FoundMultipleCerts, storeName, storeLocation, findType, findValue)); + } + else + { + return new InvalidOperationException(SR.Format(SR.FoundMultipleCertsForTarget, storeName, storeLocation, findType, findValue, target)); + } + } + } + + // This is the workaround, Since store.Certificates returns a full collection + // of certs in store. These are holding native resources. + internal static void ResetAllCertificates(X509Certificate2Collection certificates) + { + if (certificates != null) + { + for (int i = 0; i < certificates.Count; ++i) + { + ResetCertificate(certificates[i]); + } + } + } + + internal static void ResetCertificate(X509Certificate2 certificate) + { + certificate.Reset(); + } + + static bool TryCreateIdentity(ClaimSet claimSet, string claimType, out EndpointIdentity identity) + { + identity = null; + foreach (Claim claim in claimSet.FindClaims(claimType, null)) + { + identity = EndpointIdentity.CreateIdentity(claim); + return true; + } + return false; + } + + internal static void FixNetworkCredential(ref NetworkCredential credential) + { + if (credential == null) + { + return; + } + string username = NetworkCredentialHelper.UnsafeGetUsername(credential); + string domain = NetworkCredentialHelper.UnsafeGetDomain(credential); + if (!string.IsNullOrEmpty(username) && string.IsNullOrEmpty(domain)) + { + // do the splitting only if there is exactly 1 \ or exactly 1 @ + string[] partsWithSlashDelimiter = username.Split('\\'); + string[] partsWithAtDelimiter = username.Split('@'); + if (partsWithSlashDelimiter.Length == 2 && partsWithAtDelimiter.Length == 1) + { + if (!string.IsNullOrEmpty(partsWithSlashDelimiter[0]) && !string.IsNullOrEmpty(partsWithSlashDelimiter[1])) + { + credential = new NetworkCredential(partsWithSlashDelimiter[1], NetworkCredentialHelper.UnsafeGetPassword(credential), partsWithSlashDelimiter[0]); + } + } + else if (partsWithSlashDelimiter.Length == 1 && partsWithAtDelimiter.Length == 2) + { + if (!string.IsNullOrEmpty(partsWithAtDelimiter[0]) && !string.IsNullOrEmpty(partsWithAtDelimiter[1])) + { + credential = new NetworkCredential(partsWithAtDelimiter[0], NetworkCredentialHelper.UnsafeGetPassword(credential), partsWithAtDelimiter[1]); + } + } + } + } + + internal static Task OpenTokenProviderIfRequiredAsync(SecurityTokenProvider tokenProvider, CancellationToken token) + { + return OpenCommunicationObjectAsync(tokenProvider as ICommunicationObject, token); + } + + internal static Task CloseTokenProviderIfRequiredAsync(SecurityTokenProvider tokenProvider, CancellationToken token) + { + return CloseCommunicationObjectAsync(tokenProvider, false, token); + } + + internal static void AbortTokenAuthenticatorIfRequired(SecurityTokenAuthenticator tokenAuthenticator) + { + CloseCommunicationObjectAsync(tokenAuthenticator, true, CancellationToken.None).GetAwaiter().GetResult(); + } + + internal static void AbortTokenProviderIfRequired(SecurityTokenProvider tokenProvider) + { + CloseCommunicationObjectAsync(tokenProvider, true, CancellationToken.None).GetAwaiter().GetResult(); + } + + internal static Task OpenTokenAuthenticatorIfRequiredAsync(SecurityTokenAuthenticator tokenAuthenticator, CancellationToken token) + { + return OpenCommunicationObjectAsync(tokenAuthenticator as ICommunicationObject, token); + } + + internal static Task CloseTokenAuthenticatorIfRequiredAsync(SecurityTokenAuthenticator tokenAuthenticator, CancellationToken token) + { + return CloseTokenAuthenticatorIfRequiredAsync(tokenAuthenticator, false, token); + } + + internal static Task CloseTokenAuthenticatorIfRequiredAsync(SecurityTokenAuthenticator tokenAuthenticator, bool aborted, CancellationToken token) + { + return CloseCommunicationObjectAsync(tokenAuthenticator, aborted, token); + } + + static Task OpenCommunicationObjectAsync(ICommunicationObject obj, CancellationToken token) + { + if (obj != null) + return obj.OpenAsync(token); + + return Task.CompletedTask; + } + + static Task CloseCommunicationObjectAsync(object obj, bool aborted, CancellationToken token) + { + if (obj != null) + { + ICommunicationObject co = obj as ICommunicationObject; + if (co != null) + { + if (aborted) + { + try + { + co.Abort(); + } + catch (CommunicationException e) + { + DiagnosticUtility.TraceHandledException(e, TraceEventType.Information); + } + } + else + { + return co.CloseAsync(token); + } + } + else if (obj is IDisposable) + { + ((IDisposable)obj).Dispose(); + } + } + + return Task.CompletedTask; + } + + internal static EndpointIdentity GetServiceCertificateIdentity(X509Certificate2 certificate) + { + using (X509CertificateClaimSet claimSet = new X509CertificateClaimSet(certificate)) + { + EndpointIdentity identity; + if (!TryCreateIdentity(claimSet, ClaimTypes.Dns, out identity)) + { + TryCreateIdentity(claimSet, ClaimTypes.Rsa, out identity); + } + return identity; + } + } + + public static void ValidateAnonymityConstraint(WindowsIdentity identity, bool allowUnauthenticatedCallers) + { + if (!allowUnauthenticatedCallers && identity.User.IsWellKnown(WellKnownSidType.AnonymousSid)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning( + new SecurityTokenValidationException(SR.AnonymousLogonsAreNotAllowed)); + } + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Security/ServiceCredentialsSecurityTokenManager.cs b/src/CoreWCF.Primitives/src/CoreWCF/Security/ServiceCredentialsSecurityTokenManager.cs new file mode 100644 index 000000000..8436b7a59 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Security/ServiceCredentialsSecurityTokenManager.cs @@ -0,0 +1,526 @@ +using CoreWCF.IdentityModel.Selectors; +using CoreWCF.IdentityModel.Tokens; +using CoreWCF.Channels; +using CoreWCF.Description; +using CoreWCF.Security.Tokens; +using System; +using System.Collections.Generic; +using System.Net; +using System.Text; + +namespace CoreWCF.Security +{ + internal class ServiceCredentialsSecurityTokenManager : SecurityTokenManager, IEndpointIdentityProvider + { + ServiceCredentials parent; + + public ServiceCredentialsSecurityTokenManager(ServiceCredentials parent) + { + if (parent == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("parent"); + } + this.parent = parent; + } + + public ServiceCredentials ServiceCredentials + { + get { return parent; } + } + + internal override SecurityTokenSerializer CreateSecurityTokenSerializer(SecurityTokenVersion version) + { + if (version == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("version"); + } + + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.Format(SR.SecurityTokenManagerCannotCreateSerializerForVersion, version))); + } + + //protected SecurityTokenAuthenticator CreateSecureConversationTokenAuthenticator(RecipientServiceModelSecurityTokenRequirement recipientRequirement, bool preserveBootstrapTokens, out SecurityTokenResolver sctResolver) + //{ + // SecurityBindingElement securityBindingElement = recipientRequirement.SecurityBindingElement; + // if (securityBindingElement == null) + // { + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.Format(SR.TokenAuthenticatorRequiresSecurityBindingElement, recipientRequirement)); + // } + // bool isCookieMode = !recipientRequirement.SupportSecurityContextCancellation; + // LocalServiceSecuritySettings localServiceSettings = securityBindingElement.LocalServiceSettings; + // IMessageFilterTable endpointFilterTable = recipientRequirement.GetPropertyOrDefault>(ServiceModelSecurityTokenRequirement.EndpointFilterTableProperty, null); + + // if (!isCookieMode) + // { + // sctResolver = new SecurityContextSecurityTokenResolver(Int32.MaxValue, false); + + // // remember this authenticator for future reference + // SecuritySessionSecurityTokenAuthenticator authenticator = new SecuritySessionSecurityTokenAuthenticator(); + // authenticator.BootstrapSecurityBindingElement = SecurityUtils.GetIssuerSecurityBindingElement(recipientRequirement); + // authenticator.IssuedSecurityTokenParameters = recipientRequirement.GetProperty(ServiceModelSecurityTokenRequirement.IssuedSecurityTokenParametersProperty); + // authenticator.IssuedTokenCache = (ISecurityContextSecurityTokenCache)sctResolver; + // authenticator.IssuerBindingContext = recipientRequirement.GetProperty(ServiceModelSecurityTokenRequirement.IssuerBindingContextProperty); + // authenticator.KeyEntropyMode = securityBindingElement.KeyEntropyMode; + // authenticator.ListenUri = recipientRequirement.ListenUri; + // authenticator.SecurityAlgorithmSuite = recipientRequirement.SecurityAlgorithmSuite; + // authenticator.SessionTokenLifetime = TimeSpan.MaxValue; + // authenticator.KeyRenewalInterval = securityBindingElement.LocalServiceSettings.SessionKeyRenewalInterval; + // authenticator.StandardsManager = SecurityUtils.CreateSecurityStandardsManager(recipientRequirement, this); + // authenticator.EndpointFilterTable = endpointFilterTable; + // authenticator.MaximumConcurrentNegotiations = localServiceSettings.MaxStatefulNegotiations; + // authenticator.NegotiationTimeout = localServiceSettings.NegotiationTimeout; + // authenticator.PreserveBootstrapTokens = preserveBootstrapTokens; + // return authenticator; + // } + // else + // { + // sctResolver = new SecurityContextSecurityTokenResolver(localServiceSettings.MaxCachedCookies, true, localServiceSettings.MaxClockSkew); + + // AcceleratedTokenAuthenticator authenticator = new AcceleratedTokenAuthenticator(); + // authenticator.BootstrapSecurityBindingElement = SecurityUtils.GetIssuerSecurityBindingElement(recipientRequirement); + // authenticator.KeyEntropyMode = securityBindingElement.KeyEntropyMode; + // authenticator.EncryptStateInServiceToken = true; + // authenticator.IssuedSecurityTokenParameters = recipientRequirement.GetProperty(ServiceModelSecurityTokenRequirement.IssuedSecurityTokenParametersProperty); + // authenticator.IssuedTokenCache = (ISecurityContextSecurityTokenCache)sctResolver; + // authenticator.IssuerBindingContext = recipientRequirement.GetProperty(ServiceModelSecurityTokenRequirement.IssuerBindingContextProperty); + // authenticator.ListenUri = recipientRequirement.ListenUri; + // authenticator.SecurityAlgorithmSuite = recipientRequirement.SecurityAlgorithmSuite; + // authenticator.StandardsManager = SecurityUtils.CreateSecurityStandardsManager(recipientRequirement, this); + // authenticator.SecurityStateEncoder = parent.SecureConversationAuthentication.SecurityStateEncoder; + // authenticator.KnownTypes = parent.SecureConversationAuthentication.SecurityContextClaimTypes; + // authenticator.PreserveBootstrapTokens = preserveBootstrapTokens; + + // // local security quotas + // authenticator.MaximumCachedNegotiationState = localServiceSettings.MaxStatefulNegotiations; + // authenticator.NegotiationTimeout = localServiceSettings.NegotiationTimeout; + // authenticator.ServiceTokenLifetime = localServiceSettings.IssuedCookieLifetime; + // authenticator.MaximumConcurrentNegotiations = localServiceSettings.MaxStatefulNegotiations; + + // // audit settings + // authenticator.AuditLogLocation = recipientRequirement.AuditLogLocation; + // authenticator.SuppressAuditFailure = recipientRequirement.SuppressAuditFailure; + // authenticator.MessageAuthenticationAuditLevel = recipientRequirement.MessageAuthenticationAuditLevel; + // authenticator.EndpointFilterTable = endpointFilterTable; + // return authenticator; + // } + //} + + SecurityTokenAuthenticator CreateSpnegoSecurityTokenAuthenticator(RecipientServiceModelSecurityTokenRequirement recipientRequirement, out SecurityTokenResolver sctResolver) + { + throw new PlatformNotSupportedException("SpnegoSecurityTokenAuthenticator"); + //SecurityBindingElement securityBindingElement = recipientRequirement.SecurityBindingElement; + //if (securityBindingElement == null) + //{ + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.Format(SR.TokenAuthenticatorRequiresSecurityBindingElement, recipientRequirement)); + //} + //bool isCookieMode = !recipientRequirement.SupportSecurityContextCancellation; + //LocalServiceSecuritySettings localServiceSettings = securityBindingElement.LocalServiceSettings; + //sctResolver = new SecurityContextSecurityTokenResolver(localServiceSettings.MaxCachedCookies, true); + //ExtendedProtectionPolicy extendedProtectionPolicy = null; + //recipientRequirement.TryGetProperty(ServiceModelSecurityTokenRequirement.ExtendedProtectionPolicy, out extendedProtectionPolicy); + + //SpnegoTokenAuthenticator authenticator = new SpnegoTokenAuthenticator(); + //authenticator.ExtendedProtectionPolicy = extendedProtectionPolicy; + //authenticator.AllowUnauthenticatedCallers = parent.WindowsAuthentication.AllowAnonymousLogons; + //authenticator.ExtractGroupsForWindowsAccounts = parent.WindowsAuthentication.IncludeWindowsGroups; + //authenticator.IsClientAnonymous = false; + //authenticator.EncryptStateInServiceToken = isCookieMode; + //authenticator.IssuedSecurityTokenParameters = recipientRequirement.GetProperty(ServiceModelSecurityTokenRequirement.IssuedSecurityTokenParametersProperty); + //authenticator.IssuedTokenCache = (ISecurityContextSecurityTokenCache)sctResolver; + //authenticator.IssuerBindingContext = recipientRequirement.GetProperty(ServiceModelSecurityTokenRequirement.IssuerBindingContextProperty); + //authenticator.ListenUri = recipientRequirement.ListenUri; + //authenticator.SecurityAlgorithmSuite = recipientRequirement.SecurityAlgorithmSuite; + //authenticator.StandardsManager = SecurityUtils.CreateSecurityStandardsManager(recipientRequirement, this); + //authenticator.SecurityStateEncoder = parent.SecureConversationAuthentication.SecurityStateEncoder; + //authenticator.KnownTypes = parent.SecureConversationAuthentication.SecurityContextClaimTypes; + //// if the SPNEGO is being done in mixed-mode, the nego blobs are from an anonymous client and so there size bound needs to be enforced. + //if (securityBindingElement is TransportSecurityBindingElement) + //{ + // authenticator.MaxMessageSize = SecurityUtils.GetMaxNegotiationBufferSize(authenticator.IssuerBindingContext); + //} + + //// local security quotas + //authenticator.MaximumCachedNegotiationState = localServiceSettings.MaxStatefulNegotiations; + //authenticator.NegotiationTimeout = localServiceSettings.NegotiationTimeout; + //authenticator.ServiceTokenLifetime = localServiceSettings.IssuedCookieLifetime; + //authenticator.MaximumConcurrentNegotiations = localServiceSettings.MaxStatefulNegotiations; + + //// audit settings + //authenticator.AuditLogLocation = recipientRequirement.AuditLogLocation; + //authenticator.SuppressAuditFailure = recipientRequirement.SuppressAuditFailure; + //authenticator.MessageAuthenticationAuditLevel = recipientRequirement.MessageAuthenticationAuditLevel; + //return authenticator; + } + + SecurityTokenAuthenticator CreateTlsnegoClientX509TokenAuthenticator(RecipientServiceModelSecurityTokenRequirement recipientRequirement) + { + throw new PlatformNotSupportedException("TlsnegoClientX509Token"); + //RecipientServiceModelSecurityTokenRequirement clientX509Requirement = new RecipientServiceModelSecurityTokenRequirement(); + //clientX509Requirement.TokenType = SecurityTokenTypes.X509Certificate; + //clientX509Requirement.KeyUsage = SecurityKeyUsage.Signature; + //clientX509Requirement.ListenUri = recipientRequirement.ListenUri; + //clientX509Requirement.KeyType = SecurityKeyType.AsymmetricKey; + //clientX509Requirement.SecurityBindingElement = recipientRequirement.SecurityBindingElement; + //SecurityTokenResolver dummy; + //return this.CreateSecurityTokenAuthenticator(clientX509Requirement, out dummy); + } + + SecurityTokenProvider CreateTlsnegoServerX509TokenProvider(RecipientServiceModelSecurityTokenRequirement recipientRequirement) + { + throw new PlatformNotSupportedException("TlsnegoServerX509Token"); + //RecipientServiceModelSecurityTokenRequirement serverX509Requirement = new RecipientServiceModelSecurityTokenRequirement(); + //serverX509Requirement.TokenType = SecurityTokenTypes.X509Certificate; + //serverX509Requirement.KeyUsage = SecurityKeyUsage.Exchange; + //serverX509Requirement.ListenUri = recipientRequirement.ListenUri; + //serverX509Requirement.KeyType = SecurityKeyType.AsymmetricKey; + //serverX509Requirement.SecurityBindingElement = recipientRequirement.SecurityBindingElement; + //return this.CreateSecurityTokenProvider(serverX509Requirement); + } + + SecurityTokenAuthenticator CreateTlsnegoSecurityTokenAuthenticator(RecipientServiceModelSecurityTokenRequirement recipientRequirement, bool requireClientCertificate, out SecurityTokenResolver sctResolver) + { + throw new PlatformNotSupportedException("TlsnegoSecurityToken"); + //SecurityBindingElement securityBindingElement = recipientRequirement.SecurityBindingElement; + //if (securityBindingElement == null) + //{ + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.Format(SR.TokenAuthenticatorRequiresSecurityBindingElement, recipientRequirement)); + //} + //bool isCookieMode = !recipientRequirement.SupportSecurityContextCancellation; + //LocalServiceSecuritySettings localServiceSettings = securityBindingElement.LocalServiceSettings; + //sctResolver = new SecurityContextSecurityTokenResolver(localServiceSettings.MaxCachedCookies, true); + + //TlsnegoTokenAuthenticator authenticator = new TlsnegoTokenAuthenticator(); + //authenticator.IsClientAnonymous = !requireClientCertificate; + //if (requireClientCertificate) + //{ + // authenticator.ClientTokenAuthenticator = this.CreateTlsnegoClientX509TokenAuthenticator(recipientRequirement); + // authenticator.MapCertificateToWindowsAccount = this.ServiceCredentials.ClientCertificate.Authentication.MapClientCertificateToWindowsAccount; + //} + //authenticator.EncryptStateInServiceToken = isCookieMode; + //authenticator.IssuedSecurityTokenParameters = recipientRequirement.GetProperty(ServiceModelSecurityTokenRequirement.IssuedSecurityTokenParametersProperty); + //authenticator.IssuedTokenCache = (ISecurityContextSecurityTokenCache)sctResolver; + //authenticator.IssuerBindingContext = recipientRequirement.GetProperty(ServiceModelSecurityTokenRequirement.IssuerBindingContextProperty); + //authenticator.ListenUri = recipientRequirement.ListenUri; + //authenticator.SecurityAlgorithmSuite = recipientRequirement.SecurityAlgorithmSuite; + //authenticator.StandardsManager = SecurityUtils.CreateSecurityStandardsManager(recipientRequirement, this); + //authenticator.SecurityStateEncoder = parent.SecureConversationAuthentication.SecurityStateEncoder; + //authenticator.KnownTypes = parent.SecureConversationAuthentication.SecurityContextClaimTypes; + //authenticator.ServerTokenProvider = CreateTlsnegoServerX509TokenProvider(recipientRequirement); + //// local security quotas + //authenticator.MaximumCachedNegotiationState = localServiceSettings.MaxStatefulNegotiations; + //authenticator.NegotiationTimeout = localServiceSettings.NegotiationTimeout; + //authenticator.ServiceTokenLifetime = localServiceSettings.IssuedCookieLifetime; + //authenticator.MaximumConcurrentNegotiations = localServiceSettings.MaxStatefulNegotiations; + //// if the TLSNEGO is being done in mixed-mode, the nego blobs are from an anonymous client and so there size bound needs to be enforced. + //if (securityBindingElement is TransportSecurityBindingElement) + //{ + // authenticator.MaxMessageSize = SecurityUtils.GetMaxNegotiationBufferSize(authenticator.IssuerBindingContext); + //} + //// audit settings + //authenticator.AuditLogLocation = recipientRequirement.AuditLogLocation; + //authenticator.SuppressAuditFailure = recipientRequirement.SuppressAuditFailure; + //authenticator.MessageAuthenticationAuditLevel = recipientRequirement.MessageAuthenticationAuditLevel; + //return authenticator; + } + + X509SecurityTokenAuthenticator CreateClientX509TokenAuthenticator() + { + X509ClientCertificateAuthentication authentication = parent.ClientCertificate.Authentication; + return new X509SecurityTokenAuthenticator(authentication.GetCertificateValidator(), authentication.MapClientCertificateToWindowsAccount, authentication.IncludeWindowsGroups); + } + + //SamlSecurityTokenAuthenticator CreateSamlTokenAuthenticator(RecipientServiceModelSecurityTokenRequirement recipientRequirement, out SecurityTokenResolver outOfBandTokenResolver) + //{ + // if (recipientRequirement == null) + // throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("recipientRequirement"); + + // Collection outOfBandTokens = new Collection(); + // if (parent.ServiceCertificate.Certificate != null) + // { + // outOfBandTokens.Add(new X509SecurityToken(parent.ServiceCertificate.Certificate)); + // } + // List supportingAuthenticators = new List(); + // if ((parent.IssuedTokenAuthentication.KnownCertificates != null) && (parent.IssuedTokenAuthentication.KnownCertificates.Count > 0)) + // { + // for (int i = 0; i < parent.IssuedTokenAuthentication.KnownCertificates.Count; ++i) + // { + // outOfBandTokens.Add(new X509SecurityToken(parent.IssuedTokenAuthentication.KnownCertificates[i])); + // } + // } + + // X509CertificateValidator validator = parent.IssuedTokenAuthentication.GetCertificateValidator(); + // supportingAuthenticators.Add(new X509SecurityTokenAuthenticator(validator)); + + // if (parent.IssuedTokenAuthentication.AllowUntrustedRsaIssuers) + // { + // supportingAuthenticators.Add(new RsaSecurityTokenAuthenticator()); + // } + + // outOfBandTokenResolver = (outOfBandTokens.Count > 0) ? SecurityTokenResolver.CreateDefaultSecurityTokenResolver(new ReadOnlyCollection(outOfBandTokens), false) : null; + + // SamlSecurityTokenAuthenticator ssta; + + // if ((recipientRequirement.SecurityBindingElement == null) || (recipientRequirement.SecurityBindingElement.LocalServiceSettings == null)) + // { + // ssta = new SamlSecurityTokenAuthenticator(supportingAuthenticators); + // } + // else + // { + // ssta = new SamlSecurityTokenAuthenticator(supportingAuthenticators, recipientRequirement.SecurityBindingElement.LocalServiceSettings.MaxClockSkew); + // } + + // // set audience uri restrictions + // ssta.AudienceUriMode = parent.IssuedTokenAuthentication.AudienceUriMode; + // IList allowedAudienceUris = ssta.AllowedAudienceUris; + // if (parent.IssuedTokenAuthentication.AllowedAudienceUris != null) + // { + // for (int i = 0; i < parent.IssuedTokenAuthentication.AllowedAudienceUris.Count; i++) + // allowedAudienceUris.Add(parent.IssuedTokenAuthentication.AllowedAudienceUris[i]); + // } + + // if (recipientRequirement.ListenUri != null) + // { + // allowedAudienceUris.Add(recipientRequirement.ListenUri.AbsoluteUri); + // } + + // return ssta; + //} + + X509SecurityTokenProvider CreateServerX509TokenProvider() + { + if (parent.ServiceCertificate.Certificate == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.ServiceCertificateNotProvidedOnServiceCredentials)); + } + SecurityUtils.EnsureCertificateCanDoKeyExchange(parent.ServiceCertificate.Certificate); + return new ServiceX509SecurityTokenProvider(parent.ServiceCertificate.Certificate); + } + + protected bool IsIssuedSecurityTokenRequirement(SecurityTokenRequirement requirement) + { + return (requirement != null && requirement.Properties.ContainsKey(ServiceModelSecurityTokenRequirement.IssuerAddressProperty)); + } + + public override SecurityTokenAuthenticator CreateSecurityTokenAuthenticator(SecurityTokenRequirement tokenRequirement, out SecurityTokenResolver outOfBandTokenResolver) + { + if (tokenRequirement == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("tokenRequirement"); + } + string tokenType = tokenRequirement.TokenType; + outOfBandTokenResolver = null; + SecurityTokenAuthenticator result = null; + if (tokenRequirement is InitiatorServiceModelSecurityTokenRequirement) + { + // this is the uncorrelated duplex case in which the server is asking for + // an authenticator to validate its provisioned client certificate + if (tokenType == SecurityTokenTypes.X509Certificate && tokenRequirement.KeyUsage == SecurityKeyUsage.Exchange) + { + return new X509SecurityTokenAuthenticator(X509CertificateValidator.None, false); + } + } + + RecipientServiceModelSecurityTokenRequirement recipientRequirement = tokenRequirement as RecipientServiceModelSecurityTokenRequirement; + if (recipientRequirement == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.Format(SR.SecurityTokenManagerCannotCreateAuthenticatorForRequirement, tokenRequirement))); + } + if (tokenType == SecurityTokenTypes.X509Certificate) + { + result = CreateClientX509TokenAuthenticator(); + } + else if (tokenType == SecurityTokenTypes.Kerberos) + { + throw new PlatformNotSupportedException("KerberosSecurityTokenAuthenticator"); + //result = new KerberosSecurityTokenAuthenticatorWrapper( + // new KerberosSecurityTokenAuthenticator(parent.WindowsAuthentication.IncludeWindowsGroups)); + } + else if (tokenType == SecurityTokenTypes.UserName) + { + if (parent.UserNameAuthentication.UserNamePasswordValidationMode == UserNamePasswordValidationMode.Windows) + { + throw new PlatformNotSupportedException("UserNamePasswordValidationMode.Windows"); + //if (parent.UserNameAuthentication.CacheLogonTokens) + //{ + // result = new WindowsUserNameCachingSecurityTokenAuthenticator(parent.UserNameAuthentication.IncludeWindowsGroups, + // parent.UserNameAuthentication.MaxCachedLogonTokens, parent.UserNameAuthentication.CachedLogonTokenLifetime); + //} + //else + //{ + // result = new WindowsUserNameSecurityTokenAuthenticator(parent.UserNameAuthentication.IncludeWindowsGroups); + //} + } + else + { + result = new CustomUserNameSecurityTokenAuthenticator(parent.UserNameAuthentication.GetUserNamePasswordValidator()); + } + } + else if (tokenType == SecurityTokenTypes.Rsa) + { + result = new RsaSecurityTokenAuthenticator(); + } + else if (tokenType == ServiceModelSecurityTokenTypes.AnonymousSslnego) + { + result = CreateTlsnegoSecurityTokenAuthenticator(recipientRequirement, false, out outOfBandTokenResolver); + } + else if (tokenType == ServiceModelSecurityTokenTypes.MutualSslnego) + { + result = CreateTlsnegoSecurityTokenAuthenticator(recipientRequirement, true, out outOfBandTokenResolver); + } + else if (tokenType == ServiceModelSecurityTokenTypes.Spnego) + { + result = CreateSpnegoSecurityTokenAuthenticator(recipientRequirement, out outOfBandTokenResolver); + } + else if (tokenType == ServiceModelSecurityTokenTypes.SecureConversation) + { + throw new PlatformNotSupportedException("SecureConversation"); + //result = CreateSecureConversationTokenAuthenticator(recipientRequirement, false, out outOfBandTokenResolver); + } + else if ((tokenType == SecurityTokenTypes.Saml) + || (tokenType == SecurityXXX2005Strings.SamlTokenType) + || (tokenType == SecurityJan2004Strings.SamlUri) + || (tokenType == null && IsIssuedSecurityTokenRequirement(recipientRequirement))) + { + throw new PlatformNotSupportedException("SamlToken"); + //result = CreateSamlTokenAuthenticator(recipientRequirement, out outOfBandTokenResolver); + } + + if (result == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.Format(SR.SecurityTokenManagerCannotCreateAuthenticatorForRequirement, tokenRequirement))); + + return result; + } + + SecurityTokenProvider CreateLocalSecurityTokenProvider(RecipientServiceModelSecurityTokenRequirement recipientRequirement) + { + string tokenType = recipientRequirement.TokenType; + SecurityTokenProvider result = null; + if (tokenType == SecurityTokenTypes.X509Certificate) + { + result = CreateServerX509TokenProvider(); + } + else if (tokenType == ServiceModelSecurityTokenTypes.SspiCredential) + { + // if Transport Security, AuthenicationSchemes.Basic will look at parent.UserNameAuthentication settings. + AuthenticationSchemes authenticationScheme; + bool authenticationSchemeIdentified = recipientRequirement.TryGetProperty(ServiceModelSecurityTokenRequirement.HttpAuthenticationSchemeProperty, out authenticationScheme); + if (authenticationSchemeIdentified && + authenticationScheme.IsSet(AuthenticationSchemes.Basic) && + authenticationScheme.IsNotSet(AuthenticationSchemes.Digest | AuthenticationSchemes.Ntlm | AuthenticationSchemes.Negotiate)) + { + // create security token provider even when basic and Anonymous are enabled. + result = new SspiSecurityTokenProvider(null, parent.UserNameAuthentication.IncludeWindowsGroups, false); + } + else + { + if (authenticationSchemeIdentified && + authenticationScheme.IsSet(AuthenticationSchemes.Basic) && + parent.WindowsAuthentication.IncludeWindowsGroups != parent.UserNameAuthentication.IncludeWindowsGroups) + { + // Ensure there are no inconsistencies when Basic and (Digest and/or Ntlm and/or Negotiate) are both enabled + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.Format(SR.SecurityTokenProviderIncludeWindowsGroupsInconsistent, + (AuthenticationSchemes)authenticationScheme - AuthenticationSchemes.Basic, + parent.UserNameAuthentication.IncludeWindowsGroups, + parent.WindowsAuthentication.IncludeWindowsGroups))); + } + + result = new SspiSecurityTokenProvider(null, parent.WindowsAuthentication.IncludeWindowsGroups, parent.WindowsAuthentication.AllowAnonymousLogons); + } + } + return result; + } + + SecurityTokenProvider CreateUncorrelatedDuplexSecurityTokenProvider(InitiatorServiceModelSecurityTokenRequirement initiatorRequirement) + { + string tokenType = initiatorRequirement.TokenType; + SecurityTokenProvider result = null; + if (tokenType == SecurityTokenTypes.X509Certificate) + { + SecurityKeyUsage keyUsage = initiatorRequirement.KeyUsage; + if (keyUsage == SecurityKeyUsage.Exchange) + { + if (parent.ClientCertificate.Certificate == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.ClientCertificateNotProvidedOnServiceCredentials)); + } + + result = new X509SecurityTokenProvider(parent.ClientCertificate.Certificate); + } + else + { + // this is a request for the server's own cert for signing + result = CreateServerX509TokenProvider(); + } + } + return result; + } + + public override SecurityTokenProvider CreateSecurityTokenProvider(SecurityTokenRequirement requirement) + { + if (requirement == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("requirement"); + } + + RecipientServiceModelSecurityTokenRequirement recipientRequirement = requirement as RecipientServiceModelSecurityTokenRequirement; + SecurityTokenProvider result = null; + if (recipientRequirement != null) + { + result = CreateLocalSecurityTokenProvider(recipientRequirement); + } + else if (requirement is InitiatorServiceModelSecurityTokenRequirement) + { + result = CreateUncorrelatedDuplexSecurityTokenProvider((InitiatorServiceModelSecurityTokenRequirement)requirement); + } + + if (result == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.Format(SR.SecurityTokenManagerCannotCreateProviderForRequirement, requirement))); + } + return result; + } + + public virtual EndpointIdentity GetIdentityOfSelf(SecurityTokenRequirement tokenRequirement) + { + if (tokenRequirement == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("tokenRequirement"); + } + if (tokenRequirement is RecipientServiceModelSecurityTokenRequirement) + { + string tokenType = tokenRequirement.TokenType; + if (tokenType == SecurityTokenTypes.X509Certificate + || tokenType == ServiceModelSecurityTokenTypes.AnonymousSslnego + || tokenType == ServiceModelSecurityTokenTypes.MutualSslnego) + { + if (parent.ServiceCertificate.Certificate != null) + { + return EndpointIdentity.CreateX509CertificateIdentity(parent.ServiceCertificate.Certificate); + } + } + else if (tokenType == SecurityTokenTypes.Kerberos || tokenType == ServiceModelSecurityTokenTypes.Spnego) + { + // TODO: Add WindowsIdentity support here as it looks like it is doable + throw new PlatformNotSupportedException("WindowsIdentity"); + //return SecurityUtils.CreateWindowsIdentity(); + } + else if (tokenType == ServiceModelSecurityTokenTypes.SecureConversation) + { + throw new PlatformNotSupportedException("SecureConverstaion"); + //SecurityBindingElement securityBindingElement = ((RecipientServiceModelSecurityTokenRequirement)tokenRequirement).SecureConversationSecurityBindingElement; + //if (securityBindingElement != null) + //{ + // if (securityBindingElement == null || securityBindingElement is TransportSecurityBindingElement) + // { + // return null; + // } + // SecurityTokenParameters bootstrapProtectionParameters = (securityBindingElement is SymmetricSecurityBindingElement) ? ((SymmetricSecurityBindingElement)securityBindingElement).ProtectionTokenParameters : ((AsymmetricSecurityBindingElement)securityBindingElement).RecipientTokenParameters; + // SecurityTokenRequirement bootstrapRequirement = new RecipientServiceModelSecurityTokenRequirement(); + // bootstrapProtectionParameters.InitializeSecurityTokenRequirement(bootstrapRequirement); + // return GetIdentityOfSelf(bootstrapRequirement); + //} + } + } + return null; + } + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Security/SspiSecurityTokenProvider.cs b/src/CoreWCF.Primitives/src/CoreWCF/Security/SspiSecurityTokenProvider.cs new file mode 100644 index 000000000..b5814331b --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Security/SspiSecurityTokenProvider.cs @@ -0,0 +1,36 @@ +using CoreWCF.IdentityModel.Selectors; +using CoreWCF.IdentityModel.Tokens; +using CoreWCF.Security.Tokens; +using System; +using System.Collections.Generic; +using System.Net; +using System.Security.Principal; +using System.Text; + +namespace CoreWCF.Security +{ + public class SspiSecurityTokenProvider : SecurityTokenProvider + { + internal const bool DefaultAllowNtlm = true; + internal const bool DefaultExtractWindowsGroupClaims = true; + internal const bool DefaultAllowUnauthenticatedCallers = false; + SspiSecurityToken token; + + // client side ctor + public SspiSecurityTokenProvider(NetworkCredential credential, bool allowNtlm, TokenImpersonationLevel impersonationLevel) + { + this.token = new SspiSecurityToken(impersonationLevel, allowNtlm, credential); + } + + // service side ctor + public SspiSecurityTokenProvider(NetworkCredential credential, bool extractGroupsForWindowsAccounts, bool allowUnauthenticatedCallers) + { + this.token = new SspiSecurityToken(credential, extractGroupsForWindowsAccounts, allowUnauthenticatedCallers); + } + + protected override SecurityToken GetTokenCore(TimeSpan timeout) + { + return this.token; + } + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Security/Tokens/InitiatorServiceModelSecurityTokenRequirement.cs b/src/CoreWCF.Primitives/src/CoreWCF/Security/Tokens/InitiatorServiceModelSecurityTokenRequirement.cs new file mode 100644 index 000000000..3872da834 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Security/Tokens/InitiatorServiceModelSecurityTokenRequirement.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoreWCF.Security.Tokens +{ + internal sealed class InitiatorServiceModelSecurityTokenRequirement : ServiceModelSecurityTokenRequirement + { + //WebHeaderCollection webHeaderCollection; + + public InitiatorServiceModelSecurityTokenRequirement() + : base() + { + Properties.Add(IsInitiatorProperty, (object)true); + } + + public EndpointAddress TargetAddress + { + get + { + return GetPropertyOrDefault(TargetAddressProperty, null); + } + set + { + Properties[TargetAddressProperty] = value; + } + } + + public Uri Via + { + get + { + return GetPropertyOrDefault(ViaProperty, null); + } + set + { + Properties[ViaProperty] = value; + } + } + + internal bool IsOutOfBandToken + { + get + { + return GetPropertyOrDefault(IsOutOfBandTokenProperty, false); + } + set + { + Properties[IsOutOfBandTokenProperty] = value; + } + } + + internal bool PreferSslCertificateAuthenticator + { + get + { + return GetPropertyOrDefault(PreferSslCertificateAuthenticatorProperty, false); + } + set + { + Properties[PreferSslCertificateAuthenticatorProperty] = value; + } + } + + //internal WebHeaderCollection WebHeaders + //{ + // get + // { + // return this.webHeaderCollection; + // } + // set + // { + // this.webHeaderCollection = value; + // } + //} + + public override string ToString() + { + return InternalToString(); + } + } + +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Security/Tokens/RecipientServiceModelSecurityTokenRequirement.cs b/src/CoreWCF.Primitives/src/CoreWCF/Security/Tokens/RecipientServiceModelSecurityTokenRequirement.cs new file mode 100644 index 000000000..373cd8427 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Security/Tokens/RecipientServiceModelSecurityTokenRequirement.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoreWCF.Security.Tokens +{ + internal sealed class RecipientServiceModelSecurityTokenRequirement : ServiceModelSecurityTokenRequirement + { + public RecipientServiceModelSecurityTokenRequirement() + : base() + { + Properties.Add(IsInitiatorProperty, (object)false); + } + + public Uri ListenUri + { + get + { + return GetPropertyOrDefault(ListenUriProperty, null); + } + set + { + Properties[ListenUriProperty] = value; + } + } + + //public AuditLogLocation AuditLogLocation + //{ + // get + // { + // return GetPropertyOrDefault(AuditLogLocationProperty, ServiceSecurityAuditBehavior.defaultAuditLogLocation); + // } + // set + // { + // this.Properties[AuditLogLocationProperty] = value; + // } + //} + + //public bool SuppressAuditFailure + //{ + // get + // { + // return GetPropertyOrDefault(SuppressAuditFailureProperty, ServiceSecurityAuditBehavior.defaultSuppressAuditFailure); + // } + // set + // { + // this.Properties[SuppressAuditFailureProperty] = value; + // } + //} + + //public AuditLevel MessageAuthenticationAuditLevel + //{ + // get + // { + // return GetPropertyOrDefault(MessageAuthenticationAuditLevelProperty, ServiceSecurityAuditBehavior.defaultMessageAuthenticationAuditLevel); + // } + // set + // { + // this.Properties[MessageAuthenticationAuditLevelProperty] = value; + // } + //} + + public override string ToString() + { + return InternalToString(); + } + } + +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Security/Tokens/ServiceModelSecurityTokenRequirement.cs b/src/CoreWCF.Primitives/src/CoreWCF/Security/Tokens/ServiceModelSecurityTokenRequirement.cs new file mode 100644 index 000000000..42b7e3fa8 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Security/Tokens/ServiceModelSecurityTokenRequirement.cs @@ -0,0 +1,239 @@ +using CoreWCF.IdentityModel.Selectors; +using CoreWCF.Channels; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; + +namespace CoreWCF.Security.Tokens +{ + internal abstract class ServiceModelSecurityTokenRequirement : SecurityTokenRequirement + { + protected const string Namespace = "http://schemas.microsoft.com/ws/2006/05/servicemodel/securitytokenrequirement"; + const string securityAlgorithmSuiteProperty = Namespace + "/SecurityAlgorithmSuite"; + const string securityBindingElementProperty = Namespace + "/SecurityBindingElement"; + const string issuerAddressProperty = Namespace + "/IssuerAddress"; + const string issuerBindingProperty = Namespace + "/IssuerBinding"; + const string secureConversationSecurityBindingElementProperty = Namespace + "/SecureConversationSecurityBindingElement"; + const string supportSecurityContextCancellationProperty = Namespace + "/SupportSecurityContextCancellation"; + const string messageSecurityVersionProperty = Namespace + "/MessageSecurityVersion"; + const string defaultMessageSecurityVersionProperty = Namespace + "/DefaultMessageSecurityVersion"; + const string issuerBindingContextProperty = Namespace + "/IssuerBindingContext"; + const string transportSchemeProperty = Namespace + "/TransportScheme"; + const string isInitiatorProperty = Namespace + "/IsInitiator"; + const string targetAddressProperty = Namespace + "/TargetAddress"; + const string viaProperty = Namespace + "/Via"; + const string listenUriProperty = Namespace + "/ListenUri"; + const string auditLogLocationProperty = Namespace + "/AuditLogLocation"; + const string suppressAuditFailureProperty = Namespace + "/SuppressAuditFailure"; + const string messageAuthenticationAuditLevelProperty = Namespace + "/MessageAuthenticationAuditLevel"; + const string isOutOfBandTokenProperty = Namespace + "/IsOutOfBandToken"; + const string preferSslCertificateAuthenticatorProperty = Namespace + "/PreferSslCertificateAuthenticator"; + + // the following properties dont have top level OM properties but are part of the property bag + const string supportingTokenAttachmentModeProperty = Namespace + "/SupportingTokenAttachmentMode"; + const string messageDirectionProperty = Namespace + "/MessageDirection"; + const string httpAuthenticationSchemeProperty = Namespace + "/HttpAuthenticationScheme"; + const string issuedSecurityTokenParametersProperty = Namespace + "/IssuedSecurityTokenParameters"; + const string privacyNoticeUriProperty = Namespace + "/PrivacyNoticeUri"; + const string privacyNoticeVersionProperty = Namespace + "/PrivacyNoticeVersion"; + const string duplexClientLocalAddressProperty = Namespace + "/DuplexClientLocalAddress"; + const string endpointFilterTableProperty = Namespace + "/EndpointFilterTable"; + const string channelParametersCollectionProperty = Namespace + "/ChannelParametersCollection"; + const string extendedProtectionPolicy = Namespace + "/ExtendedProtectionPolicy"; + + const bool defaultSupportSecurityContextCancellation = false; + + protected ServiceModelSecurityTokenRequirement() + : base() + { + Properties[SupportSecurityContextCancellationProperty] = defaultSupportSecurityContextCancellation; + } + + static public string SecurityAlgorithmSuiteProperty { get { return securityAlgorithmSuiteProperty; } } + static public string SecurityBindingElementProperty { get { return securityBindingElementProperty; } } + static public string IssuerAddressProperty { get { return issuerAddressProperty; } } + static public string IssuerBindingProperty { get { return issuerBindingProperty; } } + static public string SecureConversationSecurityBindingElementProperty { get { return secureConversationSecurityBindingElementProperty; } } + static public string SupportSecurityContextCancellationProperty { get { return supportSecurityContextCancellationProperty; } } + static public string MessageSecurityVersionProperty { get { return messageSecurityVersionProperty; } } + static internal string DefaultMessageSecurityVersionProperty { get { return defaultMessageSecurityVersionProperty; } } + static public string IssuerBindingContextProperty { get { return issuerBindingContextProperty; } } + static public string TransportSchemeProperty { get { return transportSchemeProperty; } } + static public string IsInitiatorProperty { get { return isInitiatorProperty; } } + static public string TargetAddressProperty { get { return targetAddressProperty; } } + static public string ViaProperty { get { return viaProperty; } } + static public string ListenUriProperty { get { return listenUriProperty; } } + static public string AuditLogLocationProperty { get { return auditLogLocationProperty; } } + static public string SuppressAuditFailureProperty { get { return suppressAuditFailureProperty; } } + static public string MessageAuthenticationAuditLevelProperty { get { return messageAuthenticationAuditLevelProperty; } } + static public string IsOutOfBandTokenProperty { get { return isOutOfBandTokenProperty; } } + static public string PreferSslCertificateAuthenticatorProperty { get { return preferSslCertificateAuthenticatorProperty; } } + + static public string SupportingTokenAttachmentModeProperty { get { return supportingTokenAttachmentModeProperty; } } + static public string MessageDirectionProperty { get { return messageDirectionProperty; } } + static public string HttpAuthenticationSchemeProperty { get { return httpAuthenticationSchemeProperty; } } + static public string IssuedSecurityTokenParametersProperty { get { return issuedSecurityTokenParametersProperty; } } + static public string PrivacyNoticeUriProperty { get { return privacyNoticeUriProperty; } } + static public string PrivacyNoticeVersionProperty { get { return privacyNoticeVersionProperty; } } + static public string DuplexClientLocalAddressProperty { get { return duplexClientLocalAddressProperty; } } + static public string EndpointFilterTableProperty { get { return endpointFilterTableProperty; } } + static public string ChannelParametersCollectionProperty { get { return channelParametersCollectionProperty; } } + static public string ExtendedProtectionPolicy { get { return extendedProtectionPolicy; } } + + public bool IsInitiator + { + get + { + return GetPropertyOrDefault(IsInitiatorProperty, false); + } + } + + public SecurityAlgorithmSuite SecurityAlgorithmSuite + { + get + { + return GetPropertyOrDefault(SecurityAlgorithmSuiteProperty, null); + } + set + { + Properties[SecurityAlgorithmSuiteProperty] = value; + } + } + + //public SecurityBindingElement SecurityBindingElement + //{ + // get + // { + // return GetPropertyOrDefault(SecurityBindingElementProperty, null); + // } + // set + // { + // this.Properties[SecurityBindingElementProperty] = value; + // } + //} + + public EndpointAddress IssuerAddress + { + get + { + return GetPropertyOrDefault(IssuerAddressProperty, null); + } + set + { + Properties[IssuerAddressProperty] = value; + } + } + + public Binding IssuerBinding + { + get + { + return GetPropertyOrDefault(IssuerBindingProperty, null); + } + set + { + Properties[IssuerBindingProperty] = value; + } + } + + //public SecurityBindingElement SecureConversationSecurityBindingElement + //{ + // get + // { + // return GetPropertyOrDefault(SecureConversationSecurityBindingElementProperty, null); + // } + // set + // { + // this.Properties[SecureConversationSecurityBindingElementProperty] = value; + // } + //} + + //public SecurityTokenVersion MessageSecurityVersion + //{ + // get + // { + // return GetPropertyOrDefault(MessageSecurityVersionProperty, null); + // } + // set + // { + // this.Properties[MessageSecurityVersionProperty] = value; + // } + //} + + //internal MessageSecurityVersion DefaultMessageSecurityVersion + //{ + // get + // { + // MessageSecurityVersion messageSecurityVersion; + // return (this.TryGetProperty(DefaultMessageSecurityVersionProperty, out messageSecurityVersion)) ? messageSecurityVersion : null; + // } + // set + // { + // this.Properties[DefaultMessageSecurityVersionProperty] = (object)value; + // } + //} + + public string TransportScheme + { + get + { + return GetPropertyOrDefault(TransportSchemeProperty, null); + } + set + { + Properties[TransportSchemeProperty] = value; + } + } + + internal bool SupportSecurityContextCancellation + { + get + { + return GetPropertyOrDefault(SupportSecurityContextCancellationProperty, defaultSupportSecurityContextCancellation); + } + set + { + Properties[SupportSecurityContextCancellationProperty] = value; + } + } + + internal EndpointAddress DuplexClientLocalAddress + { + get + { + return GetPropertyOrDefault(duplexClientLocalAddressProperty, null); + } + set + { + Properties[duplexClientLocalAddressProperty] = value; + } + } + + internal TValue GetPropertyOrDefault(string propertyName, TValue defaultValue) + { + TValue result; + if (!TryGetProperty(propertyName, out result)) + { + result = defaultValue; + } + return result; + } + + internal string InternalToString() + { + StringBuilder sb = new StringBuilder(); + + sb.AppendLine(string.Format(CultureInfo.InvariantCulture, "{0}:", GetType().ToString())); + foreach (string propertyName in Properties.Keys) + { + object propertyValue = Properties[propertyName]; + sb.AppendLine(string.Format(CultureInfo.InvariantCulture, "PropertyName: {0}", propertyName)); + sb.AppendLine(string.Format(CultureInfo.InvariantCulture, "PropertyValue: {0}", propertyValue)); + sb.AppendLine(string.Format(CultureInfo.InvariantCulture, "---")); + } + return sb.ToString().Trim(); + } + } + +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Security/Tokens/ServiceModelSecurityTokenTypes.cs b/src/CoreWCF.Primitives/src/CoreWCF/Security/Tokens/ServiceModelSecurityTokenTypes.cs new file mode 100644 index 000000000..ea996a203 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Security/Tokens/ServiceModelSecurityTokenTypes.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoreWCF.Security.Tokens +{ + internal static class ServiceModelSecurityTokenTypes + { + const string Namespace = "http://schemas.microsoft.com/ws/2006/05/servicemodel/tokens"; + const string spnego = Namespace + "/Spnego"; + const string mutualSslnego = Namespace + "/MutualSslnego"; + const string anonymousSslnego = Namespace + "/AnonymousSslnego"; + const string securityContext = Namespace + "/SecurityContextToken"; + const string secureConversation = Namespace + "/SecureConversation"; + const string sspiCredential = Namespace + "/SspiCredential"; + + static public string Spnego { get { return spnego; } } + static public string MutualSslnego { get { return mutualSslnego; } } + static public string AnonymousSslnego { get { return anonymousSslnego; } } + static public string SecurityContext { get { return securityContext; } } + static public string SecureConversation { get { return secureConversation; } } + static public string SspiCredential { get { return sspiCredential; } } + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Security/Tokens/ServiceX509SecurityTokenProvider.cs b/src/CoreWCF.Primitives/src/CoreWCF/Security/Tokens/ServiceX509SecurityTokenProvider.cs new file mode 100644 index 000000000..31218fe63 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Security/Tokens/ServiceX509SecurityTokenProvider.cs @@ -0,0 +1,27 @@ +using CoreWCF.IdentityModel.Selectors; +using CoreWCF.IdentityModel.Tokens; +using System; +using System.Collections.Generic; +using System.Security.Cryptography.X509Certificates; +using System.Text; + +namespace CoreWCF.Security.Tokens +{ + internal class ServiceX509SecurityTokenProvider : X509SecurityTokenProvider + { + public ServiceX509SecurityTokenProvider(X509Certificate2 certificate) + : base(certificate) + { + } + + public ServiceX509SecurityTokenProvider(StoreLocation storeLocation, StoreName storeName, X509FindType findType, object findValue) + : base(storeLocation, storeName, findType, findValue) + { + } + + protected override SecurityToken GetTokenCore(TimeSpan timeout) + { + return new X509SecurityToken(Certificate, false, false); + } + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Security/Tokens/SspiSecurityToken.cs b/src/CoreWCF.Primitives/src/CoreWCF/Security/Tokens/SspiSecurityToken.cs new file mode 100644 index 000000000..73de96baa --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Security/Tokens/SspiSecurityToken.cs @@ -0,0 +1,62 @@ +using CoreWCF.IdentityModel; +using CoreWCF.IdentityModel.Tokens; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Net; +using System.Security.Principal; +using System.Text; + +namespace CoreWCF.Security.Tokens +{ + public class SspiSecurityToken : SecurityToken + { + string _id; + DateTime _effectiveTime; + DateTime _expirationTime; + + public SspiSecurityToken(TokenImpersonationLevel impersonationLevel, bool allowNtlm, NetworkCredential networkCredential) + { + ImpersonationLevel = impersonationLevel; + AllowNtlm = allowNtlm; + NetworkCredential = SecurityUtils.GetNetworkCredentialsCopy(networkCredential); + _effectiveTime = DateTime.UtcNow; + _expirationTime = _effectiveTime.AddHours(10); + } + + public SspiSecurityToken(NetworkCredential networkCredential, bool extractGroupsForWindowsAccounts, bool allowUnauthenticatedCallers) + { + NetworkCredential = SecurityUtils.GetNetworkCredentialsCopy(networkCredential); + ExtractGroupsForWindowsAccounts = extractGroupsForWindowsAccounts; + AllowUnauthenticatedCallers = allowUnauthenticatedCallers; + _effectiveTime = DateTime.UtcNow; + _expirationTime = _effectiveTime.AddHours(10); + } + + public override string Id + { + get + { + if (_id == null) + _id = SecurityUniqueId.Create().Value; + return _id; + } + } + + public override DateTime ValidFrom => _effectiveTime; + + public override DateTime ValidTo => _expirationTime; + + public bool AllowUnauthenticatedCallers { get; } = SspiSecurityTokenProvider.DefaultAllowUnauthenticatedCallers; + + public TokenImpersonationLevel ImpersonationLevel { get; } + + public bool AllowNtlm { get; } + + public NetworkCredential NetworkCredential { get; } + + public bool ExtractGroupsForWindowsAccounts { get; } + + public override ReadOnlyCollection SecurityKeys => EmptyReadOnlyCollection.Instance; + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Security/Tokens/WindowsSidIdentity.cs b/src/CoreWCF.Primitives/src/CoreWCF/Security/Tokens/WindowsSidIdentity.cs new file mode 100644 index 000000000..37271a2f5 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Security/Tokens/WindowsSidIdentity.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.Security.Principal; +using System.Text; + +namespace CoreWCF.Security.Tokens +{ + class WindowsSidIdentity : IIdentity + { + string _name; + + public WindowsSidIdentity(SecurityIdentifier sid) + { + SecurityIdentifier = sid ?? throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("sid"); + AuthenticationType = string.Empty; + } + + public WindowsSidIdentity(SecurityIdentifier sid, string name, string authenticationType) + { + SecurityIdentifier = sid ?? throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(sid)); + _name = name ?? throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(name)); + AuthenticationType = authenticationType ?? throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(authenticationType)); + } + + public SecurityIdentifier SecurityIdentifier { get; } + + public string AuthenticationType { get; } + + public bool IsAuthenticated + { + get { return true; } + } + + public string Name + { + get + { + if (_name == null) + _name = ((NTAccount)SecurityIdentifier.Translate(typeof(NTAccount))).Value; + return _name; + } + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(this, obj)) + return true; + + var sidIdentity = obj as WindowsSidIdentity; + if (sidIdentity == null) + return false; + + return SecurityIdentifier == sidIdentity.SecurityIdentifier; + } + + public override int GetHashCode() + { + return SecurityIdentifier.GetHashCode(); + } + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Security/UserNamePasswordServiceCredential.cs b/src/CoreWCF.Primitives/src/CoreWCF/Security/UserNamePasswordServiceCredential.cs new file mode 100644 index 000000000..0c6afd36f --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Security/UserNamePasswordServiceCredential.cs @@ -0,0 +1,175 @@ +using CoreWCF.IdentityModel.Selectors; +using CoreWCF.Runtime; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Runtime.CompilerServices; +using System.Text; + +namespace CoreWCF.Security +{ + public sealed class UserNamePasswordServiceCredential + { + internal const UserNamePasswordValidationMode DefaultUserNamePasswordValidationMode = UserNamePasswordValidationMode.Windows; + internal const bool DefaultCacheLogonTokens = false; + internal const int DefaultMaxCachedLogonTokens = 128; + internal const string DefaultCachedLogonTokenLifetimeString = "00:15:00"; + internal static readonly TimeSpan DefaultCachedLogonTokenLifetime = TimeSpan.Parse(DefaultCachedLogonTokenLifetimeString, CultureInfo.InvariantCulture); + + UserNamePasswordValidationMode validationMode = DefaultUserNamePasswordValidationMode; + UserNamePasswordValidator validator; + object membershipProvider; + bool includeWindowsGroups = SspiSecurityTokenProvider.DefaultExtractWindowsGroupClaims; + bool cacheLogonTokens = DefaultCacheLogonTokens; + int maxCachedLogonTokens = DefaultMaxCachedLogonTokens; + TimeSpan cachedLogonTokenLifetime = DefaultCachedLogonTokenLifetime; + bool isReadOnly; + + internal UserNamePasswordServiceCredential() + { + // empty + } + + internal UserNamePasswordServiceCredential(UserNamePasswordServiceCredential other) + { + includeWindowsGroups = other.includeWindowsGroups; + membershipProvider = other.membershipProvider; + validationMode = other.validationMode; + validator = other.validator; + cacheLogonTokens = other.cacheLogonTokens; + maxCachedLogonTokens = other.maxCachedLogonTokens; + cachedLogonTokenLifetime = other.cachedLogonTokenLifetime; + isReadOnly = other.isReadOnly; + } + + public UserNamePasswordValidationMode UserNamePasswordValidationMode + { + get + { + return validationMode; + } + set + { + UserNamePasswordValidationModeHelper.Validate(value); + ThrowIfImmutable(); + if(value == UserNamePasswordValidationMode.MembershipProvider) + { + throw new PlatformNotSupportedException("MembershipProvider not supported"); + } + + validationMode = value; + } + } + + public UserNamePasswordValidator CustomUserNamePasswordValidator + { + get + { + return validator; + } + set + { + ThrowIfImmutable(); + validator = value; + } + } + + public bool IncludeWindowsGroups + { + get + { + return includeWindowsGroups; + } + set + { + ThrowIfImmutable(); + includeWindowsGroups = value; + } + } + + public bool CacheLogonTokens + { + get + { + return cacheLogonTokens; + } + set + { + ThrowIfImmutable(); + cacheLogonTokens = value; + } + } + + public int MaxCachedLogonTokens + { + get + { + return maxCachedLogonTokens; + } + set + { + if (value <= 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", SR.ValueMustBeGreaterThanZero)); + } + ThrowIfImmutable(); + maxCachedLogonTokens = value; + } + } + + public TimeSpan CachedLogonTokenLifetime + { + get + { + return cachedLogonTokenLifetime; + } + set + { + if (value <= TimeSpan.Zero) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", SR.TimeSpanMustbeGreaterThanTimeSpanZero)); + } + + if (TimeoutHelper.IsTooLarge(value)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value, + SR.SFxTimeoutOutOfRangeTooBig)); + } + ThrowIfImmutable(); + cachedLogonTokenLifetime = value; + } + } + + internal UserNamePasswordValidator GetUserNamePasswordValidator() + { + if (validationMode == UserNamePasswordValidationMode.MembershipProvider) + { + throw new PlatformNotSupportedException("MembershipProvider not supported"); + } + else if (validationMode == UserNamePasswordValidationMode.Custom) + { + if (validator == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.MissingCustomUserNamePasswordValidator)); + } + return validator; + } + + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException()); + } + + internal void MakeReadOnly() + { + isReadOnly = true; + } + + void ThrowIfImmutable() + { + if (isReadOnly) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.ObjectIsReadOnly)); + } + } + + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Security/UserNamePasswordValidationMode.cs b/src/CoreWCF.Primitives/src/CoreWCF/Security/UserNamePasswordValidationMode.cs new file mode 100644 index 000000000..9d11c0123 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Security/UserNamePasswordValidationMode.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Text; + +namespace CoreWCF.Security +{ + public enum UserNamePasswordValidationMode + { + Windows, + MembershipProvider, + Custom + } + + static class UserNamePasswordValidationModeHelper + { + public static bool IsDefined(UserNamePasswordValidationMode validationMode) + { + return validationMode == UserNamePasswordValidationMode.Windows + || validationMode == UserNamePasswordValidationMode.MembershipProvider + || validationMode == UserNamePasswordValidationMode.Custom; + } + + public static void Validate(UserNamePasswordValidationMode value) + { + if (!IsDefined(value)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidEnumArgumentException("value", (int)value, + typeof(UserNamePasswordValidationMode))); + } + } + + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Security/WindowsServiceCredential.cs b/src/CoreWCF.Primitives/src/CoreWCF/Security/WindowsServiceCredential.cs new file mode 100644 index 000000000..0ee8ee903 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Security/WindowsServiceCredential.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoreWCF.Security +{ + public sealed class WindowsServiceCredential + { + bool allowAnonymousLogons = SspiSecurityTokenProvider.DefaultAllowUnauthenticatedCallers; + bool includeWindowsGroups = SspiSecurityTokenProvider.DefaultExtractWindowsGroupClaims; + bool isReadOnly; + + internal WindowsServiceCredential() + { + // empty + } + + internal WindowsServiceCredential(WindowsServiceCredential other) + { + allowAnonymousLogons = other.allowAnonymousLogons; + includeWindowsGroups = other.includeWindowsGroups; + isReadOnly = other.isReadOnly; + } + + public bool AllowAnonymousLogons + { + get + { + return allowAnonymousLogons; + } + set + { + ThrowIfImmutable(); + allowAnonymousLogons = value; + } + } + + public bool IncludeWindowsGroups + { + get + { + return includeWindowsGroups; + } + set + { + ThrowIfImmutable(); + includeWindowsGroups = value; + } + } + + internal void MakeReadOnly() + { + isReadOnly = true; + } + + void ThrowIfImmutable() + { + if (isReadOnly) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.ObjectIsReadOnly)); + } + } + } + +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Security/X509CertificateInitiatorServiceCredential.cs b/src/CoreWCF.Primitives/src/CoreWCF/Security/X509CertificateInitiatorServiceCredential.cs new file mode 100644 index 000000000..ebd19aa6b --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Security/X509CertificateInitiatorServiceCredential.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using System.Security.Cryptography.X509Certificates; +using System.Text; + +namespace CoreWCF.Security +{ + public sealed class X509CertificateInitiatorServiceCredential + { + internal const StoreLocation DefaultStoreLocation = StoreLocation.LocalMachine; + internal const StoreName DefaultStoreName = StoreName.My; + internal const X509FindType DefaultFindType = X509FindType.FindBySubjectDistinguishedName; + + X509Certificate2 certificate; + X509ClientCertificateAuthentication authentication; + bool isReadOnly; + + internal X509CertificateInitiatorServiceCredential() + { + authentication = new X509ClientCertificateAuthentication(); + } + + internal X509CertificateInitiatorServiceCredential(X509CertificateInitiatorServiceCredential other) + { + certificate = other.certificate; + authentication = new X509ClientCertificateAuthentication(other.authentication); + isReadOnly = other.isReadOnly; + } + + public X509Certificate2 Certificate + { + get + { + return certificate; + } + set + { + ThrowIfImmutable(); + certificate = value; + } + } + + public X509ClientCertificateAuthentication Authentication + { + get + { + return authentication; + } + } + + public void SetCertificate(string subjectName, StoreLocation storeLocation, StoreName storeName) + { + if (subjectName == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("subjectName"); + } + SetCertificate(storeLocation, storeName, DefaultFindType, subjectName); + } + + public void SetCertificate(StoreLocation storeLocation, StoreName storeName, X509FindType findType, object findValue) + { + if (findValue == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("findValue"); + } + ThrowIfImmutable(); + certificate = SecurityUtils.GetCertificateFromStore(storeName, storeLocation, findType, findValue, null); + } + + internal void MakeReadOnly() + { + isReadOnly = true; + Authentication.MakeReadOnly(); + } + + void ThrowIfImmutable() + { + if (isReadOnly) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.ObjectIsReadOnly)); + } + } + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Security/X509CertificateRecipientServiceCredential.cs b/src/CoreWCF.Primitives/src/CoreWCF/Security/X509CertificateRecipientServiceCredential.cs new file mode 100644 index 000000000..911e6a75f --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Security/X509CertificateRecipientServiceCredential.cs @@ -0,0 +1,75 @@ +using System; +using System.Security.Cryptography.X509Certificates; + +namespace CoreWCF.Security +{ + public sealed class X509CertificateRecipientServiceCredential + { + X509Certificate2 certificate; + internal const StoreLocation DefaultStoreLocation = StoreLocation.LocalMachine; + internal const StoreName DefaultStoreName = StoreName.My; + internal const X509FindType DefaultFindType = X509FindType.FindBySubjectDistinguishedName; + bool isReadOnly; + + internal X509CertificateRecipientServiceCredential() + { + } + + internal X509CertificateRecipientServiceCredential(X509CertificateRecipientServiceCredential other) + { + certificate = other.certificate; + isReadOnly = other.isReadOnly; + } + + public X509Certificate2 Certificate + { + get + { + return certificate; + } + set + { + ThrowIfImmutable(); + certificate = value; + } + } + + public void SetCertificate(string subjectName) + { + SetCertificate(subjectName, DefaultStoreLocation, DefaultStoreName); + } + + public void SetCertificate(string subjectName, StoreLocation storeLocation, StoreName storeName) + { + if (subjectName == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("subjectName"); + } + SetCertificate(storeLocation, storeName, DefaultFindType, subjectName); + } + + public void SetCertificate(StoreLocation storeLocation, StoreName storeName, X509FindType findType, object findValue) + { + if (findValue == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("findValue"); + } + ThrowIfImmutable(); + certificate = SecurityUtils.GetCertificateFromStore(storeName, storeLocation, findType, findValue, null); + } + + internal void MakeReadOnly() + { + isReadOnly = true; + } + + void ThrowIfImmutable() + { + if (isReadOnly) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.ObjectIsReadOnly)); + } + } + } + +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Security/X509CertificateValidationMode.cs b/src/CoreWCF.Primitives/src/CoreWCF/Security/X509CertificateValidationMode.cs new file mode 100644 index 000000000..1ee4e5e27 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Security/X509CertificateValidationMode.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Text; + +namespace CoreWCF.Security +{ + /// + /// An enumeration that lists the ways of validating a certificate. + /// + public enum X509CertificateValidationMode + { + /// + /// No validation of the certificate is performed. + /// + None, + + /// + /// The certificate is valid if it is in the trusted people store. + /// + PeerTrust, + + /// + /// The certificate is valid if the chain builds to a certification authority in the trusted root store. + /// + ChainTrust, + + /// + /// The certificate is valid if it is in the trusted people store, or if the chain builds to a certification authority in the trusted root store. + /// + PeerOrChainTrust, + + /// + /// The user must plug in a custom X509CertificateValidator to validate the certificate. + /// + Custom + } + + internal static class X509CertificateValidationModeHelper + { + public static bool IsDefined(X509CertificateValidationMode validationMode) + { + return validationMode == X509CertificateValidationMode.None + || validationMode == X509CertificateValidationMode.PeerTrust + || validationMode == X509CertificateValidationMode.ChainTrust + || validationMode == X509CertificateValidationMode.PeerOrChainTrust + || validationMode == X509CertificateValidationMode.Custom; + } + + internal static void Validate(X509CertificateValidationMode value) + { + if (!IsDefined(value)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidEnumArgumentException("value", (int)value, + typeof(X509CertificateValidationMode))); + } + } + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Security/X509ClientCertificateAuthentication.cs b/src/CoreWCF.Primitives/src/CoreWCF/Security/X509ClientCertificateAuthentication.cs new file mode 100644 index 000000000..58a330f31 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Security/X509ClientCertificateAuthentication.cs @@ -0,0 +1,182 @@ +using CoreWCF.IdentityModel.Selectors; +using System; +using System.Collections.Generic; +using System.Security.Cryptography.X509Certificates; +using System.Text; + +namespace CoreWCF.Security +{ + public class X509ClientCertificateAuthentication + { + internal const X509CertificateValidationMode DefaultCertificateValidationMode = X509CertificateValidationMode.ChainTrust; + internal const X509RevocationMode DefaultRevocationMode = X509RevocationMode.Online; + internal const StoreLocation DefaultTrustedStoreLocation = StoreLocation.LocalMachine; + internal const bool DefaultMapCertificateToWindowsAccount = false; + static X509CertificateValidator defaultCertificateValidator; + + X509CertificateValidationMode certificateValidationMode = DefaultCertificateValidationMode; + X509RevocationMode revocationMode = DefaultRevocationMode; + StoreLocation trustedStoreLocation = DefaultTrustedStoreLocation; + X509CertificateValidator customCertificateValidator = null; + bool mapClientCertificateToWindowsAccount = DefaultMapCertificateToWindowsAccount; + bool includeWindowsGroups = SspiSecurityTokenProvider.DefaultExtractWindowsGroupClaims; + bool isReadOnly; + + internal X509ClientCertificateAuthentication() + { + } + + internal X509ClientCertificateAuthentication(X509ClientCertificateAuthentication other) + { + certificateValidationMode = other.certificateValidationMode; + customCertificateValidator = other.customCertificateValidator; + includeWindowsGroups = other.includeWindowsGroups; + mapClientCertificateToWindowsAccount = other.mapClientCertificateToWindowsAccount; + trustedStoreLocation = other.trustedStoreLocation; + revocationMode = other.revocationMode; + isReadOnly = other.isReadOnly; + } + + internal static X509CertificateValidator DefaultCertificateValidator + { + get + { + if (defaultCertificateValidator == null) + { + bool useMachineContext = DefaultTrustedStoreLocation == StoreLocation.LocalMachine; + X509ChainPolicy chainPolicy = new X509ChainPolicy(); + chainPolicy.RevocationMode = DefaultRevocationMode; + defaultCertificateValidator = X509CertificateValidator.CreateChainTrustValidator(useMachineContext, chainPolicy); + } + return defaultCertificateValidator; + } + } + + public X509CertificateValidationMode CertificateValidationMode + { + get + { + return certificateValidationMode; + } + set + { + X509CertificateValidationModeHelper.Validate(value); + ThrowIfImmutable(); + certificateValidationMode = value; + } + } + + public X509RevocationMode RevocationMode + { + get + { + return revocationMode; + } + set + { + ThrowIfImmutable(); + revocationMode = value; + } + } + + public StoreLocation TrustedStoreLocation + { + get + { + return trustedStoreLocation; + } + set + { + ThrowIfImmutable(); + trustedStoreLocation = value; + } + } + + public X509CertificateValidator CustomCertificateValidator + { + get + { + return customCertificateValidator; + } + set + { + ThrowIfImmutable(); + customCertificateValidator = value; + } + } + + public bool MapClientCertificateToWindowsAccount + { + get + { + return mapClientCertificateToWindowsAccount; + } + set + { + ThrowIfImmutable(); + mapClientCertificateToWindowsAccount = value; + } + } + + public bool IncludeWindowsGroups + { + get + { + return includeWindowsGroups; + } + set + { + ThrowIfImmutable(); + includeWindowsGroups = value; + } + } + + internal X509CertificateValidator GetCertificateValidator() + { + if (certificateValidationMode == X509CertificateValidationMode.None) + { + return X509CertificateValidator.None; + } + else if (certificateValidationMode == X509CertificateValidationMode.PeerTrust) + { + return X509CertificateValidator.PeerTrust; + } + else if (certificateValidationMode == X509CertificateValidationMode.Custom) + { + if (customCertificateValidator == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.MissingCustomCertificateValidator)); + } + return customCertificateValidator; + } + else + { + bool useMachineContext = trustedStoreLocation == StoreLocation.LocalMachine; + X509ChainPolicy chainPolicy = new X509ChainPolicy(); + chainPolicy.RevocationMode = revocationMode; + if (certificateValidationMode == X509CertificateValidationMode.ChainTrust) + { + return X509CertificateValidator.CreateChainTrustValidator(useMachineContext, chainPolicy); + } + else + { + return X509CertificateValidator.CreatePeerOrChainTrustValidator(useMachineContext, chainPolicy); + } + } + } + + internal void MakeReadOnly() + { + isReadOnly = true; + } + + void ThrowIfImmutable() + { + if (isReadOnly) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.ObjectIsReadOnly)); + } + } + + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/SecurityMode.cs b/src/CoreWCF.Primitives/src/CoreWCF/SecurityMode.cs new file mode 100644 index 000000000..d93c600a5 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/SecurityMode.cs @@ -0,0 +1,10 @@ +namespace CoreWCF +{ + public enum SecurityMode + { + None = 0, + Transport = 1, + Message = 2, + TransportWithMessageCredential = 3, + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/ServerTooBusyException.cs b/src/CoreWCF.Primitives/src/CoreWCF/ServerTooBusyException.cs new file mode 100644 index 000000000..76b813468 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/ServerTooBusyException.cs @@ -0,0 +1,14 @@ +using System; +using System.Runtime.Serialization; + +namespace CoreWCF +{ + [Serializable] + public class ServerTooBusyException : CommunicationException + { + public ServerTooBusyException() { } + public ServerTooBusyException(string message) : base(message) { } + public ServerTooBusyException(string message, Exception innerException) : base(message, innerException) { } + protected ServerTooBusyException(SerializationInfo info, StreamingContext context) : base(info, context) { } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/ServiceActivationException.cs b/src/CoreWCF.Primitives/src/CoreWCF/ServiceActivationException.cs new file mode 100644 index 000000000..f7d6d38a6 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/ServiceActivationException.cs @@ -0,0 +1,14 @@ +using System; +using System.Runtime.Serialization; + +namespace CoreWCF +{ + [Serializable] + public class ServiceActivationException : CommunicationException + { + public ServiceActivationException() { } + public ServiceActivationException(string message) : base(message) { } + public ServiceActivationException(string message, Exception innerException) : base(message, innerException) { } + protected ServiceActivationException(SerializationInfo info, StreamingContext context) : base(info, context) { } + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/ServiceBehaviorAttribute.cs b/src/CoreWCF.Primitives/src/CoreWCF/ServiceBehaviorAttribute.cs new file mode 100644 index 000000000..d35fc8dc4 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/ServiceBehaviorAttribute.cs @@ -0,0 +1,254 @@ +using System; +using System.Collections.ObjectModel; +using System.ComponentModel; +using CoreWCF.Channels; +using CoreWCF.Description; +using CoreWCF.Dispatcher; + +namespace CoreWCF +{ + public sealed class ServiceBehaviorAttribute : Attribute, IServiceBehavior + { + private ConcurrencyMode _concurrencyMode; + bool _ensureOrderedDispatch = false; + private string _configurationName; + bool _includeExceptionDetailInFaults = false; + private InstanceContextMode _instanceMode; + private object _wellKnownSingleton; // if the user passes an object to the ServiceHost, it is stored here + private object _hiddenSingleton; // if the user passes a type to the ServiceHost, and instanceMode==Single, we store the instance here + bool _validateMustUnderstand = true; + bool _ignoreExtensionDataObject = DataContractSerializerDefaults.IgnoreExtensionDataObject; + int _maxItemsInObjectGraph = DataContractSerializerDefaults.MaxItemsInObjectGraph; + bool _automaticSessionShutdown = true; + IInstanceProvider _instanceProvider = null; + bool _useSynchronizationContext = true; + AddressFilterMode _addressFilterMode = AddressFilterMode.Exact; + + [DefaultValue(null)] + public string Name { get; set; } + + [DefaultValue(null)] + public string Namespace { get; set; } + + [DefaultValue(AddressFilterMode.Exact)] + public AddressFilterMode AddressFilterMode + { + get { return _addressFilterMode; } + set + { + if (!AddressFilterModeHelper.IsDefined(value)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(value))); + } + + _addressFilterMode = value; + } + } + + [DefaultValue(null)] + public string ConfigurationName + { + get { return _configurationName; } + set + { + if (value == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(value)); + } + + if (value == string.Empty) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(value), + SR.SFxConfigurationNameCannotBeEmpty)); + } + + _configurationName = value; + } + } + + [DefaultValue(ConcurrencyMode.Single)] + public ConcurrencyMode ConcurrencyMode + { + get { return _concurrencyMode; } + set + { + if (!ConcurrencyModeHelper.IsDefined(value)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(value))); + } + + _concurrencyMode = value; + } + } + + [DefaultValue(InstanceContextMode.PerSession)] + public InstanceContextMode InstanceContextMode + { + get { return _instanceMode; } + set + { + if (!InstanceContextModeHelper.IsDefined(value)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(value))); + } + + _instanceMode = value; + } + } + + public object GetWellKnownSingleton() + { + return _wellKnownSingleton; + } + + internal void SetWellKnownSingleton(object value) + { + if (value == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(value)); + + _wellKnownSingleton = value; + } + + internal object GetHiddenSingleton() + { + return _hiddenSingleton; + } + + internal void SetHiddenSingleton(object value) + { + if (value == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(value)); + + _hiddenSingleton = value; + } + + void IServiceBehavior.Validate(ServiceDescription description, ServiceHostBase serviceHostBase) + { + if (_concurrencyMode != ConcurrencyMode.Single && _ensureOrderedDispatch) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxNonConcurrentOrEnsureOrderedDispatch, description.Name))); + } + } + + void IServiceBehavior.AddBindingParameters(ServiceDescription serviceDescription, + ServiceHostBase serviceHostBase, + Collection endpoints, + BindingParameterCollection bindingParameters) + { + } + + void IServiceBehavior.ApplyDispatchBehavior(ServiceDescription description, ServiceHostBase serviceHostBase) + { + for (int i = 0; i < serviceHostBase.ChannelDispatchers.Count; i++) + { + ChannelDispatcher channelDispatcher = serviceHostBase.ChannelDispatchers[i] as ChannelDispatcher; + if (channelDispatcher != null) + { + channelDispatcher.IncludeExceptionDetailInFaults = _includeExceptionDetailInFaults; + + if (channelDispatcher.HasApplicationEndpoints) + { + foreach (EndpointDispatcher endpointDispatcher in channelDispatcher.Endpoints) + { + if (endpointDispatcher.IsSystemEndpoint) + { + continue; + } + DispatchRuntime behavior = endpointDispatcher.DispatchRuntime; + behavior.ConcurrencyMode = _concurrencyMode; + behavior.EnsureOrderedDispatch = _ensureOrderedDispatch; + behavior.ValidateMustUnderstand = _validateMustUnderstand; + behavior.AutomaticInputSessionShutdown = _automaticSessionShutdown; + if (!_useSynchronizationContext) + { + behavior.SynchronizationContext = null; + } + + if (!endpointDispatcher.AddressFilterSetExplicit) + { + EndpointAddress address = endpointDispatcher.OriginalAddress; + if (address == null || AddressFilterMode == AddressFilterMode.Any) + { + endpointDispatcher.AddressFilter = new MatchAllMessageFilter(); + } + else if (AddressFilterMode == AddressFilterMode.Prefix) + { + endpointDispatcher.AddressFilter = new PrefixEndpointAddressMessageFilter(address); + } + else if (AddressFilterMode == AddressFilterMode.Exact) + { + endpointDispatcher.AddressFilter = new EndpointAddressMessageFilter(address); + } + } + } + } + } + } + DataContractSerializerServiceBehavior.ApplySerializationSettings(description, _ignoreExtensionDataObject, _maxItemsInObjectGraph); + ApplyInstancing(description, serviceHostBase); + } + + void ApplyInstancing(ServiceDescription description, ServiceHostBase serviceHostBase) + { + Type serviceType = description.ServiceType; + InstanceContext singleton = null; + + for (int i = 0; i < serviceHostBase.ChannelDispatchers.Count; i++) + { + ChannelDispatcher channelDispatcher = serviceHostBase.ChannelDispatchers[i] as ChannelDispatcher; + if (channelDispatcher != null) + { + foreach (EndpointDispatcher endpointDispatcher in channelDispatcher.Endpoints) + { + if (endpointDispatcher.IsSystemEndpoint) + { + continue; + } + DispatchRuntime dispatch = endpointDispatcher.DispatchRuntime; + if (dispatch.InstanceProvider == null) + { + if (_instanceProvider == null) + { + if (serviceType == null && _wellKnownSingleton == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.InstanceSettingsMustHaveTypeOrWellKnownObject0)); + + if (_instanceMode != InstanceContextMode.Single && _wellKnownSingleton != null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.SFxWellKnownNonSingleton0)); + } + else + { + dispatch.InstanceProvider = _instanceProvider; + } + } + dispatch.Type = serviceType; + dispatch.InstanceContextProvider = InstanceContextProviderBase.GetProviderForMode(_instanceMode, dispatch); + + if ((_instanceMode == InstanceContextMode.Single) && + (dispatch.SingletonInstanceContext == null)) + { + if (singleton == null) + { + if (_wellKnownSingleton != null) + { + singleton = new InstanceContext(serviceHostBase, _wellKnownSingleton, true, false); + } + else if (_hiddenSingleton != null) + { + singleton = new InstanceContext(serviceHostBase, _hiddenSingleton, false, false); + } + else + { + singleton = new InstanceContext(serviceHostBase, false); + } + + singleton.AutoClose = false; + } + dispatch.SingletonInstanceContext = singleton; + } + } + } + } + } + + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/ServiceChannelManager.cs b/src/CoreWCF.Primitives/src/CoreWCF/ServiceChannelManager.cs new file mode 100644 index 000000000..c91ebf1aa --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/ServiceChannelManager.cs @@ -0,0 +1,454 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using CoreWCF.Runtime; +using CoreWCF.Channels; + +namespace CoreWCF +{ + delegate void InstanceContextEmptyCallback(InstanceContext instanceContext); + + internal class ServiceChannelManager : LifetimeManager + { + int activityCount; + ICommunicationWaiter activityWaiter; + int activityWaiterCount; + Action emptyCallback; + IChannel firstIncomingChannel; + ChannelCollection incomingChannels; + ChannelCollection outgoingChannels; + InstanceContext instanceContext; + + public ServiceChannelManager(InstanceContext instanceContext) + : this(instanceContext, null) + { + } + + public ServiceChannelManager(InstanceContext instanceContext, Action emptyCallback) + : base(instanceContext.ThisLock) + { + this.instanceContext = instanceContext; + this.emptyCallback = emptyCallback; + } + + public int ActivityCount + { + get { return activityCount; } + } + + public ICollection IncomingChannels + { + get + { + EnsureIncomingChannelCollection(); + return (ICollection)incomingChannels; + } + } + + public ICollection OutgoingChannels + { + get + { + if (outgoingChannels == null) + { + lock (ThisLock) + { + if (outgoingChannels == null) + outgoingChannels = new ChannelCollection(this, ThisLock); + } + } + return outgoingChannels; + } + } + + public bool IsBusy + { + get + { + if (ActivityCount > 0) + return true; + + if (base.BusyCount > 0) + return true; + + ICollection outgoing = outgoingChannels; + if ((outgoing != null) && (outgoing.Count > 0)) + return true; + + return false; + } + } + + public void AddIncomingChannel(IChannel channel) + { + bool added = false; + + lock (ThisLock) + { + if (State == LifetimeState.Opened) + { + if (firstIncomingChannel == null) + { + if (incomingChannels == null) + { + firstIncomingChannel = channel; + ChannelAdded(channel); + } + else + { + if (incomingChannels.Contains(channel)) + return; + incomingChannels.Add(channel); + } + } + else + { + EnsureIncomingChannelCollection(); + if (incomingChannels.Contains(channel)) + return; + incomingChannels.Add(channel); + } + added = true; + } + } + + if (!added) + { + channel.Abort(); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ObjectDisposedException(GetType().ToString())); + } + } + + void ChannelAdded(IChannel channel) + { + base.IncrementBusyCount(); + channel.Closed += OnChannelClosed; + } + + void ChannelRemoved(IChannel channel) + { + channel.Closed -= OnChannelClosed; + base.DecrementBusyCount(); + } + + public async Task CloseInputAsync(CancellationToken token) + { + AsyncCommunicationWaiter activityWaiter = null; + + lock (ThisLock) + { + if (activityCount > 0) + { + activityWaiter = new AsyncCommunicationWaiter(ThisLock); + if (!(this.activityWaiter == null)) + { + Fx.Assert("ServiceChannelManager.CloseInput: (this.activityWaiter == null)"); + } + this.activityWaiter = activityWaiter; + Interlocked.Increment(ref activityWaiterCount); + } + } + + if (activityWaiter != null) + { + CommunicationWaitResult result = await activityWaiter.WaitAsync(false, token); + if (Interlocked.Decrement(ref activityWaiterCount) == 0) + { + activityWaiter.Dispose(); + this.activityWaiter = null; + } + + switch (result) + { + case CommunicationWaitResult.Expired: + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new TimeoutException(SR.SfxCloseTimedOutWaitingForDispatchToComplete)); + case CommunicationWaitResult.Aborted: + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ObjectDisposedException(GetType().ToString())); + } + } + } + + public void DecrementActivityCount() + { + ICommunicationWaiter activityWaiter = null; + bool empty = false; + + lock (ThisLock) + { + if (!(activityCount > 0)) + { + Fx.Assert("ServiceChannelManager.DecrementActivityCount: (this.activityCount > 0)"); + } + if (--activityCount == 0) + { + if (this.activityWaiter != null) + { + activityWaiter = this.activityWaiter; + Interlocked.Increment(ref activityWaiterCount); + } + if (BusyCount == 0) + empty = true; + } + } + + if (activityWaiter != null) + { + activityWaiter.Signal(); + if (Interlocked.Decrement(ref activityWaiterCount) == 0) + { + activityWaiter.Dispose(); + this.activityWaiter = null; + } + } + + if (empty && State == LifetimeState.Opened) + OnEmpty(); + } + + void EnsureIncomingChannelCollection() + { + lock (ThisLock) + { + if (incomingChannels == null) + { + incomingChannels = new ChannelCollection(this, ThisLock); + if (firstIncomingChannel != null) + { + incomingChannels.Add(firstIncomingChannel); + ChannelRemoved(firstIncomingChannel); // Adding to collection called ChannelAdded, so call ChannelRemoved to balance + firstIncomingChannel = null; + } + } + } + } + + public void IncrementActivityCount() + { + lock (ThisLock) + { + if (State == LifetimeState.Closed) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ObjectDisposedException(GetType().ToString())); + activityCount++; + } + } + + protected override void IncrementBusyCount() + { + base.IncrementBusyCount(); + } + + protected override void OnAbort() + { + IChannel[] channels = SnapshotChannels(); + for (int index = 0; index < channels.Length; index++) + channels[index].Abort(); + + ICommunicationWaiter activityWaiter = null; + + lock (ThisLock) + { + if (this.activityWaiter != null) + { + activityWaiter = this.activityWaiter; + Interlocked.Increment(ref activityWaiterCount); + } + } + + if (activityWaiter != null) + { + activityWaiter.Signal(); + if (Interlocked.Decrement(ref activityWaiterCount) == 0) + { + activityWaiter.Dispose(); + this.activityWaiter = null; + } + } + + base.OnAbort(); + } + + protected override async Task OnCloseAsync(CancellationToken token) + { + await CloseInputAsync(token); + await base.OnCloseAsync(token); + } + + protected override void OnEmpty() + { + if (emptyCallback != null) + emptyCallback(instanceContext); + } + + void OnChannelClosed(object sender, EventArgs args) + { + RemoveChannel((IChannel)sender); + } + + public bool RemoveChannel(IChannel channel) + { + lock (ThisLock) + { + if (firstIncomingChannel == channel) + { + firstIncomingChannel = null; + ChannelRemoved(channel); + return true; + } + else if (incomingChannels != null && incomingChannels.Contains(channel)) + { + incomingChannels.Remove(channel); + return true; + } + else if (outgoingChannels != null && outgoingChannels.Contains(channel)) + { + outgoingChannels.Remove(channel); + return true; + } + } + + return false; + } + + public IChannel[] SnapshotChannels() + { + lock (ThisLock) + { + int outgoingCount = (outgoingChannels != null ? outgoingChannels.Count : 0); + + if (firstIncomingChannel != null) + { + IChannel[] channels = new IChannel[1 + outgoingCount]; + channels[0] = firstIncomingChannel; + if (outgoingCount > 0) + outgoingChannels.CopyTo(channels, 1); + return channels; + } + + if (incomingChannels != null) + { + IChannel[] channels = new IChannel[incomingChannels.Count + outgoingCount]; + incomingChannels.CopyTo(channels, 0); + if (outgoingCount > 0) + outgoingChannels.CopyTo(channels, incomingChannels.Count); + return channels; + } + + if (outgoingCount > 0) + { + IChannel[] channels = new IChannel[outgoingCount]; + outgoingChannels.CopyTo(channels, 0); + return channels; + } + } + return EmptyArray.Allocate(0); + } + + class ChannelCollection : ICollection + { + ServiceChannelManager channelManager; + object syncRoot; + HashSet hashSet = new HashSet(); + + public bool IsReadOnly + { + get { return false; } + } + + public int Count + { + get + { + lock (syncRoot) + { + return hashSet.Count; + } + } + } + + public ChannelCollection(ServiceChannelManager channelManager, object syncRoot) + { + if (syncRoot == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(syncRoot)); + + this.channelManager = channelManager; + this.syncRoot = syncRoot; + } + + public void Add(IChannel channel) + { + lock (syncRoot) + { + if (hashSet.Add(channel)) + { + channelManager.ChannelAdded(channel); + } + } + } + + public void Clear() + { + lock (syncRoot) + { + foreach (IChannel channel in hashSet) + channelManager.ChannelRemoved(channel); + hashSet.Clear(); + } + } + + public bool Contains(IChannel channel) + { + lock (syncRoot) + { + if (channel != null) + { + return hashSet.Contains(channel); + } + return false; + } + } + + public void CopyTo(IChannel[] array, int arrayIndex) + { + lock (syncRoot) + { + hashSet.CopyTo(array, arrayIndex); + } + } + + public bool Remove(IChannel channel) + { + lock (syncRoot) + { + bool ret = false; + if (channel != null) + { + ret = hashSet.Remove(channel); + if (ret) + { + channelManager.ChannelRemoved(channel); + } + } + return ret; + } + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + lock (syncRoot) + { + return hashSet.GetEnumerator(); + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + lock (syncRoot) + { + return hashSet.GetEnumerator(); + } + } + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/ServiceContractAttribute.cs b/src/CoreWCF.Primitives/src/CoreWCF/ServiceContractAttribute.cs new file mode 100644 index 000000000..d407f7709 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/ServiceContractAttribute.cs @@ -0,0 +1,83 @@ +using System; +using CoreWCF.Description; + +namespace CoreWCF +{ + [AttributeUsage(ServiceModelAttributeTargets.ServiceContract, Inherited = false, AllowMultiple = false)] + public sealed class ServiceContractAttribute : Attribute + { + Type _callbackContract; + string _configurationName; + string _name; + string _ns; + SessionMode _sessionMode; + + public string ConfigurationName + { + get { return _configurationName; } + set + { + if (value == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(value)); + } + if (value == string.Empty) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(value), + SR.SFxConfigurationNameCannotBeEmpty)); + } + _configurationName = value; + } + } + + public string Name + { + get { return _name; } + set + { + if (value == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(value)); + } + if (value == string.Empty) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(value), + SR.SFxNameCannotBeEmpty)); + } + _name = value; + } + } + + public string Namespace + { + get { return _ns; } + set + { + if (!string.IsNullOrEmpty(value)) + NamingHelper.CheckUriProperty(value, "Namespace"); + _ns = value; + } + } + + public SessionMode SessionMode + { + get { return _sessionMode; } + set + { + if (!SessionModeHelper.IsDefined(value)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(value))); + } + + _sessionMode = value; + } + } + + public Type CallbackContract + { + get { return _callbackContract; } + set { _callbackContract = value; } + } + + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/ServiceDefaults.cs b/src/CoreWCF.Primitives/src/CoreWCF/ServiceDefaults.cs new file mode 100644 index 000000000..0ba98e3e0 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/ServiceDefaults.cs @@ -0,0 +1,25 @@ +using System; + +namespace CoreWCF +{ + internal static class ServiceDefaults + { + //internal static TimeSpan ServiceHostCloseTimeout { get { return TimeSpanHelper.FromSeconds(10, ServiceHostCloseTimeoutString); } } + internal static TimeSpan ServiceHostCloseTimeout => TimeSpan.FromSeconds(10); + internal const string ServiceHostCloseTimeoutString = "00:00:10"; + //internal static TimeSpan CloseTimeout { get { return TimeSpanHelper.FromMinutes(1, CloseTimeoutString); } } + internal static TimeSpan CloseTimeout => TimeSpan.FromMinutes(1); + internal const string CloseTimeoutString = "00:01:00"; + //internal static TimeSpan OpenTimeout { get { return TimeSpanHelper.FromMinutes(1, OpenTimeoutString); } } + internal static TimeSpan OpenTimeout => TimeSpan.FromMinutes(1); + internal const string OpenTimeoutString = "00:01:00"; + //internal static TimeSpan ReceiveTimeout { get { return TimeSpanHelper.FromMinutes(10, ReceiveTimeoutString); } } + internal static TimeSpan ReceiveTimeout => TimeSpan.FromMinutes(10); + internal const string ReceiveTimeoutString = "00:10:00"; + //internal static TimeSpan SendTimeout { get { return TimeSpanHelper.FromMinutes(1, SendTimeoutString); } } + internal static TimeSpan SendTimeout => TimeSpan.FromMinutes(1); + internal const string SendTimeoutString = "00:01:00"; + //internal static TimeSpan TransactionTimeout { get { return TimeSpanHelper.FromMinutes(1, TransactionTimeoutString); } } + //internal const string TransactionTimeoutString = "00:00:00"; + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/ServiceHostBase.cs b/src/CoreWCF.Primitives/src/CoreWCF/ServiceHostBase.cs new file mode 100644 index 000000000..9516341c6 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/ServiceHostBase.cs @@ -0,0 +1,370 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Globalization; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using CoreWCF.Collections.Generic; +using CoreWCF.Runtime; +using CoreWCF.Channels; +using CoreWCF.Description; +using CoreWCF.Diagnostics; +using CoreWCF.Dispatcher; +using System.Diagnostics; + +namespace CoreWCF +{ + public abstract class ServiceHostBase : CommunicationObject, IExtensibleObject, IDisposable + { + internal static readonly Uri EmptyUri = new Uri(string.Empty, UriKind.RelativeOrAbsolute); + + bool initializeDescriptionHasFinished; + UriSchemeKeyedCollection baseAddresses; + ChannelDispatcherCollection channelDispatchers; + TimeSpan closeTimeout = ServiceDefaults.ServiceHostCloseTimeout; + ServiceDescription description; + ExtensionCollection extensions; + ReadOnlyCollection externalBaseAddresses; + IDictionary implementedContracts; + IInstanceContextManager instances; + TimeSpan openTimeout = ServiceDefaults.OpenTimeout; + ServiceCredentials readOnlyCredentials; + //ServiceAuthorizationBehavior readOnlyAuthorization; + //ServiceAuthenticationBehavior readOnlyAuthentication; + Dictionary> endpointsByListenUriInfo; + int busyCount; + //EventTraceActivity eventTraceActivity; + + public event EventHandler UnknownMessageReceived; + + protected ServiceHostBase() + { + this.baseAddresses = new UriSchemeKeyedCollection(this.ThisLock); + this.channelDispatchers = new ChannelDispatcherCollection(this, this.ThisLock); + this.extensions = new ExtensionCollection(this, this.ThisLock); + this.instances = new InstanceContextManager(this.ThisLock); + } + + // TODO: Bring in ServiceAuthorizationBehavior + //public ServiceAuthorizationBehavior Authorization + //{ + // get + // { + // if (this.Description == null) + // { + // return null; + // } + // else if (this.State == CommunicationState.Created || this.State == CommunicationState.Opening) + // { + // return EnsureAuthorization(this.Description); + // } + // else + // { + // return this.readOnlyAuthorization; + // } + // } + //} + + // TODO: Bring in ServiceAuthenticationBehavior + //public ServiceAuthenticationBehavior Authentication + //{ + // get + // { + // if (this.Description == null) + // { + // return null; + // } + // else if (this.State == CommunicationState.Created || this.State == CommunicationState.Opening) + // { + // return EnsureAuthentication(this.Description); + // } + // else + // { + // return this.readOnlyAuthentication; + // } + // } + //} + + public ReadOnlyCollection BaseAddresses + { + get + { + externalBaseAddresses = new ReadOnlyCollection(new List(this.baseAddresses)); + return externalBaseAddresses; + } + } + + public ChannelDispatcherCollection ChannelDispatchers + { + get { return this.channelDispatchers; } + } + + public TimeSpan CloseTimeout + { + get { return this.closeTimeout; } + set + { + if (value < TimeSpan.Zero) + { + string message = SR.SFxTimeoutOutOfRange0; + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", message)); + } + if (TimeoutHelper.IsTooLarge(value)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", SR.SFxTimeoutOutOfRangeTooBig)); + } + + lock (this.ThisLock) + { + this.ThrowIfClosedOrOpened(); + this.closeTimeout = value; + } + } + } + + public ServiceCredentials Credentials + { + get + { + // TODO: Decide if Credentials should be populated? + return null; + //if (this.Description == null) + //{ + // return null; + //} + //else if (this.State == CommunicationState.Created || this.State == CommunicationState.Opening) + //{ + // return EnsureCredentials(this.Description); + //} + //else + //{ + // return this.readOnlyCredentials; + //} + } + } + + protected override TimeSpan DefaultCloseTimeout + { + get { return this.CloseTimeout; } + } + + protected override TimeSpan DefaultOpenTimeout + { + get { return this.OpenTimeout; } + } + + public ServiceDescription Description + { + get { return this.description; } + } + + public IExtensionCollection Extensions + { + get { return this.extensions; } + } + + protected internal IDictionary ImplementedContracts + { + get { return this.implementedContracts; } + } + + internal UriSchemeKeyedCollection InternalBaseAddresses + { + get { return this.baseAddresses; } + } + + public int ManualFlowControlLimit + { + get { return 0; /*return this.ServiceThrottle.ManualFlowControlLimit;*/ } + set { /*this.ServiceThrottle.ManualFlowControlLimit = value;*/ } + } + + public TimeSpan OpenTimeout + { + get { return this.openTimeout; } + set + { + if (value < TimeSpan.Zero) + { + string message = SR.SFxTimeoutOutOfRange0; + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", message)); + } + if (TimeoutHelper.IsTooLarge(value)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", SR.SFxTimeoutOutOfRangeTooBig)); + } + + lock (this.ThisLock) + { + this.ThrowIfClosedOrOpened(); + this.openTimeout = value; + } + } + } + + protected void AddBaseAddress(Uri baseAddress) + { + if (this.initializeDescriptionHasFinished) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException( + SR.SFxCannotCallAddBaseAddress)); + } + this.baseAddresses.Add(baseAddress); + } + + public ServiceEndpoint AddServiceEndpoint(string implementedContract, Binding binding, string address) + { + throw new PlatformNotSupportedException(); + } + + public ServiceEndpoint AddServiceEndpoint(string implementedContract, Binding binding, string address, Uri listenUri) + { + throw new PlatformNotSupportedException(); + } + + public ServiceEndpoint AddServiceEndpoint(string implementedContract, Binding binding, Uri address) + { + throw new PlatformNotSupportedException(); + } + + public ServiceEndpoint AddServiceEndpoint(string implementedContract, Binding binding, Uri address, Uri listenUri) + { + throw new PlatformNotSupportedException(); + } + + public virtual void AddServiceEndpoint(ServiceEndpoint endpoint) + { + throw new PlatformNotSupportedException(); + } + + public void SetEndpointAddress(ServiceEndpoint endpoint, string relativeAddress) + { + throw new PlatformNotSupportedException(); + } + + protected virtual void ApplyConfiguration() + { + throw new PlatformNotSupportedException(); + } + + public virtual ReadOnlyCollection AddDefaultEndpoints() + { + throw new PlatformNotSupportedException(); + } + + void IDisposable.Dispose() + { + CloseAsync().GetAwaiter().GetResult(); + } + + protected abstract ServiceDescription CreateDescription(out IDictionary implementedContracts); + + protected virtual void InitializeRuntime() + { + throw new PlatformNotSupportedException(); + } + + public int IncrementManualFlowControlLimit(int incrementBy) + { + throw new PlatformNotSupportedException(); + } + + protected void InitializeDescription(UriSchemeKeyedCollection baseAddresses) + { + foreach (Uri baseAddress in baseAddresses) + { + this.baseAddresses.Add(baseAddress); + } + IDictionary implementedContracts = null; + ServiceDescription description = CreateDescription(out implementedContracts); + this.description = description; + this.implementedContracts = implementedContracts; + + ApplyConfiguration(); + this.initializeDescriptionHasFinished = true; + } + + // Configuration + //protected void LoadConfigurationSection(ServiceElement serviceSection) + //{ + // throw new PlatformNotSupportedException(); + //} + + protected override void OnAbort() + { + throw new PlatformNotSupportedException(); + } + + protected override Task OnCloseAsync(CancellationToken cancellationToken) + { + throw new PlatformNotSupportedException(); + } + + + + protected override Task OnOpenAsync(CancellationToken cancellationToken) + { + throw new PlatformNotSupportedException(); + } + + protected override void OnClosed() + { + throw new PlatformNotSupportedException(); + } + + protected override void OnOpened() + { + throw new PlatformNotSupportedException(); + } + + protected void ReleasePerformanceCounters() + { + throw new PlatformNotSupportedException(); + } + + class ImplementedContractsContractResolver : IContractResolver + { + IDictionary implementedContracts; + + public ImplementedContractsContractResolver(IDictionary implementedContracts) + { + this.implementedContracts = implementedContracts; + } + + public ContractDescription ResolveContract(string contractName) + { + return this.implementedContracts != null && this.implementedContracts.ContainsKey(contractName) ? this.implementedContracts[contractName] : null; + } + } + + internal class ServiceAndBehaviorsContractResolver : IContractResolver + { + IContractResolver serviceResolver; + Dictionary behaviorContracts; + + public Dictionary BehaviorContracts + { + get { return behaviorContracts; } + } + + public ServiceAndBehaviorsContractResolver(IContractResolver serviceResolver) + { + this.serviceResolver = serviceResolver; + behaviorContracts = new Dictionary(); + } + + public ContractDescription ResolveContract(string contractName) + { + ContractDescription contract = serviceResolver.ResolveContract(contractName); + + if (contract == null) + { + contract = this.behaviorContracts.ContainsKey(contractName) ? this.behaviorContracts[contractName] : null; + } + + return contract; + } + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/ServiceHostObjectModel.cs b/src/CoreWCF.Primitives/src/CoreWCF/ServiceHostObjectModel.cs new file mode 100644 index 000000000..208337b0f --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/ServiceHostObjectModel.cs @@ -0,0 +1,271 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Globalization; +using System.Text; +using CoreWCF.Collections.Generic; +using CoreWCF.Channels; +using CoreWCF.Description; + +namespace CoreWCF +{ + internal class ServiceHostObjectModel : ServiceHostBase where TService : class + { + private IDisposable _disposableInstance; + private TService _singletonInstance; + + public ServiceHostObjectModel(Uri[] baseAddresses) + { + InitializeDescription(new UriSchemeKeyedCollection(baseAddresses)); + } + + public ServiceHostObjectModel(TService singletonInstance, Uri[] baseAddresses) + { + SingletonInstance = singletonInstance ?? throw new ArgumentNullException(nameof(singletonInstance)); + + InitializeDescription(new UriSchemeKeyedCollection(baseAddresses)); + } + + public TService SingletonInstance { get; } + + internal object DisposableInstance + { + get + { + return _disposableInstance; + } + } + + internal ReflectedContractCollection ReflectedContracts { get; private set; } + + protected override ServiceDescription CreateDescription(out IDictionary implementedContracts) + { + ServiceDescription description; + if (SingletonInstance != null) + { + description = ServiceDescription.GetService(SingletonInstance); + } + else + { + description = ServiceDescription.GetService(); + } + + ServiceBehaviorAttribute serviceBehavior = description.Behaviors.Find(); + TService serviceInstanceUsedAsABehavior = (TService)serviceBehavior.GetWellKnownSingleton(); + if (serviceInstanceUsedAsABehavior == null) + { + serviceInstanceUsedAsABehavior = (TService)serviceBehavior.GetHiddenSingleton(); + _disposableInstance = serviceInstanceUsedAsABehavior as IDisposable; + } + + if ((typeof(IServiceBehavior).IsAssignableFrom(typeof(TService)) || typeof(IContractBehavior).IsAssignableFrom(typeof(TService))) + && serviceInstanceUsedAsABehavior == null) + { + serviceInstanceUsedAsABehavior = ServiceDescription.CreateImplementation(); + _disposableInstance = serviceInstanceUsedAsABehavior as IDisposable; + } + + if (SingletonInstance == null) + { + if (serviceInstanceUsedAsABehavior is IServiceBehavior) + { + description.Behaviors.Add((IServiceBehavior)serviceInstanceUsedAsABehavior); + } + } + + ReflectedContractCollection reflectedContracts = new ReflectedContractCollection(); + List interfaces = ServiceReflector.GetInterfaces(); + for (int i = 0; i < interfaces.Count; i++) + { + Type contractType = interfaces[i]; + if (!reflectedContracts.Contains(contractType)) + { + ContractDescription contract = null; + if (serviceInstanceUsedAsABehavior != null) + { + contract = ContractDescription.GetContract(contractType, serviceInstanceUsedAsABehavior); + } + else + { + contract = ContractDescription.GetContract(contractType); + } + + reflectedContracts.Add(contract); + Collection inheritedContracts = contract.GetInheritedContracts(); + for (int j = 0; j < inheritedContracts.Count; j++) + { + ContractDescription inheritedContract = inheritedContracts[j]; + if (!reflectedContracts.Contains(inheritedContract.ContractType)) + { + reflectedContracts.Add(inheritedContract); + } + } + } + } + ReflectedContracts = reflectedContracts; + + implementedContracts = reflectedContracts.ToImplementedContracts(); + return description; + } + + protected override void ApplyConfiguration() + { + // Prevent base class throw by overriding + } + + internal class ReflectedContractCollection : KeyedCollection + { + public ReflectedContractCollection() + : base(null, 4) + { + } + + protected override Type GetKeyForItem(ContractDescription item) + { + if (item == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(item)); + + return item.ContractType; + } + + public IDictionary ToImplementedContracts() + { + Dictionary implementedContracts = new Dictionary(); + foreach (ContractDescription contract in Items) + { + implementedContracts.Add(GetConfigKey(contract), contract); + } + return implementedContracts; + } + + internal static string GetConfigKey(ContractDescription contract) + { + return contract.ConfigurationName; + } + } + + class ReflectedAndBehaviorContractCollection + { + ReflectedContractCollection reflectedContracts; + KeyedByTypeCollection behaviors; + public ReflectedAndBehaviorContractCollection(ReflectedContractCollection reflectedContracts, KeyedByTypeCollection behaviors) + { + this.reflectedContracts = reflectedContracts; + this.behaviors = behaviors; + } + + internal bool Contains(Type implementedContract) + { + if (this.reflectedContracts.Contains(implementedContract)) + { + return true; + } + + //if (this.behaviors.Contains(typeof(ServiceMetadataBehavior)) && ServiceMetadataBehavior.IsMetadataImplementedType(implementedContract)) + //{ + // return true; + //} + + return false; + } + + internal string GetConfigKey(Type implementedContract) + { + if (reflectedContracts.Contains(implementedContract)) + { + return ReflectedContractCollection.GetConfigKey(reflectedContracts[implementedContract]); + } + + //if (this.behaviors.Contains(typeof(ServiceMetadataBehavior)) && ServiceMetadataBehavior.IsMetadataImplementedType(implementedContract)) + //{ + // return ServiceMetadataBehavior.MexContractName; + //} + + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SfxReflectedContractKeyNotFound2, implementedContract.FullName, string.Empty))); + + } + } + + internal Uri MakeAbsoluteUri(Uri uri, Binding binding) + { + Uri result = uri; + if (!result.IsAbsoluteUri) + { + if (binding.Scheme == string.Empty) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.SFxCustomBindingWithoutTransport)); + } + result = GetVia(binding.Scheme, result, InternalBaseAddresses); + if (result == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxEndpointNoMatchingScheme, binding.Scheme, binding.Name, GetBaseAddressSchemes(InternalBaseAddresses)))); + } + } + return result; + } + + internal static String GetBaseAddressSchemes(UriSchemeKeyedCollection uriSchemeKeyedCollection) + { + StringBuilder buffer = new StringBuilder(); + bool firstScheme = true; + foreach (Uri address in uriSchemeKeyedCollection) + { + if (firstScheme) + { + buffer.Append(address.Scheme); + firstScheme = false; + } + else + { + buffer.Append(CultureInfo.CurrentCulture.TextInfo.ListSeparator).Append(address.Scheme); + } + } + + return buffer.ToString(); + } + + internal static Uri GetVia(string scheme, Uri address, UriSchemeKeyedCollection baseAddresses) + { + Uri via = address; + if (!via.IsAbsoluteUri) + { + if (!baseAddresses.Contains(scheme)) + { + return null; + } + + via = GetUri(baseAddresses[scheme], address); + } + return via; + } + + internal static Uri GetUri(Uri baseUri, Uri relativeUri) + { + var path = relativeUri.OriginalString; + if (path.StartsWith("/", StringComparison.Ordinal) || path.StartsWith("\\", StringComparison.Ordinal)) + { + int i = 1; + for (; i < path.Length; ++i) + { + if (path[i] != '/' && path[i] != '\\') + { + break; + } + } + path = path.Substring(i); + } + + // new Uri(Uri, string.Empty) is broken + if (path.Length == 0) + return baseUri; + + if (!baseUri.AbsoluteUri.EndsWith("/", StringComparison.Ordinal)) + { + baseUri = new Uri(baseUri.AbsoluteUri + "/"); + } + + return new Uri(baseUri, path); + } + + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/ServiceKnownTypeAttribute.cs b/src/CoreWCF.Primitives/src/CoreWCF/ServiceKnownTypeAttribute.cs new file mode 100644 index 000000000..fbf9c0c8e --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/ServiceKnownTypeAttribute.cs @@ -0,0 +1,40 @@ +using System; + +namespace CoreWCF +{ + [AttributeUsage(ServiceModelAttributeTargets.ServiceContract | ServiceModelAttributeTargets.OperationContract, Inherited = true, AllowMultiple = true)] + public sealed class ServiceKnownTypeAttribute : Attribute + { + //Type _declaringType; + //string _methodName; + readonly Type _type; + + private ServiceKnownTypeAttribute() + { + // Disallow default constructor + } + + public ServiceKnownTypeAttribute(Type type) + { + _type = type; + } + + // The named method must take a parameter of ICustomAttributeProvider which isn't available so this overload can't be used + //public ServiceKnownTypeAttribute(string methodName) + //{ + // _methodName = methodName; + //} + + //public ServiceKnownTypeAttribute(string methodName, Type declaringType) + //{ + // _methodName = methodName; + // _declaringType = declaringType; + //} + + //public Type DeclaringType => _declaringType; + + //public string MethodName => _methodName; + + public Type Type => _type; + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/ServiceModelAttributeTargets.cs b/src/CoreWCF.Primitives/src/CoreWCF/ServiceModelAttributeTargets.cs new file mode 100644 index 000000000..0110707e4 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/ServiceModelAttributeTargets.cs @@ -0,0 +1,19 @@ +using System; + +namespace CoreWCF +{ + internal static class ServiceModelAttributeTargets + { + public const AttributeTargets ServiceContract = AttributeTargets.Interface | AttributeTargets.Class; + public const AttributeTargets OperationContract = AttributeTargets.Method; + public const AttributeTargets MessageContract = AttributeTargets.Class | AttributeTargets.Struct; + public const AttributeTargets MessageMember = AttributeTargets.Property | AttributeTargets.Field; + public const AttributeTargets Parameter = AttributeTargets.ReturnValue | AttributeTargets.Parameter; + + public const AttributeTargets ServiceBehavior = AttributeTargets.Class; + public const AttributeTargets CallbackBehavior = AttributeTargets.Class; + public const AttributeTargets ClientBehavior = AttributeTargets.Interface; + public const AttributeTargets ContractBehavior = ServiceBehavior | ClientBehavior; + public const AttributeTargets OperationBehavior = AttributeTargets.Method; + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/ServiceModelDictionary.cs b/src/CoreWCF.Primitives/src/CoreWCF/ServiceModelDictionary.cs new file mode 100644 index 000000000..859b5a26f --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/ServiceModelDictionary.cs @@ -0,0 +1,120 @@ +using System; +using System.Collections.Generic; +using System.Xml; + +namespace CoreWCF +{ + class ServiceModelDictionary : IXmlDictionary + { + static public readonly ServiceModelDictionary Version1 = new ServiceModelDictionary(new ServiceModelStringsVersion1()); + readonly ServiceModelStrings _strings; + readonly int _count; + XmlDictionaryString[] _dictionaryStrings1; + XmlDictionaryString[] _dictionaryStrings2; + Dictionary _dictionary; + XmlDictionaryString[] _versionedDictionaryStrings; + + public ServiceModelDictionary(ServiceModelStrings strings) + { + _strings = strings; + _count = strings.Count; + } + + static public ServiceModelDictionary CurrentVersion + { + get + { + return Version1; + } + } + + public XmlDictionaryString CreateString(string value, int key) + { + return new XmlDictionaryString(this, value, key); + } + + public bool TryLookup(string key, out XmlDictionaryString value) + { + if (key == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(key)); + if (_dictionary == null) + { + Dictionary dictionary = new Dictionary(_count); + for (int i = 0; i < _count; i++) + dictionary.Add(_strings[i], i); + _dictionary = dictionary; + } + int id; + if (_dictionary.TryGetValue(key, out id)) + return TryLookup(id, out value); + value = null; + return false; + } + + public bool TryLookup(int key, out XmlDictionaryString value) + { + const int keyThreshold = 32; + if (key < 0 || key >= _count) + { + value = null; + return false; + } + XmlDictionaryString s; + if (key < keyThreshold) + { + if (_dictionaryStrings1 == null) + _dictionaryStrings1 = new XmlDictionaryString[keyThreshold]; + s = _dictionaryStrings1[key]; + if (s == null) + { + s = CreateString(_strings[key], key); + _dictionaryStrings1[key] = s; + } + } + else + { + if (_dictionaryStrings2 == null) + _dictionaryStrings2 = new XmlDictionaryString[_count - keyThreshold]; + s = _dictionaryStrings2[key - keyThreshold]; + if (s == null) + { + s = CreateString(_strings[key], key); + _dictionaryStrings2[key - keyThreshold] = s; + } + } + value = s; + return true; + } + + public bool TryLookup(XmlDictionaryString key, out XmlDictionaryString value) + { + if (key == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(key)); + if (key.Dictionary == this) + { + value = key; + return true; + } + if (key.Dictionary == CurrentVersion) + { + if (_versionedDictionaryStrings == null) + _versionedDictionaryStrings = new XmlDictionaryString[CurrentVersion._count]; + XmlDictionaryString s = _versionedDictionaryStrings[key.Key]; + if (s == null) + { + if (!TryLookup(key.Value, out s)) + { + value = null; + return false; + } + _versionedDictionaryStrings[key.Key] = s; + } + value = s; + return true; + } + value = null; + return false; + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/ServiceModelStrings.cs b/src/CoreWCF.Primitives/src/CoreWCF/ServiceModelStrings.cs new file mode 100644 index 000000000..ea7433b4c --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/ServiceModelStrings.cs @@ -0,0 +1,8 @@ +namespace CoreWCF +{ + internal abstract class ServiceModelStrings + { + public abstract int Count { get; } + public abstract string this[int index] { get; } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/ServiceModelStringsVersion1.cs b/src/CoreWCF.Primitives/src/CoreWCF/ServiceModelStringsVersion1.cs new file mode 100644 index 000000000..f90bbb953 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/ServiceModelStringsVersion1.cs @@ -0,0 +1,995 @@ +namespace CoreWCF +{ + class ServiceModelStringsVersion1 : ServiceModelStrings + { + public const string String0 = "mustUnderstand"; + public const string String1 = "Envelope"; + public const string String2 = "http://www.w3.org/2003/05/soap-envelope"; + public const string String3 = "http://www.w3.org/2005/08/addressing"; + public const string String4 = "Header"; + public const string String5 = "Action"; + public const string String6 = "To"; + public const string String7 = "Body"; + public const string String8 = "Algorithm"; + public const string String9 = "RelatesTo"; + public const string String10 = "http://www.w3.org/2005/08/addressing/anonymous"; + public const string String11 = "URI"; + public const string String12 = "Reference"; + public const string String13 = "MessageID"; + public const string String14 = "Id"; + public const string String15 = "Identifier"; + public const string String16 = "http://schemas.xmlsoap.org/ws/2005/02/rm"; + public const string String17 = "Transforms"; + public const string String18 = "Transform"; + public const string String19 = "DigestMethod"; + public const string String20 = "DigestValue"; + public const string String21 = "Address"; + public const string String22 = "ReplyTo"; + public const string String23 = "SequenceAcknowledgement"; + public const string String24 = "AcknowledgementRange"; + public const string String25 = "Upper"; + public const string String26 = "Lower"; + public const string String27 = "BufferRemaining"; + public const string String28 = "http://schemas.microsoft.com/ws/2006/05/rm"; + public const string String29 = "http://schemas.xmlsoap.org/ws/2005/02/rm/SequenceAcknowledgement"; + public const string String30 = "SecurityTokenReference"; + public const string String31 = "Sequence"; + public const string String32 = "MessageNumber"; + public const string String33 = "http://www.w3.org/2000/09/xmldsig#"; + public const string String34 = "http://www.w3.org/2000/09/xmldsig#enveloped-signature"; + public const string String35 = "KeyInfo"; + public const string String36 = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"; + public const string String37 = "http://www.w3.org/2001/04/xmlenc#"; + public const string String38 = "http://schemas.xmlsoap.org/ws/2005/02/sc"; + public const string String39 = "DerivedKeyToken"; + public const string String40 = "Nonce"; + public const string String41 = "Signature"; + public const string String42 = "SignedInfo"; + public const string String43 = "CanonicalizationMethod"; + public const string String44 = "SignatureMethod"; + public const string String45 = "SignatureValue"; + public const string String46 = "DataReference"; + public const string String47 = "EncryptedData"; + public const string String48 = "EncryptionMethod"; + public const string String49 = "CipherData"; + public const string String50 = "CipherValue"; + public const string String51 = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"; + public const string String52 = "Security"; + public const string String53 = "Timestamp"; + public const string String54 = "Created"; + public const string String55 = "Expires"; + public const string String56 = "Length"; + public const string String57 = "ReferenceList"; + public const string String58 = "ValueType"; + public const string String59 = "Type"; + public const string String60 = "EncryptedHeader"; + public const string String61 = "http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd"; + public const string String62 = "RequestSecurityTokenResponseCollection"; + public const string String63 = "http://schemas.xmlsoap.org/ws/2005/02/trust"; + public const string String64 = "http://schemas.xmlsoap.org/ws/2005/02/trust#BinarySecret"; + public const string String65 = "http://schemas.microsoft.com/ws/2006/02/transactions"; + public const string String66 = "s"; + public const string String67 = "Fault"; + public const string String68 = "MustUnderstand"; + public const string String69 = "role"; + public const string String70 = "relay"; + public const string String71 = "Code"; + public const string String72 = "Reason"; + public const string String73 = "Text"; + public const string String74 = "Node"; + public const string String75 = "Role"; + public const string String76 = "Detail"; + public const string String77 = "Value"; + public const string String78 = "Subcode"; + public const string String79 = "NotUnderstood"; + public const string String80 = "qname"; + public const string String81 = ""; + public const string String82 = "From"; + public const string String83 = "FaultTo"; + public const string String84 = "EndpointReference"; + public const string String85 = "PortType"; + public const string String86 = "ServiceName"; + public const string String87 = "PortName"; + public const string String88 = "ReferenceProperties"; + public const string String89 = "RelationshipType"; + public const string String90 = "Reply"; + public const string String91 = "a"; + public const string String92 = "http://schemas.xmlsoap.org/ws/2006/02/addressingidentity"; + public const string String93 = "Identity"; + public const string String94 = "Spn"; + public const string String95 = "Upn"; + public const string String96 = "Rsa"; + public const string String97 = "Dns"; + public const string String98 = "X509v3Certificate"; + public const string String99 = "http://www.w3.org/2005/08/addressing/fault"; + public const string String100 = "ReferenceParameters"; + public const string String101 = "IsReferenceParameter"; + public const string String102 = "http://www.w3.org/2005/08/addressing/reply"; + public const string String103 = "http://www.w3.org/2005/08/addressing/none"; + public const string String104 = "Metadata"; + public const string String105 = "http://schemas.xmlsoap.org/ws/2004/08/addressing"; + public const string String106 = "http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous"; + public const string String107 = "http://schemas.xmlsoap.org/ws/2004/08/addressing/fault"; + public const string String108 = "http://schemas.xmlsoap.org/ws/2004/06/addressingex"; + public const string String109 = "RedirectTo"; + public const string String110 = "Via"; + public const string String111 = "http://www.w3.org/2001/10/xml-exc-c14n#"; + public const string String112 = "PrefixList"; + public const string String113 = "InclusiveNamespaces"; + public const string String114 = "ec"; + public const string String115 = "SecurityContextToken"; + public const string String116 = "Generation"; + public const string String117 = "Label"; + public const string String118 = "Offset"; + public const string String119 = "Properties"; + public const string String120 = "Cookie"; + public const string String121 = "wsc"; + public const string String122 = "http://schemas.xmlsoap.org/ws/2004/04/sc"; + public const string String123 = "http://schemas.xmlsoap.org/ws/2004/04/security/sc/dk"; + public const string String124 = "http://schemas.xmlsoap.org/ws/2004/04/security/sc/sct"; + public const string String125 = "http://schemas.xmlsoap.org/ws/2004/04/security/trust/RST/SCT"; + public const string String126 = "http://schemas.xmlsoap.org/ws/2004/04/security/trust/RSTR/SCT"; + public const string String127 = "RenewNeeded"; + public const string String128 = "BadContextToken"; + public const string String129 = "c"; + public const string String130 = "http://schemas.xmlsoap.org/ws/2005/02/sc/dk"; + public const string String131 = "http://schemas.xmlsoap.org/ws/2005/02/sc/sct"; + public const string String132 = "http://schemas.xmlsoap.org/ws/2005/02/trust/RST/SCT"; + public const string String133 = "http://schemas.xmlsoap.org/ws/2005/02/trust/RSTR/SCT"; + public const string String134 = "http://schemas.xmlsoap.org/ws/2005/02/trust/RST/SCT/Renew"; + public const string String135 = "http://schemas.xmlsoap.org/ws/2005/02/trust/RSTR/SCT/Renew"; + public const string String136 = "http://schemas.xmlsoap.org/ws/2005/02/trust/RST/SCT/Cancel"; + public const string String137 = "http://schemas.xmlsoap.org/ws/2005/02/trust/RSTR/SCT/Cancel"; + public const string String138 = "http://www.w3.org/2001/04/xmlenc#aes128-cbc"; + public const string String139 = "http://www.w3.org/2001/04/xmlenc#kw-aes128"; + public const string String140 = "http://www.w3.org/2001/04/xmlenc#aes192-cbc"; + public const string String141 = "http://www.w3.org/2001/04/xmlenc#kw-aes192"; + public const string String142 = "http://www.w3.org/2001/04/xmlenc#aes256-cbc"; + public const string String143 = "http://www.w3.org/2001/04/xmlenc#kw-aes256"; + public const string String144 = "http://www.w3.org/2001/04/xmlenc#des-cbc"; + public const string String145 = "http://www.w3.org/2000/09/xmldsig#dsa-sha1"; + public const string String146 = "http://www.w3.org/2001/10/xml-exc-c14n#WithComments"; + public const string String147 = "http://www.w3.org/2000/09/xmldsig#hmac-sha1"; + public const string String148 = "http://www.w3.org/2001/04/xmldsig-more#hmac-sha256"; + public const string String149 = "http://schemas.xmlsoap.org/ws/2005/02/sc/dk/p_sha1"; + public const string String150 = "http://www.w3.org/2001/04/xmlenc#ripemd160"; + public const string String151 = "http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p"; + public const string String152 = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; + public const string String153 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"; + public const string String154 = "http://www.w3.org/2001/04/xmlenc#rsa-1_5"; + public const string String155 = "http://www.w3.org/2000/09/xmldsig#sha1"; + public const string String156 = "http://www.w3.org/2001/04/xmlenc#sha256"; + public const string String157 = "http://www.w3.org/2001/04/xmlenc#sha512"; + public const string String158 = "http://www.w3.org/2001/04/xmlenc#tripledes-cbc"; + public const string String159 = "http://www.w3.org/2001/04/xmlenc#kw-tripledes"; + public const string String160 = "http://schemas.xmlsoap.org/2005/02/trust/tlsnego#TLS_Wrap"; + public const string String161 = "http://schemas.xmlsoap.org/2005/02/trust/spnego#GSS_Wrap"; + public const string String162 = "http://schemas.microsoft.com/ws/2006/05/security"; + public const string String163 = "dnse"; + public const string String164 = "o"; + public const string String165 = "Password"; + public const string String166 = "PasswordText"; + public const string String167 = "Username"; + public const string String168 = "UsernameToken"; + public const string String169 = "BinarySecurityToken"; + public const string String170 = "EncodingType"; + public const string String171 = "KeyIdentifier"; + public const string String172 = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary"; + public const string String173 = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#HexBinary"; + public const string String174 = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Text"; + public const string String175 = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509SubjectKeyIdentifier"; + public const string String176 = "http://docs.oasis-open.org/wss/oasis-wss-kerberos-token-profile-1.1#GSS_Kerberosv5_AP_REQ"; + public const string String177 = "http://docs.oasis-open.org/wss/oasis-wss-kerberos-token-profile-1.1#GSS_Kerberosv5_AP_REQ1510"; + public const string String178 = "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.0#SAMLAssertionID"; + public const string String179 = "Assertion"; + public const string String180 = "urn:oasis:names:tc:SAML:1.0:assertion"; + public const string String181 = "http://docs.oasis-open.org/wss/oasis-wss-rel-token-profile-1.0.pdf#license"; + public const string String182 = "FailedAuthentication"; + public const string String183 = "InvalidSecurityToken"; + public const string String184 = "InvalidSecurity"; + public const string String185 = "k"; + public const string String186 = "SignatureConfirmation"; + public const string String187 = "TokenType"; + public const string String188 = "http://docs.oasis-open.org/wss/oasis-wss-soap-message-security-1.1#ThumbprintSHA1"; + public const string String189 = "http://docs.oasis-open.org/wss/oasis-wss-soap-message-security-1.1#EncryptedKey"; + public const string String190 = "http://docs.oasis-open.org/wss/oasis-wss-soap-message-security-1.1#EncryptedKeySHA1"; + public const string String191 = "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1"; + public const string String192 = "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0"; + public const string String193 = "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLID"; + public const string String194 = "AUTH-HASH"; + public const string String195 = "RequestSecurityTokenResponse"; + public const string String196 = "KeySize"; + public const string String197 = "RequestedTokenReference"; + public const string String198 = "AppliesTo"; + public const string String199 = "Authenticator"; + public const string String200 = "CombinedHash"; + public const string String201 = "BinaryExchange"; + public const string String202 = "Lifetime"; + public const string String203 = "RequestedSecurityToken"; + public const string String204 = "Entropy"; + public const string String205 = "RequestedProofToken"; + public const string String206 = "ComputedKey"; + public const string String207 = "RequestSecurityToken"; + public const string String208 = "RequestType"; + public const string String209 = "Context"; + public const string String210 = "BinarySecret"; + public const string String211 = "http://schemas.xmlsoap.org/ws/2005/02/trust/spnego"; + public const string String212 = " http://schemas.xmlsoap.org/ws/2005/02/trust/tlsnego"; + public const string String213 = "wst"; + public const string String214 = "http://schemas.xmlsoap.org/ws/2004/04/trust"; + public const string String215 = "http://schemas.xmlsoap.org/ws/2004/04/security/trust/RST/Issue"; + public const string String216 = "http://schemas.xmlsoap.org/ws/2004/04/security/trust/RSTR/Issue"; + public const string String217 = "http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue"; + public const string String218 = "http://schemas.xmlsoap.org/ws/2004/04/security/trust/CK/PSHA1"; + public const string String219 = "http://schemas.xmlsoap.org/ws/2004/04/security/trust/SymmetricKey"; + public const string String220 = "http://schemas.xmlsoap.org/ws/2004/04/security/trust/Nonce"; + public const string String221 = "KeyType"; + public const string String222 = "http://schemas.xmlsoap.org/ws/2004/04/trust/SymmetricKey"; + public const string String223 = "http://schemas.xmlsoap.org/ws/2004/04/trust/PublicKey"; + public const string String224 = "Claims"; + public const string String225 = "InvalidRequest"; + public const string String226 = "RequestFailed"; + public const string String227 = "SignWith"; + public const string String228 = "EncryptWith"; + public const string String229 = "EncryptionAlgorithm"; + public const string String230 = "CanonicalizationAlgorithm"; + public const string String231 = "ComputedKeyAlgorithm"; + public const string String232 = "UseKey"; + public const string String233 = "http://schemas.microsoft.com/net/2004/07/secext/WS-SPNego"; + public const string String234 = "http://schemas.microsoft.com/net/2004/07/secext/TLSNego"; + public const string String235 = "t"; + public const string String236 = "http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue"; + public const string String237 = "http://schemas.xmlsoap.org/ws/2005/02/trust/RSTR/Issue"; + public const string String238 = "http://schemas.xmlsoap.org/ws/2005/02/trust/Issue"; + public const string String239 = "http://schemas.xmlsoap.org/ws/2005/02/trust/SymmetricKey"; + public const string String240 = "http://schemas.xmlsoap.org/ws/2005/02/trust/CK/PSHA1"; + public const string String241 = "http://schemas.xmlsoap.org/ws/2005/02/trust/Nonce"; + public const string String242 = "RenewTarget"; + public const string String243 = "CancelTarget"; + public const string String244 = "RequestedTokenCancelled"; + public const string String245 = "RequestedAttachedReference"; + public const string String246 = "RequestedUnattachedReference"; + public const string String247 = "IssuedTokens"; + public const string String248 = "http://schemas.xmlsoap.org/ws/2005/02/trust/Renew"; + public const string String249 = "http://schemas.xmlsoap.org/ws/2005/02/trust/Cancel"; + public const string String250 = "http://schemas.xmlsoap.org/ws/2005/02/trust/PublicKey"; + public const string String251 = "Access"; + public const string String252 = "AccessDecision"; + public const string String253 = "Advice"; + public const string String254 = "AssertionID"; + public const string String255 = "AssertionIDReference"; + public const string String256 = "Attribute"; + public const string String257 = "AttributeName"; + public const string String258 = "AttributeNamespace"; + public const string String259 = "AttributeStatement"; + public const string String260 = "AttributeValue"; + public const string String261 = "Audience"; + public const string String262 = "AudienceRestrictionCondition"; + public const string String263 = "AuthenticationInstant"; + public const string String264 = "AuthenticationMethod"; + public const string String265 = "AuthenticationStatement"; + public const string String266 = "AuthorityBinding"; + public const string String267 = "AuthorityKind"; + public const string String268 = "AuthorizationDecisionStatement"; + public const string String269 = "Binding"; + public const string String270 = "Condition"; + public const string String271 = "Conditions"; + public const string String272 = "Decision"; + public const string String273 = "DoNotCacheCondition"; + public const string String274 = "Evidence"; + public const string String275 = "IssueInstant"; + public const string String276 = "Issuer"; + public const string String277 = "Location"; + public const string String278 = "MajorVersion"; + public const string String279 = "MinorVersion"; + public const string String280 = "NameIdentifier"; + public const string String281 = "Format"; + public const string String282 = "NameQualifier"; + public const string String283 = "Namespace"; + public const string String284 = "NotBefore"; + public const string String285 = "NotOnOrAfter"; + public const string String286 = "saml"; + public const string String287 = "Statement"; + public const string String288 = "Subject"; + public const string String289 = "SubjectConfirmation"; + public const string String290 = "SubjectConfirmationData"; + public const string String291 = "ConfirmationMethod"; + public const string String292 = "urn:oasis:names:tc:SAML:1.0:cm:holder-of-key"; + public const string String293 = "urn:oasis:names:tc:SAML:1.0:cm:sender-vouches"; + public const string String294 = "SubjectLocality"; + public const string String295 = "DNSAddress"; + public const string String296 = "IPAddress"; + public const string String297 = "SubjectStatement"; + public const string String298 = "urn:oasis:names:tc:SAML:1.0:am:unspecified"; + public const string String299 = "xmlns"; + public const string String300 = "Resource"; + public const string String301 = "UserName"; + public const string String302 = "urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName"; + public const string String303 = "EmailName"; + public const string String304 = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"; + public const string String305 = "u"; + public const string String306 = "ChannelInstance"; + public const string String307 = "http://schemas.microsoft.com/ws/2005/02/duplex"; + public const string String308 = "Encoding"; + public const string String309 = "MimeType"; + public const string String310 = "CarriedKeyName"; + public const string String311 = "Recipient"; + public const string String312 = "EncryptedKey"; + public const string String313 = "KeyReference"; + public const string String314 = "e"; + public const string String315 = "http://www.w3.org/2001/04/xmlenc#Element"; + public const string String316 = "http://www.w3.org/2001/04/xmlenc#Content"; + public const string String317 = "KeyName"; + public const string String318 = "MgmtData"; + public const string String319 = "KeyValue"; + public const string String320 = "RSAKeyValue"; + public const string String321 = "Modulus"; + public const string String322 = "Exponent"; + public const string String323 = "X509Data"; + public const string String324 = "X509IssuerSerial"; + public const string String325 = "X509IssuerName"; + public const string String326 = "X509SerialNumber"; + public const string String327 = "X509Certificate"; + public const string String328 = "AckRequested"; + public const string String329 = "http://schemas.xmlsoap.org/ws/2005/02/rm/AckRequested"; + public const string String330 = "AcksTo"; + public const string String331 = "Accept"; + public const string String332 = "CreateSequence"; + public const string String333 = "http://schemas.xmlsoap.org/ws/2005/02/rm/CreateSequence"; + public const string String334 = "CreateSequenceRefused"; + public const string String335 = "CreateSequenceResponse"; + public const string String336 = "http://schemas.xmlsoap.org/ws/2005/02/rm/CreateSequenceResponse"; + public const string String337 = "FaultCode"; + public const string String338 = "InvalidAcknowledgement"; + public const string String339 = "LastMessage"; + public const string String340 = "http://schemas.xmlsoap.org/ws/2005/02/rm/LastMessage"; + public const string String341 = "LastMessageNumberExceeded"; + public const string String342 = "MessageNumberRollover"; + public const string String343 = "Nack"; + public const string String344 = "netrm"; + public const string String345 = "Offer"; + public const string String346 = "r"; + public const string String347 = "SequenceFault"; + public const string String348 = "SequenceTerminated"; + public const string String349 = "TerminateSequence"; + public const string String350 = "http://schemas.xmlsoap.org/ws/2005/02/rm/TerminateSequence"; + public const string String351 = "UnknownSequence"; + public const string String352 = "http://schemas.microsoft.com/ws/2006/02/tx/oletx"; + public const string String353 = "oletx"; + public const string String354 = "OleTxTransaction"; + public const string String355 = "PropagationToken"; + public const string String356 = "http://schemas.xmlsoap.org/ws/2004/10/wscoor"; + public const string String357 = "wscoor"; + public const string String358 = "CreateCoordinationContext"; + public const string String359 = "CreateCoordinationContextResponse"; + public const string String360 = "CoordinationContext"; + public const string String361 = "CurrentContext"; + public const string String362 = "CoordinationType"; + public const string String363 = "RegistrationService"; + public const string String364 = "Register"; + public const string String365 = "RegisterResponse"; + public const string String366 = "ProtocolIdentifier"; + public const string String367 = "CoordinatorProtocolService"; + public const string String368 = "ParticipantProtocolService"; + public const string String369 = "http://schemas.xmlsoap.org/ws/2004/10/wscoor/CreateCoordinationContext"; + public const string String370 = "http://schemas.xmlsoap.org/ws/2004/10/wscoor/CreateCoordinationContextResponse"; + public const string String371 = "http://schemas.xmlsoap.org/ws/2004/10/wscoor/Register"; + public const string String372 = "http://schemas.xmlsoap.org/ws/2004/10/wscoor/RegisterResponse"; + public const string String373 = "http://schemas.xmlsoap.org/ws/2004/10/wscoor/fault"; + public const string String374 = "ActivationCoordinatorPortType"; + public const string String375 = "RegistrationCoordinatorPortType"; + public const string String376 = "InvalidState"; + public const string String377 = "InvalidProtocol"; + public const string String378 = "InvalidParameters"; + public const string String379 = "NoActivity"; + public const string String380 = "ContextRefused"; + public const string String381 = "AlreadyRegistered"; + public const string String382 = "http://schemas.xmlsoap.org/ws/2004/10/wsat"; + public const string String383 = "wsat"; + public const string String384 = "http://schemas.xmlsoap.org/ws/2004/10/wsat/Completion"; + public const string String385 = "http://schemas.xmlsoap.org/ws/2004/10/wsat/Durable2PC"; + public const string String386 = "http://schemas.xmlsoap.org/ws/2004/10/wsat/Volatile2PC"; + public const string String387 = "Prepare"; + public const string String388 = "Prepared"; + public const string String389 = "ReadOnly"; + public const string String390 = "Commit"; + public const string String391 = "Rollback"; + public const string String392 = "Committed"; + public const string String393 = "Aborted"; + public const string String394 = "Replay"; + public const string String395 = "http://schemas.xmlsoap.org/ws/2004/10/wsat/Commit"; + public const string String396 = "http://schemas.xmlsoap.org/ws/2004/10/wsat/Rollback"; + public const string String397 = "http://schemas.xmlsoap.org/ws/2004/10/wsat/Committed"; + public const string String398 = "http://schemas.xmlsoap.org/ws/2004/10/wsat/Aborted"; + public const string String399 = "http://schemas.xmlsoap.org/ws/2004/10/wsat/Prepare"; + public const string String400 = "http://schemas.xmlsoap.org/ws/2004/10/wsat/Prepared"; + public const string String401 = "http://schemas.xmlsoap.org/ws/2004/10/wsat/ReadOnly"; + public const string String402 = "http://schemas.xmlsoap.org/ws/2004/10/wsat/Replay"; + public const string String403 = "http://schemas.xmlsoap.org/ws/2004/10/wsat/fault"; + public const string String404 = "CompletionCoordinatorPortType"; + public const string String405 = "CompletionParticipantPortType"; + public const string String406 = "CoordinatorPortType"; + public const string String407 = "ParticipantPortType"; + public const string String408 = "InconsistentInternalState"; + public const string String409 = "mstx"; + public const string String410 = "Enlistment"; + public const string String411 = "protocol"; + public const string String412 = "LocalTransactionId"; + public const string String413 = "IsolationLevel"; + public const string String414 = "IsolationFlags"; + public const string String415 = "Description"; + public const string String416 = "Loopback"; + public const string String417 = "RegisterInfo"; + public const string String418 = "ContextId"; + public const string String419 = "TokenId"; + public const string String420 = "AccessDenied"; + public const string String421 = "InvalidPolicy"; + public const string String422 = "CoordinatorRegistrationFailed"; + public const string String423 = "TooManyEnlistments"; + public const string String424 = "Disabled"; + public const string String425 = "ActivityId"; + public const string String426 = "http://schemas.microsoft.com/2004/09/ServiceModel/Diagnostics"; + public const string String427 = "http://docs.oasis-open.org/wss/oasis-wss-kerberos-token-profile-1.1#Kerberosv5APREQSHA1"; + public const string String428 = "http://schemas.xmlsoap.org/ws/2002/12/policy"; + public const string String429 = "FloodMessage"; + public const string String430 = "LinkUtility"; + public const string String431 = "Hops"; + public const string String432 = "http://schemas.microsoft.com/net/2006/05/peer/HopCount"; + public const string String433 = "PeerVia"; + public const string String434 = "http://schemas.microsoft.com/net/2006/05/peer"; + public const string String435 = "PeerFlooder"; + public const string String436 = "PeerTo"; + public const string String437 = "http://schemas.microsoft.com/ws/2005/05/routing"; + public const string String438 = "PacketRoutable"; + public const string String439 = "http://schemas.microsoft.com/ws/2005/05/addressing/none"; + public const string String440 = "http://schemas.microsoft.com/ws/2005/05/envelope/none"; + public const string String441 = "http://www.w3.org/2001/XMLSchema-instance"; + public const string String442 = "http://www.w3.org/2001/XMLSchema"; + public const string String443 = "nil"; + public const string String444 = "type"; + public const string String445 = "char"; + public const string String446 = "boolean"; + public const string String447 = "byte"; + public const string String448 = "unsignedByte"; + public const string String449 = "short"; + public const string String450 = "unsignedShort"; + public const string String451 = "int"; + public const string String452 = "unsignedInt"; + public const string String453 = "long"; + public const string String454 = "unsignedLong"; + public const string String455 = "float"; + public const string String456 = "double"; + public const string String457 = "decimal"; + public const string String458 = "dateTime"; + public const string String459 = "string"; + public const string String460 = "base64Binary"; + public const string String461 = "anyType"; + public const string String462 = "duration"; + public const string String463 = "guid"; + public const string String464 = "anyURI"; + public const string String465 = "QName"; + public const string String466 = "time"; + public const string String467 = "date"; + public const string String468 = "hexBinary"; + public const string String469 = "gYearMonth"; + public const string String470 = "gYear"; + public const string String471 = "gMonthDay"; + public const string String472 = "gDay"; + public const string String473 = "gMonth"; + public const string String474 = "integer"; + public const string String475 = "positiveInteger"; + public const string String476 = "negativeInteger"; + public const string String477 = "nonPositiveInteger"; + public const string String478 = "nonNegativeInteger"; + public const string String479 = "normalizedString"; + public const string String480 = "ConnectionLimitReached"; + public const string String481 = "http://schemas.xmlsoap.org/soap/envelope/"; + public const string String482 = "actor"; + public const string String483 = "faultcode"; + public const string String484 = "faultstring"; + public const string String485 = "faultactor"; + public const string String486 = "detail"; + + public override int Count { get { return 487; } } + + public override string this[int index] + { + get + { + DiagnosticUtility.DebugAssert(index >= 0 && index < Count, "check index"); + switch (index) + { + case 0: return String0; + case 1: return String1; + case 2: return String2; + case 3: return String3; + case 4: return String4; + case 5: return String5; + case 6: return String6; + case 7: return String7; + case 8: return String8; + case 9: return String9; + case 10: return String10; + case 11: return String11; + case 12: return String12; + case 13: return String13; + case 14: return String14; + case 15: return String15; + case 16: return String16; + case 17: return String17; + case 18: return String18; + case 19: return String19; + case 20: return String20; + case 21: return String21; + case 22: return String22; + case 23: return String23; + case 24: return String24; + case 25: return String25; + case 26: return String26; + case 27: return String27; + case 28: return String28; + case 29: return String29; + case 30: return String30; + case 31: return String31; + case 32: return String32; + case 33: return String33; + case 34: return String34; + case 35: return String35; + case 36: return String36; + case 37: return String37; + case 38: return String38; + case 39: return String39; + case 40: return String40; + case 41: return String41; + case 42: return String42; + case 43: return String43; + case 44: return String44; + case 45: return String45; + case 46: return String46; + case 47: return String47; + case 48: return String48; + case 49: return String49; + case 50: return String50; + case 51: return String51; + case 52: return String52; + case 53: return String53; + case 54: return String54; + case 55: return String55; + case 56: return String56; + case 57: return String57; + case 58: return String58; + case 59: return String59; + case 60: return String60; + case 61: return String61; + case 62: return String62; + case 63: return String63; + case 64: return String64; + case 65: return String65; + case 66: return String66; + case 67: return String67; + case 68: return String68; + case 69: return String69; + case 70: return String70; + case 71: return String71; + case 72: return String72; + case 73: return String73; + case 74: return String74; + case 75: return String75; + case 76: return String76; + case 77: return String77; + case 78: return String78; + case 79: return String79; + case 80: return String80; + case 81: return String81; + case 82: return String82; + case 83: return String83; + case 84: return String84; + case 85: return String85; + case 86: return String86; + case 87: return String87; + case 88: return String88; + case 89: return String89; + case 90: return String90; + case 91: return String91; + case 92: return String92; + case 93: return String93; + case 94: return String94; + case 95: return String95; + case 96: return String96; + case 97: return String97; + case 98: return String98; + case 99: return String99; + case 100: return String100; + case 101: return String101; + case 102: return String102; + case 103: return String103; + case 104: return String104; + case 105: return String105; + case 106: return String106; + case 107: return String107; + case 108: return String108; + case 109: return String109; + case 110: return String110; + case 111: return String111; + case 112: return String112; + case 113: return String113; + case 114: return String114; + case 115: return String115; + case 116: return String116; + case 117: return String117; + case 118: return String118; + case 119: return String119; + case 120: return String120; + case 121: return String121; + case 122: return String122; + case 123: return String123; + case 124: return String124; + case 125: return String125; + case 126: return String126; + case 127: return String127; + case 128: return String128; + case 129: return String129; + case 130: return String130; + case 131: return String131; + case 132: return String132; + case 133: return String133; + case 134: return String134; + case 135: return String135; + case 136: return String136; + case 137: return String137; + case 138: return String138; + case 139: return String139; + case 140: return String140; + case 141: return String141; + case 142: return String142; + case 143: return String143; + case 144: return String144; + case 145: return String145; + case 146: return String146; + case 147: return String147; + case 148: return String148; + case 149: return String149; + case 150: return String150; + case 151: return String151; + case 152: return String152; + case 153: return String153; + case 154: return String154; + case 155: return String155; + case 156: return String156; + case 157: return String157; + case 158: return String158; + case 159: return String159; + case 160: return String160; + case 161: return String161; + case 162: return String162; + case 163: return String163; + case 164: return String164; + case 165: return String165; + case 166: return String166; + case 167: return String167; + case 168: return String168; + case 169: return String169; + case 170: return String170; + case 171: return String171; + case 172: return String172; + case 173: return String173; + case 174: return String174; + case 175: return String175; + case 176: return String176; + case 177: return String177; + case 178: return String178; + case 179: return String179; + case 180: return String180; + case 181: return String181; + case 182: return String182; + case 183: return String183; + case 184: return String184; + case 185: return String185; + case 186: return String186; + case 187: return String187; + case 188: return String188; + case 189: return String189; + case 190: return String190; + case 191: return String191; + case 192: return String192; + case 193: return String193; + case 194: return String194; + case 195: return String195; + case 196: return String196; + case 197: return String197; + case 198: return String198; + case 199: return String199; + case 200: return String200; + case 201: return String201; + case 202: return String202; + case 203: return String203; + case 204: return String204; + case 205: return String205; + case 206: return String206; + case 207: return String207; + case 208: return String208; + case 209: return String209; + case 210: return String210; + case 211: return String211; + case 212: return String212; + case 213: return String213; + case 214: return String214; + case 215: return String215; + case 216: return String216; + case 217: return String217; + case 218: return String218; + case 219: return String219; + case 220: return String220; + case 221: return String221; + case 222: return String222; + case 223: return String223; + case 224: return String224; + case 225: return String225; + case 226: return String226; + case 227: return String227; + case 228: return String228; + case 229: return String229; + case 230: return String230; + case 231: return String231; + case 232: return String232; + case 233: return String233; + case 234: return String234; + case 235: return String235; + case 236: return String236; + case 237: return String237; + case 238: return String238; + case 239: return String239; + case 240: return String240; + case 241: return String241; + case 242: return String242; + case 243: return String243; + case 244: return String244; + case 245: return String245; + case 246: return String246; + case 247: return String247; + case 248: return String248; + case 249: return String249; + case 250: return String250; + case 251: return String251; + case 252: return String252; + case 253: return String253; + case 254: return String254; + case 255: return String255; + case 256: return String256; + case 257: return String257; + case 258: return String258; + case 259: return String259; + case 260: return String260; + case 261: return String261; + case 262: return String262; + case 263: return String263; + case 264: return String264; + case 265: return String265; + case 266: return String266; + case 267: return String267; + case 268: return String268; + case 269: return String269; + case 270: return String270; + case 271: return String271; + case 272: return String272; + case 273: return String273; + case 274: return String274; + case 275: return String275; + case 276: return String276; + case 277: return String277; + case 278: return String278; + case 279: return String279; + case 280: return String280; + case 281: return String281; + case 282: return String282; + case 283: return String283; + case 284: return String284; + case 285: return String285; + case 286: return String286; + case 287: return String287; + case 288: return String288; + case 289: return String289; + case 290: return String290; + case 291: return String291; + case 292: return String292; + case 293: return String293; + case 294: return String294; + case 295: return String295; + case 296: return String296; + case 297: return String297; + case 298: return String298; + case 299: return String299; + case 300: return String300; + case 301: return String301; + case 302: return String302; + case 303: return String303; + case 304: return String304; + case 305: return String305; + case 306: return String306; + case 307: return String307; + case 308: return String308; + case 309: return String309; + case 310: return String310; + case 311: return String311; + case 312: return String312; + case 313: return String313; + case 314: return String314; + case 315: return String315; + case 316: return String316; + case 317: return String317; + case 318: return String318; + case 319: return String319; + case 320: return String320; + case 321: return String321; + case 322: return String322; + case 323: return String323; + case 324: return String324; + case 325: return String325; + case 326: return String326; + case 327: return String327; + case 328: return String328; + case 329: return String329; + case 330: return String330; + case 331: return String331; + case 332: return String332; + case 333: return String333; + case 334: return String334; + case 335: return String335; + case 336: return String336; + case 337: return String337; + case 338: return String338; + case 339: return String339; + case 340: return String340; + case 341: return String341; + case 342: return String342; + case 343: return String343; + case 344: return String344; + case 345: return String345; + case 346: return String346; + case 347: return String347; + case 348: return String348; + case 349: return String349; + case 350: return String350; + case 351: return String351; + case 352: return String352; + case 353: return String353; + case 354: return String354; + case 355: return String355; + case 356: return String356; + case 357: return String357; + case 358: return String358; + case 359: return String359; + case 360: return String360; + case 361: return String361; + case 362: return String362; + case 363: return String363; + case 364: return String364; + case 365: return String365; + case 366: return String366; + case 367: return String367; + case 368: return String368; + case 369: return String369; + case 370: return String370; + case 371: return String371; + case 372: return String372; + case 373: return String373; + case 374: return String374; + case 375: return String375; + case 376: return String376; + case 377: return String377; + case 378: return String378; + case 379: return String379; + case 380: return String380; + case 381: return String381; + case 382: return String382; + case 383: return String383; + case 384: return String384; + case 385: return String385; + case 386: return String386; + case 387: return String387; + case 388: return String388; + case 389: return String389; + case 390: return String390; + case 391: return String391; + case 392: return String392; + case 393: return String393; + case 394: return String394; + case 395: return String395; + case 396: return String396; + case 397: return String397; + case 398: return String398; + case 399: return String399; + case 400: return String400; + case 401: return String401; + case 402: return String402; + case 403: return String403; + case 404: return String404; + case 405: return String405; + case 406: return String406; + case 407: return String407; + case 408: return String408; + case 409: return String409; + case 410: return String410; + case 411: return String411; + case 412: return String412; + case 413: return String413; + case 414: return String414; + case 415: return String415; + case 416: return String416; + case 417: return String417; + case 418: return String418; + case 419: return String419; + case 420: return String420; + case 421: return String421; + case 422: return String422; + case 423: return String423; + case 424: return String424; + case 425: return String425; + case 426: return String426; + case 427: return String427; + case 428: return String428; + case 429: return String429; + case 430: return String430; + case 431: return String431; + case 432: return String432; + case 433: return String433; + case 434: return String434; + case 435: return String435; + case 436: return String436; + case 437: return String437; + case 438: return String438; + case 439: return String439; + case 440: return String440; + case 441: return String441; + case 442: return String442; + case 443: return String443; + case 444: return String444; + case 445: return String445; + case 446: return String446; + case 447: return String447; + case 448: return String448; + case 449: return String449; + case 450: return String450; + case 451: return String451; + case 452: return String452; + case 453: return String453; + case 454: return String454; + case 455: return String455; + case 456: return String456; + case 457: return String457; + case 458: return String458; + case 459: return String459; + case 460: return String460; + case 461: return String461; + case 462: return String462; + case 463: return String463; + case 464: return String464; + case 465: return String465; + case 466: return String466; + case 467: return String467; + case 468: return String468; + case 469: return String469; + case 470: return String470; + case 471: return String471; + case 472: return String472; + case 473: return String473; + case 474: return String474; + case 475: return String475; + case 476: return String476; + case 477: return String477; + case 478: return String478; + case 479: return String479; + case 480: return String480; + case 481: return String481; + case 482: return String482; + case 483: return String483; + case 484: return String484; + case 485: return String485; + case 486: return String486; + default: return null; + } + } + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/ServiceRequestDelegate.cs b/src/CoreWCF.Primitives/src/CoreWCF/ServiceRequestDelegate.cs new file mode 100644 index 000000000..56d058d07 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/ServiceRequestDelegate.cs @@ -0,0 +1,20 @@ +using CoreWCF.Channels; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; + +namespace CoreWCF +{ + // + // Summary: + // A function that can process a ServiceModel request. + // + // Parameters: + // context: + // The CoreWCF.Channels.RequestContext for the request. + // + // Returns: + // A task that represents the completion of request processing. + public delegate Task ServiceRequestDelegate(RequestContext context); +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/ServiceSecurityContext.cs b/src/CoreWCF.Primitives/src/CoreWCF/ServiceSecurityContext.cs new file mode 100644 index 000000000..afaee69dc --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/ServiceSecurityContext.cs @@ -0,0 +1,198 @@ +using CoreWCF.IdentityModel; +using CoreWCF.IdentityModel.Claims; +using CoreWCF.IdentityModel.Policy; +using CoreWCF.Channels; +using CoreWCF.Security; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Security.Principal; +using System.Text; + +namespace CoreWCF +{ + public class ServiceSecurityContext + { + static ServiceSecurityContext anonymous; + ReadOnlyCollection authorizationPolicies; + AuthorizationContext authorizationContext; + IIdentity primaryIdentity; + Claim identityClaim; + WindowsIdentity windowsIdentity; + + // Perf: delay created authorizationContext using forward chain. + internal ServiceSecurityContext(ReadOnlyCollection authorizationPolicies) + { + if (authorizationPolicies == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("authorizationPolicies"); + } + authorizationContext = null; + this.authorizationPolicies = authorizationPolicies; + } + + internal ServiceSecurityContext(AuthorizationContext authorizationContext) + : this(authorizationContext, EmptyReadOnlyCollection.Instance) + { + } + + internal ServiceSecurityContext(AuthorizationContext authorizationContext, ReadOnlyCollection authorizationPolicies) + { + if (authorizationContext == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("authorizationContext"); + } + if (authorizationPolicies == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("authorizationPolicies"); + } + this.authorizationContext = authorizationContext; + this.authorizationPolicies = authorizationPolicies; + } + + public static ServiceSecurityContext Anonymous + { + get + { + if (anonymous == null) + { + anonymous = new ServiceSecurityContext(EmptyReadOnlyCollection.Instance); + } + return anonymous; + } + } + + public static ServiceSecurityContext Current + { + get + { + ServiceSecurityContext result = null; + + OperationContext operationContext = OperationContext.Current; + if (operationContext != null) + { + MessageProperties properties = operationContext.IncomingMessageProperties; + if (properties != null) + { + SecurityMessageProperty security = properties.Security; + if (security != null) + { + result = security.ServiceSecurityContext; + } + } + } + + return result; + } + } + + public bool IsAnonymous + { + get + { + return this == Anonymous || IdentityClaim == null; + } + } + + // TODO: Claim is from IdentityModel but I needed to expose it. This needs to be resolved. + public Claim IdentityClaim + { + get + { + if (identityClaim == null) + { + identityClaim = Security.SecurityUtils.GetPrimaryIdentityClaim(AuthorizationContext); + } + return identityClaim; + } + } + + public IIdentity PrimaryIdentity + { + get + { + if (this.primaryIdentity == null) + { + IIdentity primaryIdentity = null; + IList identities = GetIdentities(); + // Multiple Identities is treated as anonymous + if (identities != null && identities.Count == 1) + { + primaryIdentity = identities[0]; + } + + this.primaryIdentity = primaryIdentity ?? Security.SecurityUtils.AnonymousIdentity; + } + return this.primaryIdentity; + } + } + + public WindowsIdentity WindowsIdentity + { + get + { + if (this.windowsIdentity == null) + { + WindowsIdentity windowsIdentity = null; + IList identities = GetIdentities(); + if (identities != null) + { + for (int i = 0; i < identities.Count; ++i) + { + WindowsIdentity identity = identities[i] as WindowsIdentity; + if (identity != null) + { + // Multiple Identities is treated as anonymous + if (windowsIdentity != null) + { + windowsIdentity = WindowsIdentity.GetAnonymous(); + break; + } + windowsIdentity = identity; + } + } + } + + this.windowsIdentity = windowsIdentity ?? WindowsIdentity.GetAnonymous(); + } + return this.windowsIdentity; + } + } + + internal ReadOnlyCollection AuthorizationPolicies + { + get + { + return authorizationPolicies; + } + set + { + authorizationPolicies = value; + } + } + + internal AuthorizationContext AuthorizationContext + { + get + { + if (authorizationContext == null) + { + authorizationContext = AuthorizationContext.CreateDefaultAuthorizationContext(authorizationPolicies); + } + return authorizationContext; + } + } + + IList GetIdentities() + { + object identities; + AuthorizationContext authContext = AuthorizationContext; + if (authContext != null && authContext.Properties.TryGetValue(Security.SecurityUtils.Identities, out identities)) + { + return identities as IList; + } + return null; + } + } + +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/SessionMode.cs b/src/CoreWCF.Primitives/src/CoreWCF/SessionMode.cs new file mode 100644 index 000000000..418966334 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/SessionMode.cs @@ -0,0 +1,21 @@ +using System; + +namespace CoreWCF +{ + public enum SessionMode + { + Allowed, + Required, + NotAllowed, + } + + static class SessionModeHelper + { + public static bool IsDefined(SessionMode sessionMode) + { + return (sessionMode == SessionMode.NotAllowed || + sessionMode == SessionMode.Allowed || + sessionMode == SessionMode.Required); + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/SpnEndpointIdentity.cs b/src/CoreWCF.Primitives/src/CoreWCF/SpnEndpointIdentity.cs new file mode 100644 index 000000000..6dfc2ff4f --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/SpnEndpointIdentity.cs @@ -0,0 +1,156 @@ +using CoreWCF.IdentityModel.Claims; +using CoreWCF.Runtime; +using CoreWCF.Security; +using System; +using System.Collections.Generic; +using System.DirectoryServices; +using System.Runtime.InteropServices; +using System.Security.Principal; +using System.Text; +using System.Xml; + +namespace CoreWCF +{ + public class SpnEndpointIdentity : EndpointIdentity + { + private static TimeSpan s_spnLookupTime = TimeSpan.FromMinutes(1); + private SecurityIdentifier _spnSid; + + // Double-checked locking pattern requires volatile for read/write synchronization + private bool _hasSpnSidBeenComputed; + private object _thisLock = new object(); + private static object s_typeLock = new object(); + + // Double-checked locking pattern requires volatile for read/write synchronization + private static DirectoryEntry directoryEntry; + + public static TimeSpan SpnLookupTime + { + get + { + return s_spnLookupTime; + } + set + { + if (value.Ticks < 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(value), value.Ticks, + SR.ValueMustBeNonNegative)); + } + s_spnLookupTime = value; + } + } + + public SpnEndpointIdentity(string spnName) + { + if (spnName == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("spnName"); + + base.Initialize(Claim.CreateSpnClaim(spnName)); + } + + public SpnEndpointIdentity(Claim identity) + { + if (identity == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("identity"); + + if (!ClaimTypes.Spn.Equals(identity.ClaimType)) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.Format(SR.UnrecognizedClaimTypeForIdentity, identity.ClaimType, ClaimTypes.Spn)); + + base.Initialize(identity); + } + + internal override void WriteContentsTo(XmlDictionaryWriter writer) + { + if (writer == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer"); + + writer.WriteElementString(XD.AddressingDictionary.Spn, XD.AddressingDictionary.IdentityExtensionNamespace, (string)IdentityClaim.Resource); + } + + internal SecurityIdentifier GetSpnSid() + { + Fx.Assert(ClaimTypes.Spn.Equals(IdentityClaim.ClaimType) || ClaimTypes.Dns.Equals(IdentityClaim.ClaimType), ""); + if (!_hasSpnSidBeenComputed) + { + lock (_thisLock) + { + if (!_hasSpnSidBeenComputed) + { + string spn = null; + try + { + + if (ClaimTypes.Dns.Equals(IdentityClaim.ClaimType)) + { + spn = "host/" + (string)IdentityClaim.Resource; + } + else + { + spn = (string)IdentityClaim.Resource; + } + // canonicalize SPN for use in LDAP filter following RFC 1960: + if (spn != null) + { + spn = spn.Replace("*", @"\*").Replace("(", @"\(").Replace(")", @"\)"); + } + + DirectoryEntry de = GetDirectoryEntry(); + using (DirectorySearcher searcher = new DirectorySearcher(de)) + { + searcher.CacheResults = true; + searcher.ClientTimeout = SpnLookupTime; + searcher.Filter = "(&(objectCategory=Computer)(objectClass=computer)(servicePrincipalName=" + spn + "))"; + searcher.PropertiesToLoad.Add("objectSid"); + SearchResult result = searcher.FindOne(); + if (result != null) + { + byte[] sidBinaryForm = (byte[])result.Properties["objectSid"][0]; + _spnSid = new SecurityIdentifier(sidBinaryForm, 0); + } + else + { + //SecurityTraceRecordHelper.TraceSpnToSidMappingFailure(spn, null); + } + } + } +#pragma warning disable 56500 // covered by FxCOP + catch (Exception e) + { + // Always immediately rethrow fatal exceptions. + if (Fx.IsFatal(e)) throw; + + if (e is NullReferenceException || e is SEHException) + throw; + + //SecurityTraceRecordHelper.TraceSpnToSidMappingFailure(spn, e); + } + finally + { + _hasSpnSidBeenComputed = true; + } + } + } + } + return _spnSid; + } + + private static DirectoryEntry GetDirectoryEntry() + { + if (directoryEntry == null) + { + lock (s_typeLock) + { + if (directoryEntry == null) + { + DirectoryEntry tmp = new DirectoryEntry(@"LDAP://" + SecurityUtils.GetPrimaryDomain()); + tmp.RefreshCache(new string[] { "name" }); + directoryEntry = tmp; + } + } + } + return directoryEntry; + } + } + +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/TransferMode.cs b/src/CoreWCF.Primitives/src/CoreWCF/TransferMode.cs new file mode 100644 index 000000000..731999c54 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/TransferMode.cs @@ -0,0 +1,10 @@ +namespace CoreWCF +{ + public enum TransferMode + { + Buffered = 0, + Streamed = 1, + StreamedRequest = 2, + StreamedResponse = 3, + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/TryAsyncResult.cs b/src/CoreWCF.Primitives/src/CoreWCF/TryAsyncResult.cs new file mode 100644 index 000000000..13d927144 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/TryAsyncResult.cs @@ -0,0 +1,29 @@ +namespace CoreWCF +{ + public struct TryAsyncResult + { + public static TryAsyncResult FromResult(TResult result) + { + return new TryAsyncResult(result); + } + + public static TryAsyncResult Failed() + { + return TryAsyncResult.FailedResult; + } + } + + public struct TryAsyncResult + { + public static TryAsyncResult FailedResult = new TryAsyncResult(); + + public TryAsyncResult(TResult result) + { + Success = true; + Result = result; + } + + public bool Success { get; private set; } + public TResult Result { get; private set; } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/UnknownMessageReceivedEventArgs.cs b/src/CoreWCF.Primitives/src/CoreWCF/UnknownMessageReceivedEventArgs.cs new file mode 100644 index 000000000..f361437f3 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/UnknownMessageReceivedEventArgs.cs @@ -0,0 +1,20 @@ +using System; +using CoreWCF.Channels; + +namespace CoreWCF +{ + public sealed class UnknownMessageReceivedEventArgs : EventArgs + { + Message message; + + internal UnknownMessageReceivedEventArgs(Message message) + { + this.message = message; + } + + public Message Message + { + get { return message; } + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/UpnEndpointIdentity.cs b/src/CoreWCF.Primitives/src/CoreWCF/UpnEndpointIdentity.cs new file mode 100644 index 000000000..cd01773c6 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/UpnEndpointIdentity.cs @@ -0,0 +1,147 @@ +using CoreWCF.IdentityModel.Claims; +using CoreWCF.Runtime; +using System; +using System.Collections.Generic; +using System.Security.Principal; +using System.Text; +using System.Xml; + +namespace CoreWCF +{ + public class UpnEndpointIdentity : EndpointIdentity + { + SecurityIdentifier _upnSid; + bool _hasUpnSidBeenComputed; + WindowsIdentity _windowsIdentity; + + object _thisLock = new object(); + + public UpnEndpointIdentity(string upnName) + { + if (upnName == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(upnName)); + + Initialize(Claim.CreateUpnClaim(upnName)); + _hasUpnSidBeenComputed = false; + } + + public UpnEndpointIdentity(Claim identity) + { + if (identity == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(identity)); + + if (!identity.ClaimType.Equals(ClaimTypes.Upn)) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.Format(SR.UnrecognizedClaimTypeForIdentity, identity.ClaimType, ClaimTypes.Upn)); + + Initialize(identity); + } + + internal UpnEndpointIdentity(WindowsIdentity windowsIdentity) + { + _windowsIdentity = windowsIdentity ?? throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(windowsIdentity)); + _upnSid = windowsIdentity.User; + _hasUpnSidBeenComputed = true; + } + + internal override void EnsureIdentityClaim() + { + if (_windowsIdentity != null) + { + lock (_thisLock) + { + if (_windowsIdentity != null) + { + Initialize(Claim.CreateUpnClaim(GetUpnFromWindowsIdentity(_windowsIdentity))); + _windowsIdentity.Dispose(); + _windowsIdentity = null; + } + } + } + } + + string GetUpnFromWindowsIdentity(WindowsIdentity windowsIdentity) + { + string downlevelName = null; + string upnName = null; + + try + { + downlevelName = windowsIdentity.Name; + + if (IsMachineJoinedToDomain()) + { + upnName = GetUpnFromDownlevelName(downlevelName); + } + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + } + + // if the AD cannot be queried for the fully qualified domain name, + // fall back to the downlevel UPN name + return upnName ?? downlevelName; + } + + bool IsMachineJoinedToDomain() + { + throw new PlatformNotSupportedException(); + } + + string GetUpnFromDownlevelName(string downlevelName) + { + throw new PlatformNotSupportedException(); + } + + internal override void WriteContentsTo(XmlDictionaryWriter writer) + { + if (writer == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(writer)); + + writer.WriteElementString(XD.AddressingDictionary.Upn, XD.AddressingDictionary.IdentityExtensionNamespace, (string)IdentityClaim.Resource); + } + + internal SecurityIdentifier GetUpnSid() + { + Fx.Assert(ClaimTypes.Upn.Equals(IdentityClaim.ClaimType), ""); + if (!_hasUpnSidBeenComputed) + { + lock (_thisLock) + { + string upn = (string)IdentityClaim.Resource; + if (!_hasUpnSidBeenComputed) + { + try + { + var userAccount = new NTAccount(upn); + _upnSid = userAccount.Translate(typeof(SecurityIdentifier)) as SecurityIdentifier; + } + catch (Exception e) + { + // Always immediately rethrow fatal exceptions. + if (Fx.IsFatal(e)) + { + throw; + } + + if (e is NullReferenceException) + { + throw; + } + + //SecurityTraceRecordHelper.TraceSpnToSidMappingFailure(upn, e); + } + finally + { + _hasUpnSidBeenComputed = true; + } + } + } + } + return _upnSid; + } + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/UriSchemeKeyedCollection.cs b/src/CoreWCF.Primitives/src/CoreWCF/UriSchemeKeyedCollection.cs new file mode 100644 index 000000000..9774ab6dc --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/UriSchemeKeyedCollection.cs @@ -0,0 +1,77 @@ +using CoreWCF.Collections.Generic; +using System; + +namespace CoreWCF +{ + public class UriSchemeKeyedCollection : SynchronizedKeyedCollection + { + internal UriSchemeKeyedCollection(object syncRoot) + : base(syncRoot) + { + } + + public UriSchemeKeyedCollection(params Uri[] addresses) + { + if (addresses == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(addresses)); + + for (int i = 0; i < addresses.Length; i++) + { + Add(addresses[i]); + } + } + + protected override string GetKeyForItem(Uri item) + { + return item.Scheme; + } + + protected override void InsertItem(int index, Uri item) + { + ValidateBaseAddress(item, "item"); + if (Contains(item.Scheme)) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(nameof(item), SR.Format(SR.BaseAddressDuplicateScheme, item.Scheme)); + + base.InsertItem(index, item); + } + + protected override void SetItem(int index, Uri item) + { + ValidateBaseAddress(item, "item"); + if (this[index].Scheme != item.Scheme) + { + if (Contains(item.Scheme)) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(nameof(item), SR.Format(SR.BaseAddressDuplicateScheme, item.Scheme)); + } + base.SetItem(index, item); + } + + internal static void ValidateBaseAddress(Uri uri, string argumentName) + { + if (uri == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(argumentName); + } + + if (!uri.IsAbsoluteUri) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(argumentName, SR.BaseAddressMustBeAbsolute); + } + + if (!string.IsNullOrEmpty(uri.UserInfo)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(argumentName, SR.BaseAddressCannotHaveUserInfo); + } + + if (!string.IsNullOrEmpty(uri.Query)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(argumentName, SR.BaseAddressCannotHaveQuery); + } + + if (!string.IsNullOrEmpty(uri.Fragment)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(argumentName, SR.BaseAddressCannotHaveFragment); + } + } + } +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/WSAddressing10ProblemHeaderQNameFault.cs b/src/CoreWCF.Primitives/src/CoreWCF/WSAddressing10ProblemHeaderQNameFault.cs new file mode 100644 index 000000000..5f32cc7ee --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/WSAddressing10ProblemHeaderQNameFault.cs @@ -0,0 +1,147 @@ +using System.Globalization; +using System.Xml; +using CoreWCF.Channels; + +namespace CoreWCF +{ + class WSAddressing10ProblemHeaderQNameFault : MessageFault + { + FaultCode code; + FaultReason reason; + string actor; + string node; + string invalidHeaderName; + + public WSAddressing10ProblemHeaderQNameFault(MessageHeaderException e) + { + invalidHeaderName = e.HeaderName; + + if (e.IsDuplicate) + { + code = FaultCode.CreateSenderFaultCode( + new FaultCode(Addressing10Strings.InvalidAddressingHeader, + AddressingVersion.WSAddressing10.Namespace, + new FaultCode(Addressing10Strings.InvalidCardinality, + AddressingVersion.WSAddressing10.Namespace))); + } + else + { + code = FaultCode.CreateSenderFaultCode( + new FaultCode(Addressing10Strings.MessageAddressingHeaderRequired, + AddressingVersion.WSAddressing10.Namespace)); + } + + reason = new FaultReason(e.Message, CultureInfo.CurrentCulture); + actor = ""; + node = ""; + } + + public WSAddressing10ProblemHeaderQNameFault(ActionMismatchAddressingException e) + { + invalidHeaderName = AddressingStrings.Action; + code = FaultCode.CreateSenderFaultCode( + new FaultCode(Addressing10Strings.ActionMismatch, AddressingVersion.WSAddressing10.Namespace)); + reason = new FaultReason(e.Message, CultureInfo.CurrentCulture); + actor = ""; + node = ""; + } + + public override string Actor + { + get + { + return actor; + } + } + + public override FaultCode Code + { + get + { + return code; + } + } + + public override bool HasDetail + { + get + { + return true; + } + } + + public override string Node + { + get + { + return node; + } + } + + public override FaultReason Reason + { + get + { + return reason; + } + } + + protected override void OnWriteDetail(XmlDictionaryWriter writer, EnvelopeVersion version) + { + if (version == EnvelopeVersion.Soap12) // Soap11 wants the detail in the header + { + OnWriteStartDetail(writer, version); + OnWriteDetailContents(writer); + writer.WriteEndElement(); + } + } + + protected override void OnWriteDetailContents(XmlDictionaryWriter writer) + { + writer.WriteStartElement(Addressing10Strings.ProblemHeaderQName, AddressingVersion.WSAddressing10.Namespace); + writer.WriteQualifiedName(invalidHeaderName, AddressingVersion.WSAddressing10.Namespace); + writer.WriteEndElement(); + } + + public void AddHeaders(MessageHeaders headers) + { + if (headers.MessageVersion.Envelope == EnvelopeVersion.Soap11) + { + headers.Add(new WSAddressing10ProblemHeaderQNameHeader(invalidHeaderName)); + } + } + + class WSAddressing10ProblemHeaderQNameHeader : MessageHeader + { + string invalidHeaderName; + + public WSAddressing10ProblemHeaderQNameHeader(string invalidHeaderName) + { + this.invalidHeaderName = invalidHeaderName; + } + + public override string Name + { + get { return Addressing10Strings.FaultDetail; } + } + + public override string Namespace + { + get { return AddressingVersion.WSAddressing10.Namespace; } + } + + protected override void OnWriteStartHeader(XmlDictionaryWriter writer, MessageVersion messageVersion) + { + writer.WriteStartElement(Name, Namespace); + } + + protected override void OnWriteHeaderContents(XmlDictionaryWriter writer, MessageVersion messageVersion) + { + writer.WriteStartElement(Addressing10Strings.ProblemHeaderQName, Namespace); + writer.WriteQualifiedName(invalidHeaderName, Namespace); + writer.WriteEndElement(); + } + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/X509CertificateEndpointIdentity.cs b/src/CoreWCF.Primitives/src/CoreWCF/X509CertificateEndpointIdentity.cs new file mode 100644 index 000000000..a4597071b --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/X509CertificateEndpointIdentity.cs @@ -0,0 +1,92 @@ +using CoreWCF.IdentityModel.Claims; +using System; +using System.Collections.Generic; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using System.Xml; + +namespace CoreWCF +{ + public class X509CertificateEndpointIdentity : EndpointIdentity + { + X509Certificate2Collection certificateCollection = new X509Certificate2Collection(); + + public X509CertificateEndpointIdentity(X509Certificate2 certificate) + { + if (certificate == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("certificate"); + + base.Initialize(new Claim(ClaimTypes.Thumbprint, certificate.GetCertHash(), Rights.PossessProperty)); + + certificateCollection.Add(certificate); + } + + public X509CertificateEndpointIdentity(X509Certificate2 primaryCertificate, X509Certificate2Collection supportingCertificates) + { + if (primaryCertificate == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("primaryCertificate"); + + if (supportingCertificates == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("supportingCertificates"); + + base.Initialize(new Claim(ClaimTypes.Thumbprint, primaryCertificate.GetCertHash(), Rights.PossessProperty)); + + certificateCollection.Add(primaryCertificate); + + for (int i = 0; i < supportingCertificates.Count; ++i) + { + certificateCollection.Add(supportingCertificates[i]); + } + } + + internal X509CertificateEndpointIdentity(XmlDictionaryReader reader) + { + if (reader == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader"); + + reader.MoveToContent(); + if (reader.IsEmptyElement) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.Format(SR.UnexpectedEmptyElementExpectingClaim, XD.AddressingDictionary.X509v3Certificate.Value, XD.AddressingDictionary.IdentityExtensionNamespace.Value))); + + reader.ReadStartElement(XD.XmlSignatureDictionary.X509Data, XD.XmlSignatureDictionary.Namespace); + while (reader.IsStartElement(XD.XmlSignatureDictionary.X509Certificate, XD.XmlSignatureDictionary.Namespace)) + { + X509Certificate2 certificate = new X509Certificate2(Convert.FromBase64String(reader.ReadElementString())); + if (certificateCollection.Count == 0) + { + // This is the first certificate. We assume this as the primary + // certificate and initialize the base class. + base.Initialize(new Claim(ClaimTypes.Thumbprint, certificate.GetCertHash(), Rights.PossessProperty)); + } + + certificateCollection.Add(certificate); + } + + reader.ReadEndElement(); + + if (certificateCollection.Count == 0) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.Format(SR.UnexpectedEmptyElementExpectingClaim, XD.AddressingDictionary.X509v3Certificate.Value, XD.AddressingDictionary.IdentityExtensionNamespace.Value))); + } + + public X509Certificate2Collection Certificates + { + get { return certificateCollection; } + } + + internal override void WriteContentsTo(XmlDictionaryWriter writer) + { + if (writer == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer"); + + writer.WriteStartElement(XD.XmlSignatureDictionary.Prefix.Value, XD.XmlSignatureDictionary.KeyInfo, XD.XmlSignatureDictionary.Namespace); + writer.WriteStartElement(XD.XmlSignatureDictionary.Prefix.Value, XD.XmlSignatureDictionary.X509Data, XD.XmlSignatureDictionary.Namespace); + for (int i = 0; i < certificateCollection.Count; ++i) + { + writer.WriteElementString(XD.XmlSignatureDictionary.X509Certificate, XD.XmlSignatureDictionary.Namespace, Convert.ToBase64String(certificateCollection[i].RawData)); + } + writer.WriteEndElement(); + writer.WriteEndElement(); + } + } + +} diff --git a/src/CoreWCF.Primitives/src/CoreWCF/XD.cs b/src/CoreWCF.Primitives/src/CoreWCF/XD.cs new file mode 100644 index 000000000..b3a608ae2 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/XD.cs @@ -0,0 +1,2493 @@ +using System.Xml; + +namespace CoreWCF +{ + // Static Xml Dictionary + static class XD + { + static public ServiceModelDictionary Dictionary { get { return ServiceModelDictionary.CurrentVersion; } } + + static ActivityIdFlowDictionary activityIdFlowDictionary; + static AddressingDictionary addressingDictionary; + static Addressing10Dictionary addressing10Dictionary; + static Addressing200408Dictionary addressing200408Dictionary; + static AddressingNoneDictionary addressingNoneDictionary; + static AtomicTransactionExternalDictionary atomicTransactionExternalDictionary; + static AtomicTransactionExternal10Dictionary atomicTransactionExternal10Dictionary; + static CoordinationExternalDictionary coordinationExternalDictionary; + static CoordinationExternal10Dictionary coordinationExternal10Dictionary; + static DotNetAddressingDictionary dotNetAddressingDictionary; + static DotNetAtomicTransactionExternalDictionary dotNetAtomicTransactionExternalDictionary; + static DotNetOneWayDictionary dotNetOneWayDictionary; + static DotNetSecurityDictionary dotNetSecurityDictionary; + static ExclusiveC14NDictionary exclusiveC14NDictionary; + static MessageDictionary messageDictionary; + static Message11Dictionary message11Dictionary; + static Message12Dictionary message12Dictionary; + static OleTxTransactionExternalDictionary oleTxTransactionExternalDictionary; + static PeerWireStringsDictionary peerWireStringsDictionary; + static PolicyDictionary policyDictionary; + static SamlDictionary samlDictionary; + static SecureConversationApr2004Dictionary secureConversationApr2004Dictionary; + static SecureConversationFeb2005Dictionary secureConversationFeb2005Dictionary; + static SecurityAlgorithmDictionary securityAlgorithmDictionary; + static SecurityJan2004Dictionary securityJan2004Dictionary; + static SecurityXXX2005Dictionary securityXXX2005Dictionary; + static SerializationDictionary serializationDictionary; + static TrustApr2004Dictionary trustApr2004Dictionary; + static TrustFeb2005Dictionary trustFeb2005Dictionary; + static UtilityDictionary utilityDictionary; + static WsrmFeb2005Dictionary wsrmFeb2005Dictionary; + static XmlEncryptionDictionary xmlEncryptionDictionary; + static XmlSignatureDictionary xmlSignatureDictionary; + + static public ActivityIdFlowDictionary ActivityIdFlowDictionary + { + get + { + if (activityIdFlowDictionary == null) + activityIdFlowDictionary = new ActivityIdFlowDictionary(Dictionary); + return activityIdFlowDictionary; + } + } + + static public AddressingDictionary AddressingDictionary + { + get + { + if (addressingDictionary == null) + addressingDictionary = new AddressingDictionary(Dictionary); + return addressingDictionary; + } + } + + static public Addressing10Dictionary Addressing10Dictionary + { + get + { + if (addressing10Dictionary == null) + addressing10Dictionary = new Addressing10Dictionary(Dictionary); + return addressing10Dictionary; + } + } + + static public Addressing200408Dictionary Addressing200408Dictionary + { + get + { + if (addressing200408Dictionary == null) + addressing200408Dictionary = new Addressing200408Dictionary(Dictionary); + return addressing200408Dictionary; + } + } + + static public AddressingNoneDictionary AddressingNoneDictionary + { + get + { + if (addressingNoneDictionary == null) + addressingNoneDictionary = new AddressingNoneDictionary(Dictionary); + return addressingNoneDictionary; + } + } + + static public AtomicTransactionExternalDictionary AtomicTransactionExternalDictionary + { + get + { + if (atomicTransactionExternalDictionary == null) + atomicTransactionExternalDictionary = new AtomicTransactionExternalDictionary(Dictionary); + return atomicTransactionExternalDictionary; + } + } + + static public AtomicTransactionExternal10Dictionary AtomicTransactionExternal10Dictionary + { + get + { + if (atomicTransactionExternal10Dictionary == null) + atomicTransactionExternal10Dictionary = new AtomicTransactionExternal10Dictionary(Dictionary); + return atomicTransactionExternal10Dictionary; + } + } + + static public CoordinationExternalDictionary CoordinationExternalDictionary + { + get + { + if (coordinationExternalDictionary == null) + coordinationExternalDictionary = new CoordinationExternalDictionary(Dictionary); + return coordinationExternalDictionary; + } + } + + static public CoordinationExternal10Dictionary CoordinationExternal10Dictionary + { + get + { + if (coordinationExternal10Dictionary == null) + coordinationExternal10Dictionary = new CoordinationExternal10Dictionary(Dictionary); + return coordinationExternal10Dictionary; + } + } + + static public DotNetAddressingDictionary DotNetAddressingDictionary + { + get + { + if (dotNetAddressingDictionary == null) + dotNetAddressingDictionary = new DotNetAddressingDictionary(Dictionary); + return dotNetAddressingDictionary; + } + } + + static public DotNetAtomicTransactionExternalDictionary DotNetAtomicTransactionExternalDictionary + { + get + { + if (dotNetAtomicTransactionExternalDictionary == null) + dotNetAtomicTransactionExternalDictionary = new DotNetAtomicTransactionExternalDictionary(Dictionary); + return dotNetAtomicTransactionExternalDictionary; + } + } + + static public DotNetOneWayDictionary DotNetOneWayDictionary + { + get + { + if (dotNetOneWayDictionary == null) + dotNetOneWayDictionary = new DotNetOneWayDictionary(Dictionary); + return dotNetOneWayDictionary; + } + } + + static public DotNetSecurityDictionary DotNetSecurityDictionary + { + get + { + if (dotNetSecurityDictionary == null) + dotNetSecurityDictionary = new DotNetSecurityDictionary(Dictionary); + return dotNetSecurityDictionary; + } + } + + static public ExclusiveC14NDictionary ExclusiveC14NDictionary + { + get + { + if (exclusiveC14NDictionary == null) + exclusiveC14NDictionary = new ExclusiveC14NDictionary(Dictionary); + return exclusiveC14NDictionary; + } + } + + static public MessageDictionary MessageDictionary + { + get + { + if (messageDictionary == null) + messageDictionary = new MessageDictionary(Dictionary); + return messageDictionary; + } + } + + static public Message11Dictionary Message11Dictionary + { + get + { + if (message11Dictionary == null) + message11Dictionary = new Message11Dictionary(Dictionary); + return message11Dictionary; + } + } + + static public Message12Dictionary Message12Dictionary + { + get + { + if (message12Dictionary == null) + message12Dictionary = new Message12Dictionary(Dictionary); + return message12Dictionary; + } + } + + static public OleTxTransactionExternalDictionary OleTxTransactionExternalDictionary + { + get + { + if (oleTxTransactionExternalDictionary == null) + oleTxTransactionExternalDictionary = new OleTxTransactionExternalDictionary(Dictionary); + return oleTxTransactionExternalDictionary; + } + } + + static public PeerWireStringsDictionary PeerWireStringsDictionary + { + get + { + if (peerWireStringsDictionary == null) + peerWireStringsDictionary = new PeerWireStringsDictionary(Dictionary); + return peerWireStringsDictionary; + } + } + + static public PolicyDictionary PolicyDictionary + { + get + { + if (policyDictionary == null) + policyDictionary = new PolicyDictionary(Dictionary); + return policyDictionary; + } + } + + static public SamlDictionary SamlDictionary + { + get + { + if (samlDictionary == null) + samlDictionary = new SamlDictionary(Dictionary); + return samlDictionary; + } + } + + static public SecureConversationApr2004Dictionary SecureConversationApr2004Dictionary + { + get + { + if (secureConversationApr2004Dictionary == null) + secureConversationApr2004Dictionary = new SecureConversationApr2004Dictionary(Dictionary); + return secureConversationApr2004Dictionary; + } + } + + static public SecureConversationFeb2005Dictionary SecureConversationFeb2005Dictionary + { + get + { + if (secureConversationFeb2005Dictionary == null) + secureConversationFeb2005Dictionary = new SecureConversationFeb2005Dictionary(Dictionary); + return secureConversationFeb2005Dictionary; + } + } + + static public SecurityAlgorithmDictionary SecurityAlgorithmDictionary + { + get + { + if (securityAlgorithmDictionary == null) + securityAlgorithmDictionary = new SecurityAlgorithmDictionary(Dictionary); + return securityAlgorithmDictionary; + } + } + + static public SecurityJan2004Dictionary SecurityJan2004Dictionary + { + get + { + if (securityJan2004Dictionary == null) + securityJan2004Dictionary = new SecurityJan2004Dictionary(Dictionary); + return securityJan2004Dictionary; + } + } + + static public SecurityXXX2005Dictionary SecurityXXX2005Dictionary + { + get + { + if (securityXXX2005Dictionary == null) + securityXXX2005Dictionary = new SecurityXXX2005Dictionary(Dictionary); + return securityXXX2005Dictionary; + } + } + + static public SerializationDictionary SerializationDictionary + { + get + { + if (serializationDictionary == null) + serializationDictionary = new SerializationDictionary(Dictionary); + return serializationDictionary; + } + } + + static public TrustApr2004Dictionary TrustApr2004Dictionary + { + get + { + if (trustApr2004Dictionary == null) + trustApr2004Dictionary = new TrustApr2004Dictionary(Dictionary); + return trustApr2004Dictionary; + } + } + + static public TrustFeb2005Dictionary TrustFeb2005Dictionary + { + get + { + if (trustFeb2005Dictionary == null) + trustFeb2005Dictionary = new TrustFeb2005Dictionary(Dictionary); + return trustFeb2005Dictionary; + } + } + + static public UtilityDictionary UtilityDictionary + { + get + { + if (utilityDictionary == null) + utilityDictionary = new UtilityDictionary(Dictionary); + return utilityDictionary; + } + } + + static public WsrmFeb2005Dictionary WsrmFeb2005Dictionary + { + get + { + if (wsrmFeb2005Dictionary == null) + wsrmFeb2005Dictionary = new WsrmFeb2005Dictionary(Dictionary); + return wsrmFeb2005Dictionary; + } + } + + static public XmlEncryptionDictionary XmlEncryptionDictionary + { + get + { + if (xmlEncryptionDictionary == null) + xmlEncryptionDictionary = new XmlEncryptionDictionary(Dictionary); + return xmlEncryptionDictionary; + } + } + + static public XmlSignatureDictionary XmlSignatureDictionary + { + get + { + if (xmlSignatureDictionary == null) + xmlSignatureDictionary = new XmlSignatureDictionary(Dictionary); + return xmlSignatureDictionary; + } + } + + } + + internal class ActivityIdFlowDictionary + { + public XmlDictionaryString ActivityId; + public XmlDictionaryString ActivityIdNamespace; + + public ActivityIdFlowDictionary(ServiceModelDictionary dictionary) + { + ActivityId = dictionary.CreateString(ServiceModelStringsVersion1.String425, 425); + ActivityIdNamespace = dictionary.CreateString(ServiceModelStringsVersion1.String426, 426); + } + } + + internal class AddressingDictionary + { + public XmlDictionaryString Action; + public XmlDictionaryString To; + public XmlDictionaryString RelatesTo; + public XmlDictionaryString MessageId; + public XmlDictionaryString Address; + public XmlDictionaryString ReplyTo; + public XmlDictionaryString Empty; + public XmlDictionaryString From; + public XmlDictionaryString FaultTo; + public XmlDictionaryString EndpointReference; + public XmlDictionaryString PortType; + public XmlDictionaryString ServiceName; + public XmlDictionaryString PortName; + public XmlDictionaryString ReferenceProperties; + public XmlDictionaryString RelationshipType; + public XmlDictionaryString Reply; + public XmlDictionaryString Prefix; + public XmlDictionaryString IdentityExtensionNamespace; + public XmlDictionaryString Identity; + public XmlDictionaryString Spn; + public XmlDictionaryString Upn; + public XmlDictionaryString Rsa; + public XmlDictionaryString Dns; + public XmlDictionaryString X509v3Certificate; + public XmlDictionaryString ReferenceParameters; + public XmlDictionaryString IsReferenceParameter; + + public AddressingDictionary(ServiceModelDictionary dictionary) + { + Action = dictionary.CreateString(ServiceModelStringsVersion1.String5, 5); + To = dictionary.CreateString(ServiceModelStringsVersion1.String6, 6); + RelatesTo = dictionary.CreateString(ServiceModelStringsVersion1.String9, 9); + MessageId = dictionary.CreateString(ServiceModelStringsVersion1.String13, 13); + Address = dictionary.CreateString(ServiceModelStringsVersion1.String21, 21); + ReplyTo = dictionary.CreateString(ServiceModelStringsVersion1.String22, 22); + Empty = dictionary.CreateString(ServiceModelStringsVersion1.String81, 81); + From = dictionary.CreateString(ServiceModelStringsVersion1.String82, 82); + FaultTo = dictionary.CreateString(ServiceModelStringsVersion1.String83, 83); + EndpointReference = dictionary.CreateString(ServiceModelStringsVersion1.String84, 84); + PortType = dictionary.CreateString(ServiceModelStringsVersion1.String85, 85); + ServiceName = dictionary.CreateString(ServiceModelStringsVersion1.String86, 86); + PortName = dictionary.CreateString(ServiceModelStringsVersion1.String87, 87); + ReferenceProperties = dictionary.CreateString(ServiceModelStringsVersion1.String88, 88); + RelationshipType = dictionary.CreateString(ServiceModelStringsVersion1.String89, 89); + Reply = dictionary.CreateString(ServiceModelStringsVersion1.String90, 90); + Prefix = dictionary.CreateString(ServiceModelStringsVersion1.String91, 91); + IdentityExtensionNamespace = dictionary.CreateString(ServiceModelStringsVersion1.String92, 92); + Identity = dictionary.CreateString(ServiceModelStringsVersion1.String93, 93); + Spn = dictionary.CreateString(ServiceModelStringsVersion1.String94, 94); + Upn = dictionary.CreateString(ServiceModelStringsVersion1.String95, 95); + Rsa = dictionary.CreateString(ServiceModelStringsVersion1.String96, 96); + Dns = dictionary.CreateString(ServiceModelStringsVersion1.String97, 97); + X509v3Certificate = dictionary.CreateString(ServiceModelStringsVersion1.String98, 98); + ReferenceParameters = dictionary.CreateString(ServiceModelStringsVersion1.String100, 100); + IsReferenceParameter = dictionary.CreateString(ServiceModelStringsVersion1.String101, 101); + } + } + + class Addressing10Dictionary + { + public XmlDictionaryString Namespace; + public XmlDictionaryString Anonymous; + public XmlDictionaryString FaultAction; + public XmlDictionaryString ReplyRelationship; + public XmlDictionaryString NoneAddress; + public XmlDictionaryString Metadata; + + public Addressing10Dictionary(ServiceModelDictionary dictionary) + { + Namespace = dictionary.CreateString(ServiceModelStringsVersion1.String3, 3); + Anonymous = dictionary.CreateString(ServiceModelStringsVersion1.String10, 10); + FaultAction = dictionary.CreateString(ServiceModelStringsVersion1.String99, 99); + ReplyRelationship = dictionary.CreateString(ServiceModelStringsVersion1.String102, 102); + NoneAddress = dictionary.CreateString(ServiceModelStringsVersion1.String103, 103); + Metadata = dictionary.CreateString(ServiceModelStringsVersion1.String104, 104); + } + } + + class Addressing200408Dictionary + { + public XmlDictionaryString Namespace; + public XmlDictionaryString Anonymous; + public XmlDictionaryString FaultAction; + + public Addressing200408Dictionary(ServiceModelDictionary dictionary) + { + Namespace = dictionary.CreateString(ServiceModelStringsVersion1.String105, 105); + Anonymous = dictionary.CreateString(ServiceModelStringsVersion1.String106, 106); + FaultAction = dictionary.CreateString(ServiceModelStringsVersion1.String107, 107); + } + } + + class AddressingNoneDictionary + { + public XmlDictionaryString Namespace; + + public AddressingNoneDictionary(ServiceModelDictionary dictionary) + { + Namespace = dictionary.CreateString(ServiceModelStringsVersion1.String439, 439); + } + } + + class AtomicTransactionExternalDictionary + { + public XmlDictionaryString Prefix; + public XmlDictionaryString Prepare; + public XmlDictionaryString Prepared; + public XmlDictionaryString ReadOnly; + public XmlDictionaryString Commit; + public XmlDictionaryString Rollback; + public XmlDictionaryString Committed; + public XmlDictionaryString Aborted; + public XmlDictionaryString Replay; + public XmlDictionaryString CompletionCoordinatorPortType; + public XmlDictionaryString CompletionParticipantPortType; + public XmlDictionaryString CoordinatorPortType; + public XmlDictionaryString ParticipantPortType; + public XmlDictionaryString InconsistentInternalState; + + public AtomicTransactionExternalDictionary(ServiceModelDictionary dictionary) + { + Prefix = dictionary.CreateString(ServiceModelStringsVersion1.String383, 383); + Prepare = dictionary.CreateString(ServiceModelStringsVersion1.String387, 387); + Prepared = dictionary.CreateString(ServiceModelStringsVersion1.String388, 388); + ReadOnly = dictionary.CreateString(ServiceModelStringsVersion1.String389, 389); + Commit = dictionary.CreateString(ServiceModelStringsVersion1.String390, 390); + Rollback = dictionary.CreateString(ServiceModelStringsVersion1.String391, 391); + Committed = dictionary.CreateString(ServiceModelStringsVersion1.String392, 392); + Aborted = dictionary.CreateString(ServiceModelStringsVersion1.String393, 393); + Replay = dictionary.CreateString(ServiceModelStringsVersion1.String394, 394); + CompletionCoordinatorPortType = dictionary.CreateString(ServiceModelStringsVersion1.String404, 404); + CompletionParticipantPortType = dictionary.CreateString(ServiceModelStringsVersion1.String405, 405); + CoordinatorPortType = dictionary.CreateString(ServiceModelStringsVersion1.String406, 406); + ParticipantPortType = dictionary.CreateString(ServiceModelStringsVersion1.String407, 407); + InconsistentInternalState = dictionary.CreateString(ServiceModelStringsVersion1.String408, 408); + } + } + + class AtomicTransactionExternal10Dictionary + { + public XmlDictionaryString Namespace; + public XmlDictionaryString CompletionUri; + public XmlDictionaryString Durable2PCUri; + public XmlDictionaryString Volatile2PCUri; + public XmlDictionaryString CommitAction; + public XmlDictionaryString RollbackAction; + public XmlDictionaryString CommittedAction; + public XmlDictionaryString AbortedAction; + public XmlDictionaryString PrepareAction; + public XmlDictionaryString PreparedAction; + public XmlDictionaryString ReadOnlyAction; + public XmlDictionaryString ReplayAction; + public XmlDictionaryString FaultAction; + + public AtomicTransactionExternal10Dictionary(ServiceModelDictionary dictionary) + { + Namespace = dictionary.CreateString(ServiceModelStringsVersion1.String382, 382); + CompletionUri = dictionary.CreateString(ServiceModelStringsVersion1.String384, 384); + Durable2PCUri = dictionary.CreateString(ServiceModelStringsVersion1.String385, 385); + Volatile2PCUri = dictionary.CreateString(ServiceModelStringsVersion1.String386, 386); + CommitAction = dictionary.CreateString(ServiceModelStringsVersion1.String395, 395); + RollbackAction = dictionary.CreateString(ServiceModelStringsVersion1.String396, 396); + CommittedAction = dictionary.CreateString(ServiceModelStringsVersion1.String397, 397); + AbortedAction = dictionary.CreateString(ServiceModelStringsVersion1.String398, 398); + PrepareAction = dictionary.CreateString(ServiceModelStringsVersion1.String399, 399); + PreparedAction = dictionary.CreateString(ServiceModelStringsVersion1.String400, 400); + ReadOnlyAction = dictionary.CreateString(ServiceModelStringsVersion1.String401, 401); + ReplayAction = dictionary.CreateString(ServiceModelStringsVersion1.String402, 402); + FaultAction = dictionary.CreateString(ServiceModelStringsVersion1.String403, 403); + } + } + + class CoordinationExternalDictionary + { + public XmlDictionaryString Prefix; + public XmlDictionaryString CreateCoordinationContext; + public XmlDictionaryString CreateCoordinationContextResponse; + public XmlDictionaryString CoordinationContext; + public XmlDictionaryString CurrentContext; + public XmlDictionaryString CoordinationType; + public XmlDictionaryString RegistrationService; + public XmlDictionaryString Register; + public XmlDictionaryString RegisterResponse; + public XmlDictionaryString Protocol; + public XmlDictionaryString CoordinatorProtocolService; + public XmlDictionaryString ParticipantProtocolService; + public XmlDictionaryString Expires; + public XmlDictionaryString Identifier; + public XmlDictionaryString ActivationCoordinatorPortType; + public XmlDictionaryString RegistrationCoordinatorPortType; + public XmlDictionaryString InvalidState; + public XmlDictionaryString InvalidProtocol; + public XmlDictionaryString InvalidParameters; + public XmlDictionaryString NoActivity; + public XmlDictionaryString ContextRefused; + public XmlDictionaryString AlreadyRegistered; + + public CoordinationExternalDictionary(ServiceModelDictionary dictionary) + { + Prefix = dictionary.CreateString(ServiceModelStringsVersion1.String357, 357); + CreateCoordinationContext = dictionary.CreateString(ServiceModelStringsVersion1.String358, 358); + CreateCoordinationContextResponse = dictionary.CreateString(ServiceModelStringsVersion1.String359, 359); + CoordinationContext = dictionary.CreateString(ServiceModelStringsVersion1.String360, 360); + CurrentContext = dictionary.CreateString(ServiceModelStringsVersion1.String361, 361); + CoordinationType = dictionary.CreateString(ServiceModelStringsVersion1.String362, 362); + RegistrationService = dictionary.CreateString(ServiceModelStringsVersion1.String363, 363); + Register = dictionary.CreateString(ServiceModelStringsVersion1.String364, 364); + RegisterResponse = dictionary.CreateString(ServiceModelStringsVersion1.String365, 365); + Protocol = dictionary.CreateString(ServiceModelStringsVersion1.String366, 366); + CoordinatorProtocolService = dictionary.CreateString(ServiceModelStringsVersion1.String367, 367); + ParticipantProtocolService = dictionary.CreateString(ServiceModelStringsVersion1.String368, 368); + Expires = dictionary.CreateString(ServiceModelStringsVersion1.String55, 55); + Identifier = dictionary.CreateString(ServiceModelStringsVersion1.String15, 15); + ActivationCoordinatorPortType = dictionary.CreateString(ServiceModelStringsVersion1.String374, 374); + RegistrationCoordinatorPortType = dictionary.CreateString(ServiceModelStringsVersion1.String375, 375); + InvalidState = dictionary.CreateString(ServiceModelStringsVersion1.String376, 376); + InvalidProtocol = dictionary.CreateString(ServiceModelStringsVersion1.String377, 377); + InvalidParameters = dictionary.CreateString(ServiceModelStringsVersion1.String378, 378); + NoActivity = dictionary.CreateString(ServiceModelStringsVersion1.String379, 379); + ContextRefused = dictionary.CreateString(ServiceModelStringsVersion1.String380, 380); + AlreadyRegistered = dictionary.CreateString(ServiceModelStringsVersion1.String381, 381); + } + } + + class CoordinationExternal10Dictionary + { + public XmlDictionaryString Namespace; + public XmlDictionaryString CreateCoordinationContextAction; + public XmlDictionaryString CreateCoordinationContextResponseAction; + public XmlDictionaryString RegisterAction; + public XmlDictionaryString RegisterResponseAction; + public XmlDictionaryString FaultAction; + + public CoordinationExternal10Dictionary(ServiceModelDictionary dictionary) + { + Namespace = dictionary.CreateString(ServiceModelStringsVersion1.String356, 356); + CreateCoordinationContextAction = dictionary.CreateString(ServiceModelStringsVersion1.String369, 369); + CreateCoordinationContextResponseAction = dictionary.CreateString(ServiceModelStringsVersion1.String370, 370); + RegisterAction = dictionary.CreateString(ServiceModelStringsVersion1.String371, 371); + RegisterResponseAction = dictionary.CreateString(ServiceModelStringsVersion1.String372, 372); + FaultAction = dictionary.CreateString(ServiceModelStringsVersion1.String373, 373); + } + } + + class DotNetAddressingDictionary + { + public XmlDictionaryString Namespace; + public XmlDictionaryString RedirectTo; + public XmlDictionaryString Via; + + public DotNetAddressingDictionary(ServiceModelDictionary dictionary) + { + Namespace = dictionary.CreateString(ServiceModelStringsVersion1.String108, 108); + RedirectTo = dictionary.CreateString(ServiceModelStringsVersion1.String109, 109); + Via = dictionary.CreateString(ServiceModelStringsVersion1.String110, 110); + } + } + + class DotNetAtomicTransactionExternalDictionary + { + public XmlDictionaryString Namespace; + public XmlDictionaryString Prefix; + public XmlDictionaryString Enlistment; + public XmlDictionaryString Protocol; + public XmlDictionaryString LocalTransactionId; + public XmlDictionaryString IsolationLevel; + public XmlDictionaryString IsolationFlags; + public XmlDictionaryString Description; + public XmlDictionaryString Loopback; + public XmlDictionaryString RegisterInfo; + public XmlDictionaryString ContextId; + public XmlDictionaryString TokenId; + public XmlDictionaryString AccessDenied; + public XmlDictionaryString InvalidPolicy; + public XmlDictionaryString CoordinatorRegistrationFailed; + public XmlDictionaryString TooManyEnlistments; + public XmlDictionaryString Disabled; + + public DotNetAtomicTransactionExternalDictionary(ServiceModelDictionary dictionary) + { + Namespace = dictionary.CreateString(ServiceModelStringsVersion1.String65, 65); + Prefix = dictionary.CreateString(ServiceModelStringsVersion1.String409, 409); + Enlistment = dictionary.CreateString(ServiceModelStringsVersion1.String410, 410); + Protocol = dictionary.CreateString(ServiceModelStringsVersion1.String411, 411); + LocalTransactionId = dictionary.CreateString(ServiceModelStringsVersion1.String412, 412); + IsolationLevel = dictionary.CreateString(ServiceModelStringsVersion1.String413, 413); + IsolationFlags = dictionary.CreateString(ServiceModelStringsVersion1.String414, 414); + Description = dictionary.CreateString(ServiceModelStringsVersion1.String415, 415); + Loopback = dictionary.CreateString(ServiceModelStringsVersion1.String416, 416); + RegisterInfo = dictionary.CreateString(ServiceModelStringsVersion1.String417, 417); + ContextId = dictionary.CreateString(ServiceModelStringsVersion1.String418, 418); + TokenId = dictionary.CreateString(ServiceModelStringsVersion1.String419, 419); + AccessDenied = dictionary.CreateString(ServiceModelStringsVersion1.String420, 420); + InvalidPolicy = dictionary.CreateString(ServiceModelStringsVersion1.String421, 421); + CoordinatorRegistrationFailed = dictionary.CreateString(ServiceModelStringsVersion1.String422, 422); + TooManyEnlistments = dictionary.CreateString(ServiceModelStringsVersion1.String423, 423); + Disabled = dictionary.CreateString(ServiceModelStringsVersion1.String424, 424); + } + } + + class DotNetOneWayDictionary + { + public XmlDictionaryString Namespace; + public XmlDictionaryString HeaderName; + + public DotNetOneWayDictionary(ServiceModelDictionary dictionary) + { + Namespace = dictionary.CreateString(ServiceModelStringsVersion1.String437, 437); + HeaderName = dictionary.CreateString(ServiceModelStringsVersion1.String438, 438); + } + } + + class DotNetSecurityDictionary + { + public XmlDictionaryString Namespace; + public XmlDictionaryString Prefix; + + public DotNetSecurityDictionary(ServiceModelDictionary dictionary) + { + Namespace = dictionary.CreateString(ServiceModelStringsVersion1.String162, 162); + Prefix = dictionary.CreateString(ServiceModelStringsVersion1.String163, 163); + } + } + + class ExclusiveC14NDictionary + { + public XmlDictionaryString Namespace; + public XmlDictionaryString PrefixList; + public XmlDictionaryString InclusiveNamespaces; + public XmlDictionaryString Prefix; + + public ExclusiveC14NDictionary(ServiceModelDictionary dictionary) + { + Namespace = dictionary.CreateString(ServiceModelStringsVersion1.String111, 111); + PrefixList = dictionary.CreateString(ServiceModelStringsVersion1.String112, 112); + InclusiveNamespaces = dictionary.CreateString(ServiceModelStringsVersion1.String113, 113); + Prefix = dictionary.CreateString(ServiceModelStringsVersion1.String114, 114); + } + } + + class MessageDictionary + { + public XmlDictionaryString MustUnderstand; + public XmlDictionaryString Envelope; + public XmlDictionaryString Header; + public XmlDictionaryString Body; + public XmlDictionaryString Prefix; + public XmlDictionaryString Fault; + public XmlDictionaryString MustUnderstandFault; + public XmlDictionaryString Namespace; + + public MessageDictionary(ServiceModelDictionary dictionary) + { + MustUnderstand = dictionary.CreateString(ServiceModelStringsVersion1.String0, 0); + Envelope = dictionary.CreateString(ServiceModelStringsVersion1.String1, 1); + Header = dictionary.CreateString(ServiceModelStringsVersion1.String4, 4); + Body = dictionary.CreateString(ServiceModelStringsVersion1.String7, 7); + Prefix = dictionary.CreateString(ServiceModelStringsVersion1.String66, 66); + Fault = dictionary.CreateString(ServiceModelStringsVersion1.String67, 67); + MustUnderstandFault = dictionary.CreateString(ServiceModelStringsVersion1.String68, 68); + Namespace = dictionary.CreateString(ServiceModelStringsVersion1.String440, 440); + } + } + + class Message12Dictionary + { + public XmlDictionaryString Namespace; + public XmlDictionaryString Role; + public XmlDictionaryString Relay; + public XmlDictionaryString FaultCode; + public XmlDictionaryString FaultReason; + public XmlDictionaryString FaultText; + public XmlDictionaryString FaultNode; + public XmlDictionaryString FaultRole; + public XmlDictionaryString FaultDetail; + public XmlDictionaryString FaultValue; + public XmlDictionaryString FaultSubcode; + public XmlDictionaryString NotUnderstood; + public XmlDictionaryString QName; + + public Message12Dictionary(ServiceModelDictionary dictionary) + { + Namespace = dictionary.CreateString(ServiceModelStringsVersion1.String2, 2); + Role = dictionary.CreateString(ServiceModelStringsVersion1.String69, 69); + Relay = dictionary.CreateString(ServiceModelStringsVersion1.String70, 70); + FaultCode = dictionary.CreateString(ServiceModelStringsVersion1.String71, 71); + FaultReason = dictionary.CreateString(ServiceModelStringsVersion1.String72, 72); + FaultText = dictionary.CreateString(ServiceModelStringsVersion1.String73, 73); + FaultNode = dictionary.CreateString(ServiceModelStringsVersion1.String74, 74); + FaultRole = dictionary.CreateString(ServiceModelStringsVersion1.String75, 75); + FaultDetail = dictionary.CreateString(ServiceModelStringsVersion1.String76, 76); + FaultValue = dictionary.CreateString(ServiceModelStringsVersion1.String77, 77); + FaultSubcode = dictionary.CreateString(ServiceModelStringsVersion1.String78, 78); + NotUnderstood = dictionary.CreateString(ServiceModelStringsVersion1.String79, 79); + QName = dictionary.CreateString(ServiceModelStringsVersion1.String80, 80); + } + } + + class OleTxTransactionExternalDictionary + { + public XmlDictionaryString Namespace; + public XmlDictionaryString Prefix; + public XmlDictionaryString OleTxTransaction; + public XmlDictionaryString PropagationToken; + + public OleTxTransactionExternalDictionary(ServiceModelDictionary dictionary) + { + Namespace = dictionary.CreateString(ServiceModelStringsVersion1.String352, 352); + Prefix = dictionary.CreateString(ServiceModelStringsVersion1.String353, 353); + OleTxTransaction = dictionary.CreateString(ServiceModelStringsVersion1.String354, 354); + PropagationToken = dictionary.CreateString(ServiceModelStringsVersion1.String355, 355); + } + } + + class PeerWireStringsDictionary + { + public XmlDictionaryString FloodAction; + public XmlDictionaryString LinkUtilityAction; + public XmlDictionaryString HopCount; + public XmlDictionaryString HopCountNamespace; + public XmlDictionaryString PeerVia; + public XmlDictionaryString Namespace; + public XmlDictionaryString Demuxer; + public XmlDictionaryString PeerTo; + + public PeerWireStringsDictionary(ServiceModelDictionary dictionary) + { + FloodAction = dictionary.CreateString(ServiceModelStringsVersion1.String429, 429); + LinkUtilityAction = dictionary.CreateString(ServiceModelStringsVersion1.String430, 430); + HopCount = dictionary.CreateString(ServiceModelStringsVersion1.String431, 431); + HopCountNamespace = dictionary.CreateString(ServiceModelStringsVersion1.String432, 432); + PeerVia = dictionary.CreateString(ServiceModelStringsVersion1.String433, 433); + Namespace = dictionary.CreateString(ServiceModelStringsVersion1.String434, 434); + Demuxer = dictionary.CreateString(ServiceModelStringsVersion1.String435, 435); + PeerTo = dictionary.CreateString(ServiceModelStringsVersion1.String436, 436); + } + } + + class PolicyDictionary + { + public XmlDictionaryString Namespace; + + public PolicyDictionary(ServiceModelDictionary dictionary) + { + Namespace = dictionary.CreateString(ServiceModelStringsVersion1.String428, 428); + } + } + + class SamlDictionary + { + public XmlDictionaryString Access; + public XmlDictionaryString AccessDecision; + public XmlDictionaryString Action; + public XmlDictionaryString Advice; + public XmlDictionaryString Assertion; + public XmlDictionaryString AssertionId; + public XmlDictionaryString AssertionIdReference; + public XmlDictionaryString Attribute; + public XmlDictionaryString AttributeName; + public XmlDictionaryString AttributeNamespace; + public XmlDictionaryString AttributeStatement; + public XmlDictionaryString AttributeValue; + public XmlDictionaryString Audience; + public XmlDictionaryString AudienceRestrictionCondition; + public XmlDictionaryString AuthenticationInstant; + public XmlDictionaryString AuthenticationMethod; + public XmlDictionaryString AuthenticationStatement; + public XmlDictionaryString AuthorityBinding; + public XmlDictionaryString AuthorityKind; + public XmlDictionaryString AuthorizationDecisionStatement; + public XmlDictionaryString Binding; + public XmlDictionaryString Condition; + public XmlDictionaryString Conditions; + public XmlDictionaryString Decision; + public XmlDictionaryString DoNotCacheCondition; + public XmlDictionaryString Evidence; + public XmlDictionaryString IssueInstant; + public XmlDictionaryString Issuer; + public XmlDictionaryString Location; + public XmlDictionaryString MajorVersion; + public XmlDictionaryString MinorVersion; + public XmlDictionaryString Namespace; + public XmlDictionaryString NameIdentifier; + public XmlDictionaryString NameIdentifierFormat; + public XmlDictionaryString NameIdentifierNameQualifier; + public XmlDictionaryString ActionNamespaceAttribute; + public XmlDictionaryString NotBefore; + public XmlDictionaryString NotOnOrAfter; + public XmlDictionaryString PreferredPrefix; + public XmlDictionaryString Statement; + public XmlDictionaryString Subject; + public XmlDictionaryString SubjectConfirmation; + public XmlDictionaryString SubjectConfirmationData; + public XmlDictionaryString SubjectConfirmationMethod; + public XmlDictionaryString HolderOfKey; + public XmlDictionaryString SenderVouches; + public XmlDictionaryString SubjectLocality; + public XmlDictionaryString SubjectLocalityDNSAddress; + public XmlDictionaryString SubjectLocalityIPAddress; + public XmlDictionaryString SubjectStatement; + public XmlDictionaryString UnspecifiedAuthenticationMethod; + public XmlDictionaryString NamespaceAttributePrefix; + public XmlDictionaryString Resource; + public XmlDictionaryString UserName; + public XmlDictionaryString UserNameNamespace; + public XmlDictionaryString EmailName; + public XmlDictionaryString EmailNamespace; + + public SamlDictionary(ServiceModelDictionary dictionary) + { + Access = dictionary.CreateString(ServiceModelStringsVersion1.String251, 251); + AccessDecision = dictionary.CreateString(ServiceModelStringsVersion1.String252, 252); + Action = dictionary.CreateString(ServiceModelStringsVersion1.String5, 5); + Advice = dictionary.CreateString(ServiceModelStringsVersion1.String253, 253); + Assertion = dictionary.CreateString(ServiceModelStringsVersion1.String179, 179); + AssertionId = dictionary.CreateString(ServiceModelStringsVersion1.String254, 254); + AssertionIdReference = dictionary.CreateString(ServiceModelStringsVersion1.String255, 255); + Attribute = dictionary.CreateString(ServiceModelStringsVersion1.String256, 256); + AttributeName = dictionary.CreateString(ServiceModelStringsVersion1.String257, 257); + AttributeNamespace = dictionary.CreateString(ServiceModelStringsVersion1.String258, 258); + AttributeStatement = dictionary.CreateString(ServiceModelStringsVersion1.String259, 259); + AttributeValue = dictionary.CreateString(ServiceModelStringsVersion1.String260, 260); + Audience = dictionary.CreateString(ServiceModelStringsVersion1.String261, 261); + AudienceRestrictionCondition = dictionary.CreateString(ServiceModelStringsVersion1.String262, 262); + AuthenticationInstant = dictionary.CreateString(ServiceModelStringsVersion1.String263, 263); + AuthenticationMethod = dictionary.CreateString(ServiceModelStringsVersion1.String264, 264); + AuthenticationStatement = dictionary.CreateString(ServiceModelStringsVersion1.String265, 265); + AuthorityBinding = dictionary.CreateString(ServiceModelStringsVersion1.String266, 266); + AuthorityKind = dictionary.CreateString(ServiceModelStringsVersion1.String267, 267); + AuthorizationDecisionStatement = dictionary.CreateString(ServiceModelStringsVersion1.String268, 268); + Binding = dictionary.CreateString(ServiceModelStringsVersion1.String269, 269); + Condition = dictionary.CreateString(ServiceModelStringsVersion1.String270, 270); + Conditions = dictionary.CreateString(ServiceModelStringsVersion1.String271, 271); + Decision = dictionary.CreateString(ServiceModelStringsVersion1.String272, 272); + DoNotCacheCondition = dictionary.CreateString(ServiceModelStringsVersion1.String273, 273); + Evidence = dictionary.CreateString(ServiceModelStringsVersion1.String274, 274); + IssueInstant = dictionary.CreateString(ServiceModelStringsVersion1.String275, 275); + Issuer = dictionary.CreateString(ServiceModelStringsVersion1.String276, 276); + Location = dictionary.CreateString(ServiceModelStringsVersion1.String277, 277); + MajorVersion = dictionary.CreateString(ServiceModelStringsVersion1.String278, 278); + MinorVersion = dictionary.CreateString(ServiceModelStringsVersion1.String279, 279); + Namespace = dictionary.CreateString(ServiceModelStringsVersion1.String180, 180); + NameIdentifier = dictionary.CreateString(ServiceModelStringsVersion1.String280, 280); + NameIdentifierFormat = dictionary.CreateString(ServiceModelStringsVersion1.String281, 281); + NameIdentifierNameQualifier = dictionary.CreateString(ServiceModelStringsVersion1.String282, 282); + ActionNamespaceAttribute = dictionary.CreateString(ServiceModelStringsVersion1.String283, 283); + NotBefore = dictionary.CreateString(ServiceModelStringsVersion1.String284, 284); + NotOnOrAfter = dictionary.CreateString(ServiceModelStringsVersion1.String285, 285); + PreferredPrefix = dictionary.CreateString(ServiceModelStringsVersion1.String286, 286); + Statement = dictionary.CreateString(ServiceModelStringsVersion1.String287, 287); + Subject = dictionary.CreateString(ServiceModelStringsVersion1.String288, 288); + SubjectConfirmation = dictionary.CreateString(ServiceModelStringsVersion1.String289, 289); + SubjectConfirmationData = dictionary.CreateString(ServiceModelStringsVersion1.String290, 290); + SubjectConfirmationMethod = dictionary.CreateString(ServiceModelStringsVersion1.String291, 291); + HolderOfKey = dictionary.CreateString(ServiceModelStringsVersion1.String292, 292); + SenderVouches = dictionary.CreateString(ServiceModelStringsVersion1.String293, 293); + SubjectLocality = dictionary.CreateString(ServiceModelStringsVersion1.String294, 294); + SubjectLocalityDNSAddress = dictionary.CreateString(ServiceModelStringsVersion1.String295, 295); + SubjectLocalityIPAddress = dictionary.CreateString(ServiceModelStringsVersion1.String296, 296); + SubjectStatement = dictionary.CreateString(ServiceModelStringsVersion1.String297, 297); + UnspecifiedAuthenticationMethod = dictionary.CreateString(ServiceModelStringsVersion1.String298, 298); + NamespaceAttributePrefix = dictionary.CreateString(ServiceModelStringsVersion1.String299, 299); + Resource = dictionary.CreateString(ServiceModelStringsVersion1.String300, 300); + UserName = dictionary.CreateString(ServiceModelStringsVersion1.String301, 301); + UserNameNamespace = dictionary.CreateString(ServiceModelStringsVersion1.String302, 302); + EmailName = dictionary.CreateString(ServiceModelStringsVersion1.String303, 303); + EmailNamespace = dictionary.CreateString(ServiceModelStringsVersion1.String304, 304); + } + } + + class SecureConversationDictionary + { + public XmlDictionaryString Namespace; + public XmlDictionaryString DerivedKeyToken; + public XmlDictionaryString Nonce; + public XmlDictionaryString Length; + public XmlDictionaryString SecurityContextToken; + public XmlDictionaryString AlgorithmAttribute; + public XmlDictionaryString Generation; + public XmlDictionaryString Label; + public XmlDictionaryString Offset; + public XmlDictionaryString Properties; + public XmlDictionaryString Identifier; + public XmlDictionaryString Cookie; + public XmlDictionaryString Prefix; + public XmlDictionaryString DerivedKeyTokenType; + public XmlDictionaryString SecurityContextTokenType; + public XmlDictionaryString SecurityContextTokenReferenceValueType; + public XmlDictionaryString RequestSecurityContextIssuance; + public XmlDictionaryString RequestSecurityContextIssuanceResponse; + public XmlDictionaryString RenewNeededFaultCode; + public XmlDictionaryString BadContextTokenFaultCode; + + public SecureConversationDictionary() + { + } + + public SecureConversationDictionary(ServiceModelDictionary dictionary) + { + } + } + + class SecureConversationApr2004Dictionary : SecureConversationDictionary + { + + public SecureConversationApr2004Dictionary(ServiceModelDictionary dictionary) + : base(dictionary) + { + SecurityContextToken = dictionary.CreateString(ServiceModelStringsVersion1.String115, 115); + DerivedKeyToken = dictionary.CreateString(ServiceModelStringsVersion1.String39, 39); + AlgorithmAttribute = dictionary.CreateString(ServiceModelStringsVersion1.String8, 8); + Generation = dictionary.CreateString(ServiceModelStringsVersion1.String116, 116); + Label = dictionary.CreateString(ServiceModelStringsVersion1.String117, 117); + Length = dictionary.CreateString(ServiceModelStringsVersion1.String56, 56); + Nonce = dictionary.CreateString(ServiceModelStringsVersion1.String40, 40); + Offset = dictionary.CreateString(ServiceModelStringsVersion1.String118, 118); + Properties = dictionary.CreateString(ServiceModelStringsVersion1.String119, 119); + Identifier = dictionary.CreateString(ServiceModelStringsVersion1.String15, 15); + Cookie = dictionary.CreateString(ServiceModelStringsVersion1.String120, 120); + Prefix = dictionary.CreateString(ServiceModelStringsVersion1.String121, 121); + Namespace = dictionary.CreateString(ServiceModelStringsVersion1.String122, 122); + DerivedKeyTokenType = dictionary.CreateString(ServiceModelStringsVersion1.String123, 123); + SecurityContextTokenType = dictionary.CreateString(ServiceModelStringsVersion1.String124, 124); + SecurityContextTokenReferenceValueType = dictionary.CreateString(ServiceModelStringsVersion1.String124, 124); + RequestSecurityContextIssuance = dictionary.CreateString(ServiceModelStringsVersion1.String125, 125); + RequestSecurityContextIssuanceResponse = dictionary.CreateString(ServiceModelStringsVersion1.String126, 126); + RenewNeededFaultCode = dictionary.CreateString(ServiceModelStringsVersion1.String127, 127); + BadContextTokenFaultCode = dictionary.CreateString(ServiceModelStringsVersion1.String128, 128); + } + } + + class SecureConversationFeb2005Dictionary : SecureConversationDictionary + { + public XmlDictionaryString RequestSecurityContextRenew; + public XmlDictionaryString RequestSecurityContextRenewResponse; + public XmlDictionaryString RequestSecurityContextClose; + public XmlDictionaryString RequestSecurityContextCloseResponse; + + public SecureConversationFeb2005Dictionary(ServiceModelDictionary dictionary) + : base(dictionary) + { + Namespace = dictionary.CreateString(ServiceModelStringsVersion1.String38, 38); + DerivedKeyToken = dictionary.CreateString(ServiceModelStringsVersion1.String39, 39); + Nonce = dictionary.CreateString(ServiceModelStringsVersion1.String40, 40); + Length = dictionary.CreateString(ServiceModelStringsVersion1.String56, 56); + SecurityContextToken = dictionary.CreateString(ServiceModelStringsVersion1.String115, 115); + AlgorithmAttribute = dictionary.CreateString(ServiceModelStringsVersion1.String8, 8); + Generation = dictionary.CreateString(ServiceModelStringsVersion1.String116, 116); + Label = dictionary.CreateString(ServiceModelStringsVersion1.String117, 117); + Offset = dictionary.CreateString(ServiceModelStringsVersion1.String118, 118); + Properties = dictionary.CreateString(ServiceModelStringsVersion1.String119, 119); + Identifier = dictionary.CreateString(ServiceModelStringsVersion1.String15, 15); + Cookie = dictionary.CreateString(ServiceModelStringsVersion1.String120, 120); + RenewNeededFaultCode = dictionary.CreateString(ServiceModelStringsVersion1.String127, 127); + BadContextTokenFaultCode = dictionary.CreateString(ServiceModelStringsVersion1.String128, 128); + Prefix = dictionary.CreateString(ServiceModelStringsVersion1.String129, 129); + DerivedKeyTokenType = dictionary.CreateString(ServiceModelStringsVersion1.String130, 130); + SecurityContextTokenType = dictionary.CreateString(ServiceModelStringsVersion1.String131, 131); + SecurityContextTokenReferenceValueType = dictionary.CreateString(ServiceModelStringsVersion1.String131, 131); + RequestSecurityContextIssuance = dictionary.CreateString(ServiceModelStringsVersion1.String132, 132); + RequestSecurityContextIssuanceResponse = dictionary.CreateString(ServiceModelStringsVersion1.String133, 133); + RequestSecurityContextRenew = dictionary.CreateString(ServiceModelStringsVersion1.String134, 134); + RequestSecurityContextRenewResponse = dictionary.CreateString(ServiceModelStringsVersion1.String135, 135); + RequestSecurityContextClose = dictionary.CreateString(ServiceModelStringsVersion1.String136, 136); + RequestSecurityContextCloseResponse = dictionary.CreateString(ServiceModelStringsVersion1.String137, 137); + } + } + + class SecurityAlgorithmDictionary + { + public XmlDictionaryString Aes128Encryption; + public XmlDictionaryString Aes128KeyWrap; + public XmlDictionaryString Aes192Encryption; + public XmlDictionaryString Aes192KeyWrap; + public XmlDictionaryString Aes256Encryption; + public XmlDictionaryString Aes256KeyWrap; + public XmlDictionaryString DesEncryption; + public XmlDictionaryString DsaSha1Signature; + public XmlDictionaryString ExclusiveC14n; + public XmlDictionaryString ExclusiveC14nWithComments; + public XmlDictionaryString HmacSha1Signature; + public XmlDictionaryString HmacSha256Signature; + public XmlDictionaryString Psha1KeyDerivation; + public XmlDictionaryString Ripemd160Digest; + public XmlDictionaryString RsaOaepKeyWrap; + public XmlDictionaryString RsaSha1Signature; + public XmlDictionaryString RsaSha256Signature; + public XmlDictionaryString RsaV15KeyWrap; + public XmlDictionaryString Sha1Digest; + public XmlDictionaryString Sha256Digest; + public XmlDictionaryString Sha512Digest; + public XmlDictionaryString TripleDesEncryption; + public XmlDictionaryString TripleDesKeyWrap; + public XmlDictionaryString TlsSspiKeyWrap; + public XmlDictionaryString WindowsSspiKeyWrap; + + public SecurityAlgorithmDictionary(ServiceModelDictionary dictionary) + { + Aes128Encryption = dictionary.CreateString(ServiceModelStringsVersion1.String138, 138); + Aes128KeyWrap = dictionary.CreateString(ServiceModelStringsVersion1.String139, 139); + Aes192Encryption = dictionary.CreateString(ServiceModelStringsVersion1.String140, 140); + Aes192KeyWrap = dictionary.CreateString(ServiceModelStringsVersion1.String141, 141); + Aes256Encryption = dictionary.CreateString(ServiceModelStringsVersion1.String142, 142); + Aes256KeyWrap = dictionary.CreateString(ServiceModelStringsVersion1.String143, 143); + DesEncryption = dictionary.CreateString(ServiceModelStringsVersion1.String144, 144); + DsaSha1Signature = dictionary.CreateString(ServiceModelStringsVersion1.String145, 145); + ExclusiveC14n = dictionary.CreateString(ServiceModelStringsVersion1.String111, 111); + ExclusiveC14nWithComments = dictionary.CreateString(ServiceModelStringsVersion1.String146, 146); + HmacSha1Signature = dictionary.CreateString(ServiceModelStringsVersion1.String147, 147); + HmacSha256Signature = dictionary.CreateString(ServiceModelStringsVersion1.String148, 148); + Psha1KeyDerivation = dictionary.CreateString(ServiceModelStringsVersion1.String149, 149); + Ripemd160Digest = dictionary.CreateString(ServiceModelStringsVersion1.String150, 150); + RsaOaepKeyWrap = dictionary.CreateString(ServiceModelStringsVersion1.String151, 151); + RsaSha1Signature = dictionary.CreateString(ServiceModelStringsVersion1.String152, 152); + RsaSha256Signature = dictionary.CreateString(ServiceModelStringsVersion1.String153, 153); + RsaV15KeyWrap = dictionary.CreateString(ServiceModelStringsVersion1.String154, 154); + Sha1Digest = dictionary.CreateString(ServiceModelStringsVersion1.String155, 155); + Sha256Digest = dictionary.CreateString(ServiceModelStringsVersion1.String156, 156); + Sha512Digest = dictionary.CreateString(ServiceModelStringsVersion1.String157, 157); + TripleDesEncryption = dictionary.CreateString(ServiceModelStringsVersion1.String158, 158); + TripleDesKeyWrap = dictionary.CreateString(ServiceModelStringsVersion1.String159, 159); + TlsSspiKeyWrap = dictionary.CreateString(ServiceModelStringsVersion1.String160, 160); + WindowsSspiKeyWrap = dictionary.CreateString(ServiceModelStringsVersion1.String161, 161); + } + } + + class SecurityJan2004Dictionary + { + public XmlDictionaryString SecurityTokenReference; + public XmlDictionaryString Namespace; + public XmlDictionaryString Security; + public XmlDictionaryString ValueType; + public XmlDictionaryString TypeAttribute; + public XmlDictionaryString Prefix; + public XmlDictionaryString NonceElement; + public XmlDictionaryString PasswordElement; + public XmlDictionaryString PasswordTextName; + public XmlDictionaryString UserNameElement; + public XmlDictionaryString UserNameTokenElement; + public XmlDictionaryString BinarySecurityToken; + public XmlDictionaryString EncodingType; + public XmlDictionaryString Reference; + public XmlDictionaryString URI; + public XmlDictionaryString KeyIdentifier; + public XmlDictionaryString EncodingTypeValueBase64Binary; + public XmlDictionaryString EncodingTypeValueHexBinary; + public XmlDictionaryString EncodingTypeValueText; + public XmlDictionaryString X509SKIValueType; + public XmlDictionaryString KerberosTokenTypeGSS; + public XmlDictionaryString KerberosTokenType1510; + public XmlDictionaryString SamlAssertionIdValueType; + public XmlDictionaryString SamlAssertion; + public XmlDictionaryString SamlUri; + public XmlDictionaryString RelAssertionValueType; + public XmlDictionaryString FailedAuthenticationFaultCode; + public XmlDictionaryString InvalidSecurityTokenFaultCode; + public XmlDictionaryString InvalidSecurityFaultCode; + public XmlDictionaryString KerberosHashValueType; + + public SecurityJan2004Dictionary(ServiceModelDictionary dictionary) + { + SecurityTokenReference = dictionary.CreateString(ServiceModelStringsVersion1.String30, 30); + Namespace = dictionary.CreateString(ServiceModelStringsVersion1.String36, 36); + Security = dictionary.CreateString(ServiceModelStringsVersion1.String52, 52); + ValueType = dictionary.CreateString(ServiceModelStringsVersion1.String58, 58); + TypeAttribute = dictionary.CreateString(ServiceModelStringsVersion1.String59, 59); + Prefix = dictionary.CreateString(ServiceModelStringsVersion1.String164, 164); + NonceElement = dictionary.CreateString(ServiceModelStringsVersion1.String40, 40); + PasswordElement = dictionary.CreateString(ServiceModelStringsVersion1.String165, 165); + PasswordTextName = dictionary.CreateString(ServiceModelStringsVersion1.String166, 166); + UserNameElement = dictionary.CreateString(ServiceModelStringsVersion1.String167, 167); + UserNameTokenElement = dictionary.CreateString(ServiceModelStringsVersion1.String168, 168); + BinarySecurityToken = dictionary.CreateString(ServiceModelStringsVersion1.String169, 169); + EncodingType = dictionary.CreateString(ServiceModelStringsVersion1.String170, 170); + Reference = dictionary.CreateString(ServiceModelStringsVersion1.String12, 12); + URI = dictionary.CreateString(ServiceModelStringsVersion1.String11, 11); + KeyIdentifier = dictionary.CreateString(ServiceModelStringsVersion1.String171, 171); + EncodingTypeValueBase64Binary = dictionary.CreateString(ServiceModelStringsVersion1.String172, 172); + EncodingTypeValueHexBinary = dictionary.CreateString(ServiceModelStringsVersion1.String173, 173); + EncodingTypeValueText = dictionary.CreateString(ServiceModelStringsVersion1.String174, 174); + X509SKIValueType = dictionary.CreateString(ServiceModelStringsVersion1.String175, 175); + KerberosTokenTypeGSS = dictionary.CreateString(ServiceModelStringsVersion1.String176, 176); + KerberosTokenType1510 = dictionary.CreateString(ServiceModelStringsVersion1.String177, 177); + SamlAssertionIdValueType = dictionary.CreateString(ServiceModelStringsVersion1.String178, 178); + SamlAssertion = dictionary.CreateString(ServiceModelStringsVersion1.String179, 179); + SamlUri = dictionary.CreateString(ServiceModelStringsVersion1.String180, 180); + RelAssertionValueType = dictionary.CreateString(ServiceModelStringsVersion1.String181, 181); + FailedAuthenticationFaultCode = dictionary.CreateString(ServiceModelStringsVersion1.String182, 182); + InvalidSecurityTokenFaultCode = dictionary.CreateString(ServiceModelStringsVersion1.String183, 183); + InvalidSecurityFaultCode = dictionary.CreateString(ServiceModelStringsVersion1.String184, 184); + KerberosHashValueType = dictionary.CreateString(ServiceModelStringsVersion1.String427, 427); + } + } + + class SecurityXXX2005Dictionary + { + public XmlDictionaryString EncryptedHeader; + public XmlDictionaryString Namespace; + public XmlDictionaryString Prefix; + public XmlDictionaryString SignatureConfirmation; + public XmlDictionaryString ValueAttribute; + public XmlDictionaryString TokenTypeAttribute; + public XmlDictionaryString ThumbprintSha1ValueType; + public XmlDictionaryString EncryptedKeyTokenType; + public XmlDictionaryString EncryptedKeyHashValueType; + public XmlDictionaryString SamlTokenType; + public XmlDictionaryString Saml20TokenType; + public XmlDictionaryString Saml11AssertionValueType; + + public SecurityXXX2005Dictionary(ServiceModelDictionary dictionary) + { + EncryptedHeader = dictionary.CreateString(ServiceModelStringsVersion1.String60, 60); + Namespace = dictionary.CreateString(ServiceModelStringsVersion1.String61, 61); + Prefix = dictionary.CreateString(ServiceModelStringsVersion1.String185, 185); + SignatureConfirmation = dictionary.CreateString(ServiceModelStringsVersion1.String186, 186); + ValueAttribute = dictionary.CreateString(ServiceModelStringsVersion1.String77, 77); + TokenTypeAttribute = dictionary.CreateString(ServiceModelStringsVersion1.String187, 187); + ThumbprintSha1ValueType = dictionary.CreateString(ServiceModelStringsVersion1.String188, 188); + EncryptedKeyTokenType = dictionary.CreateString(ServiceModelStringsVersion1.String189, 189); + EncryptedKeyHashValueType = dictionary.CreateString(ServiceModelStringsVersion1.String190, 190); + SamlTokenType = dictionary.CreateString(ServiceModelStringsVersion1.String191, 191); + Saml20TokenType = dictionary.CreateString(ServiceModelStringsVersion1.String192, 192); + Saml11AssertionValueType = dictionary.CreateString(ServiceModelStringsVersion1.String193, 193); + } + } + + class SerializationDictionary + { + public XmlDictionaryString XmlSchemaInstanceNamespace; + public XmlDictionaryString XmlSchemaNamespace; + public XmlDictionaryString Nil; + public XmlDictionaryString Type; + public XmlDictionaryString Char; + public XmlDictionaryString Boolean; + public XmlDictionaryString Byte; + public XmlDictionaryString UnsignedByte; + public XmlDictionaryString Short; + public XmlDictionaryString UnsignedShort; + public XmlDictionaryString Int; + public XmlDictionaryString UnsignedInt; + public XmlDictionaryString Long; + public XmlDictionaryString UnsignedLong; + public XmlDictionaryString Float; + public XmlDictionaryString Double; + public XmlDictionaryString Decimal; + public XmlDictionaryString DateTime; + public XmlDictionaryString String; + public XmlDictionaryString Base64Binary; + public XmlDictionaryString AnyType; + public XmlDictionaryString Duration; + public XmlDictionaryString Guid; + public XmlDictionaryString AnyURI; + public XmlDictionaryString QName; + public XmlDictionaryString Time; + public XmlDictionaryString Date; + public XmlDictionaryString HexBinary; + public XmlDictionaryString GYearMonth; + public XmlDictionaryString GYear; + public XmlDictionaryString GMonthDay; + public XmlDictionaryString GDay; + public XmlDictionaryString GMonth; + public XmlDictionaryString Integer; + public XmlDictionaryString PositiveInteger; + public XmlDictionaryString NegativeInteger; + public XmlDictionaryString NonPositiveInteger; + public XmlDictionaryString NonNegativeInteger; + public XmlDictionaryString NormalizedString; + + public SerializationDictionary(ServiceModelDictionary dictionary) + { + XmlSchemaInstanceNamespace = dictionary.CreateString(ServiceModelStringsVersion1.String441, 441); + XmlSchemaNamespace = dictionary.CreateString(ServiceModelStringsVersion1.String442, 442); + Nil = dictionary.CreateString(ServiceModelStringsVersion1.String443, 443); + Type = dictionary.CreateString(ServiceModelStringsVersion1.String444, 444); + Char = dictionary.CreateString(ServiceModelStringsVersion1.String445, 445); + Boolean = dictionary.CreateString(ServiceModelStringsVersion1.String446, 446); + Byte = dictionary.CreateString(ServiceModelStringsVersion1.String447, 447); + UnsignedByte = dictionary.CreateString(ServiceModelStringsVersion1.String448, 448); + Short = dictionary.CreateString(ServiceModelStringsVersion1.String449, 449); + UnsignedShort = dictionary.CreateString(ServiceModelStringsVersion1.String450, 450); + Int = dictionary.CreateString(ServiceModelStringsVersion1.String451, 451); + UnsignedInt = dictionary.CreateString(ServiceModelStringsVersion1.String452, 452); + Long = dictionary.CreateString(ServiceModelStringsVersion1.String453, 453); + UnsignedLong = dictionary.CreateString(ServiceModelStringsVersion1.String454, 454); + Float = dictionary.CreateString(ServiceModelStringsVersion1.String455, 455); + Double = dictionary.CreateString(ServiceModelStringsVersion1.String456, 456); + Decimal = dictionary.CreateString(ServiceModelStringsVersion1.String457, 457); + DateTime = dictionary.CreateString(ServiceModelStringsVersion1.String458, 458); + String = dictionary.CreateString(ServiceModelStringsVersion1.String459, 459); + Base64Binary = dictionary.CreateString(ServiceModelStringsVersion1.String460, 460); + AnyType = dictionary.CreateString(ServiceModelStringsVersion1.String461, 461); + Duration = dictionary.CreateString(ServiceModelStringsVersion1.String462, 462); + Guid = dictionary.CreateString(ServiceModelStringsVersion1.String463, 463); + AnyURI = dictionary.CreateString(ServiceModelStringsVersion1.String464, 464); + QName = dictionary.CreateString(ServiceModelStringsVersion1.String465, 465); + Time = dictionary.CreateString(ServiceModelStringsVersion1.String466, 466); + Date = dictionary.CreateString(ServiceModelStringsVersion1.String467, 467); + HexBinary = dictionary.CreateString(ServiceModelStringsVersion1.String468, 468); + GYearMonth = dictionary.CreateString(ServiceModelStringsVersion1.String469, 469); + GYear = dictionary.CreateString(ServiceModelStringsVersion1.String470, 470); + GMonthDay = dictionary.CreateString(ServiceModelStringsVersion1.String471, 471); + GDay = dictionary.CreateString(ServiceModelStringsVersion1.String472, 472); + GMonth = dictionary.CreateString(ServiceModelStringsVersion1.String473, 473); + Integer = dictionary.CreateString(ServiceModelStringsVersion1.String474, 474); + PositiveInteger = dictionary.CreateString(ServiceModelStringsVersion1.String475, 475); + NegativeInteger = dictionary.CreateString(ServiceModelStringsVersion1.String476, 476); + NonPositiveInteger = dictionary.CreateString(ServiceModelStringsVersion1.String477, 477); + NonNegativeInteger = dictionary.CreateString(ServiceModelStringsVersion1.String478, 478); + NormalizedString = dictionary.CreateString(ServiceModelStringsVersion1.String479, 479); + } + } + + class TrustDictionary + { + public XmlDictionaryString RequestSecurityTokenResponseCollection; + public XmlDictionaryString Namespace; + public XmlDictionaryString BinarySecretClauseType; + public XmlDictionaryString CombinedHashLabel; + public XmlDictionaryString RequestSecurityTokenResponse; + public XmlDictionaryString TokenType; + public XmlDictionaryString KeySize; + public XmlDictionaryString RequestedTokenReference; + public XmlDictionaryString AppliesTo; + public XmlDictionaryString Authenticator; + public XmlDictionaryString CombinedHash; + public XmlDictionaryString BinaryExchange; + public XmlDictionaryString Lifetime; + public XmlDictionaryString RequestedSecurityToken; + public XmlDictionaryString Entropy; + public XmlDictionaryString RequestedProofToken; + public XmlDictionaryString ComputedKey; + public XmlDictionaryString RequestSecurityToken; + public XmlDictionaryString RequestType; + public XmlDictionaryString Context; + public XmlDictionaryString BinarySecret; + public XmlDictionaryString Type; + public XmlDictionaryString SpnegoValueTypeUri; + public XmlDictionaryString TlsnegoValueTypeUri; + public XmlDictionaryString Prefix; + public XmlDictionaryString RequestSecurityTokenIssuance; + public XmlDictionaryString RequestSecurityTokenIssuanceResponse; + public XmlDictionaryString RequestTypeIssue; + public XmlDictionaryString Psha1ComputedKeyUri; + public XmlDictionaryString SymmetricKeyBinarySecret; + public XmlDictionaryString NonceBinarySecret; + public XmlDictionaryString KeyType; + public XmlDictionaryString SymmetricKeyType; + public XmlDictionaryString PublicKeyType; + public XmlDictionaryString Claims; + public XmlDictionaryString InvalidRequestFaultCode; + public XmlDictionaryString FailedAuthenticationFaultCode; + public XmlDictionaryString RequestFailedFaultCode; + public XmlDictionaryString SignWith; + public XmlDictionaryString EncryptWith; + public XmlDictionaryString EncryptionAlgorithm; + public XmlDictionaryString CanonicalizationAlgorithm; + public XmlDictionaryString ComputedKeyAlgorithm; + public XmlDictionaryString UseKey; + public XmlDictionaryString RenewTarget; + public XmlDictionaryString CloseTarget; + public XmlDictionaryString RequestedTokenClosed; + public XmlDictionaryString RequestedAttachedReference; + public XmlDictionaryString RequestedUnattachedReference; + public XmlDictionaryString IssuedTokensHeader; + public XmlDictionaryString RequestTypeRenew; + public XmlDictionaryString RequestTypeClose; + + public TrustDictionary() + { + } + + public TrustDictionary(ServiceModelDictionary dictionary) + { + } + } + + class TrustApr2004Dictionary : TrustDictionary + { + + public TrustApr2004Dictionary(ServiceModelDictionary dictionary) + : base(dictionary) + { + CombinedHashLabel = dictionary.CreateString(ServiceModelStringsVersion1.String194, 194); + RequestSecurityTokenResponse = dictionary.CreateString(ServiceModelStringsVersion1.String195, 195); + TokenType = dictionary.CreateString(ServiceModelStringsVersion1.String187, 187); + KeySize = dictionary.CreateString(ServiceModelStringsVersion1.String196, 196); + RequestedTokenReference = dictionary.CreateString(ServiceModelStringsVersion1.String197, 197); + AppliesTo = dictionary.CreateString(ServiceModelStringsVersion1.String198, 198); + Authenticator = dictionary.CreateString(ServiceModelStringsVersion1.String199, 199); + CombinedHash = dictionary.CreateString(ServiceModelStringsVersion1.String200, 200); + BinaryExchange = dictionary.CreateString(ServiceModelStringsVersion1.String201, 201); + Lifetime = dictionary.CreateString(ServiceModelStringsVersion1.String202, 202); + RequestedSecurityToken = dictionary.CreateString(ServiceModelStringsVersion1.String203, 203); + Entropy = dictionary.CreateString(ServiceModelStringsVersion1.String204, 204); + RequestedProofToken = dictionary.CreateString(ServiceModelStringsVersion1.String205, 205); + ComputedKey = dictionary.CreateString(ServiceModelStringsVersion1.String206, 206); + RequestSecurityToken = dictionary.CreateString(ServiceModelStringsVersion1.String207, 207); + RequestType = dictionary.CreateString(ServiceModelStringsVersion1.String208, 208); + RequestSecurityTokenResponseCollection = dictionary.CreateString(ServiceModelStringsVersion1.String62, 62); + Context = dictionary.CreateString(ServiceModelStringsVersion1.String209, 209); + BinarySecret = dictionary.CreateString(ServiceModelStringsVersion1.String210, 210); + Type = dictionary.CreateString(ServiceModelStringsVersion1.String59, 59); + SpnegoValueTypeUri = dictionary.CreateString(ServiceModelStringsVersion1.String211, 211); + TlsnegoValueTypeUri = dictionary.CreateString(ServiceModelStringsVersion1.String212, 212); + Prefix = dictionary.CreateString(ServiceModelStringsVersion1.String213, 213); + Namespace = dictionary.CreateString(ServiceModelStringsVersion1.String214, 214); + RequestSecurityTokenIssuance = dictionary.CreateString(ServiceModelStringsVersion1.String215, 215); + RequestSecurityTokenIssuanceResponse = dictionary.CreateString(ServiceModelStringsVersion1.String216, 216); + RequestTypeIssue = dictionary.CreateString(ServiceModelStringsVersion1.String217, 217); + Psha1ComputedKeyUri = dictionary.CreateString(ServiceModelStringsVersion1.String218, 218); + SymmetricKeyBinarySecret = dictionary.CreateString(ServiceModelStringsVersion1.String219, 219); + NonceBinarySecret = dictionary.CreateString(ServiceModelStringsVersion1.String220, 220); + KeyType = dictionary.CreateString(ServiceModelStringsVersion1.String221, 221); + SymmetricKeyType = dictionary.CreateString(ServiceModelStringsVersion1.String222, 222); + PublicKeyType = dictionary.CreateString(ServiceModelStringsVersion1.String223, 223); + Claims = dictionary.CreateString(ServiceModelStringsVersion1.String224, 224); + InvalidRequestFaultCode = dictionary.CreateString(ServiceModelStringsVersion1.String225, 225); + FailedAuthenticationFaultCode = dictionary.CreateString(ServiceModelStringsVersion1.String182, 182); + RequestFailedFaultCode = dictionary.CreateString(ServiceModelStringsVersion1.String226, 226); + SignWith = dictionary.CreateString(ServiceModelStringsVersion1.String227, 227); + EncryptWith = dictionary.CreateString(ServiceModelStringsVersion1.String228, 228); + EncryptionAlgorithm = dictionary.CreateString(ServiceModelStringsVersion1.String229, 229); + CanonicalizationAlgorithm = dictionary.CreateString(ServiceModelStringsVersion1.String230, 230); + ComputedKeyAlgorithm = dictionary.CreateString(ServiceModelStringsVersion1.String231, 231); + UseKey = dictionary.CreateString(ServiceModelStringsVersion1.String232, 232); + } + } + + class TrustFeb2005Dictionary : TrustDictionary + { + + public TrustFeb2005Dictionary(ServiceModelDictionary dictionary) + : base(dictionary) + { + RequestSecurityTokenResponseCollection = dictionary.CreateString(ServiceModelStringsVersion1.String62, 62); + Namespace = dictionary.CreateString(ServiceModelStringsVersion1.String63, 63); + BinarySecretClauseType = dictionary.CreateString(ServiceModelStringsVersion1.String64, 64); + CombinedHashLabel = dictionary.CreateString(ServiceModelStringsVersion1.String194, 194); + RequestSecurityTokenResponse = dictionary.CreateString(ServiceModelStringsVersion1.String195, 195); + TokenType = dictionary.CreateString(ServiceModelStringsVersion1.String187, 187); + KeySize = dictionary.CreateString(ServiceModelStringsVersion1.String196, 196); + RequestedTokenReference = dictionary.CreateString(ServiceModelStringsVersion1.String197, 197); + AppliesTo = dictionary.CreateString(ServiceModelStringsVersion1.String198, 198); + Authenticator = dictionary.CreateString(ServiceModelStringsVersion1.String199, 199); + CombinedHash = dictionary.CreateString(ServiceModelStringsVersion1.String200, 200); + BinaryExchange = dictionary.CreateString(ServiceModelStringsVersion1.String201, 201); + Lifetime = dictionary.CreateString(ServiceModelStringsVersion1.String202, 202); + RequestedSecurityToken = dictionary.CreateString(ServiceModelStringsVersion1.String203, 203); + Entropy = dictionary.CreateString(ServiceModelStringsVersion1.String204, 204); + RequestedProofToken = dictionary.CreateString(ServiceModelStringsVersion1.String205, 205); + ComputedKey = dictionary.CreateString(ServiceModelStringsVersion1.String206, 206); + RequestSecurityToken = dictionary.CreateString(ServiceModelStringsVersion1.String207, 207); + RequestType = dictionary.CreateString(ServiceModelStringsVersion1.String208, 208); + Context = dictionary.CreateString(ServiceModelStringsVersion1.String209, 209); + BinarySecret = dictionary.CreateString(ServiceModelStringsVersion1.String210, 210); + Type = dictionary.CreateString(ServiceModelStringsVersion1.String59, 59); + SpnegoValueTypeUri = dictionary.CreateString(ServiceModelStringsVersion1.String233, 233); + TlsnegoValueTypeUri = dictionary.CreateString(ServiceModelStringsVersion1.String234, 234); + Prefix = dictionary.CreateString(ServiceModelStringsVersion1.String235, 235); + RequestSecurityTokenIssuance = dictionary.CreateString(ServiceModelStringsVersion1.String236, 236); + RequestSecurityTokenIssuanceResponse = dictionary.CreateString(ServiceModelStringsVersion1.String237, 237); + RequestTypeIssue = dictionary.CreateString(ServiceModelStringsVersion1.String238, 238); + SymmetricKeyBinarySecret = dictionary.CreateString(ServiceModelStringsVersion1.String239, 239); + Psha1ComputedKeyUri = dictionary.CreateString(ServiceModelStringsVersion1.String240, 240); + NonceBinarySecret = dictionary.CreateString(ServiceModelStringsVersion1.String241, 241); + RenewTarget = dictionary.CreateString(ServiceModelStringsVersion1.String242, 242); + CloseTarget = dictionary.CreateString(ServiceModelStringsVersion1.String243, 243); + RequestedTokenClosed = dictionary.CreateString(ServiceModelStringsVersion1.String244, 244); + RequestedAttachedReference = dictionary.CreateString(ServiceModelStringsVersion1.String245, 245); + RequestedUnattachedReference = dictionary.CreateString(ServiceModelStringsVersion1.String246, 246); + IssuedTokensHeader = dictionary.CreateString(ServiceModelStringsVersion1.String247, 247); + RequestTypeRenew = dictionary.CreateString(ServiceModelStringsVersion1.String248, 248); + RequestTypeClose = dictionary.CreateString(ServiceModelStringsVersion1.String249, 249); + KeyType = dictionary.CreateString(ServiceModelStringsVersion1.String221, 221); + SymmetricKeyType = dictionary.CreateString(ServiceModelStringsVersion1.String239, 239); + PublicKeyType = dictionary.CreateString(ServiceModelStringsVersion1.String250, 250); + Claims = dictionary.CreateString(ServiceModelStringsVersion1.String224, 224); + InvalidRequestFaultCode = dictionary.CreateString(ServiceModelStringsVersion1.String225, 225); + FailedAuthenticationFaultCode = dictionary.CreateString(ServiceModelStringsVersion1.String182, 182); + UseKey = dictionary.CreateString(ServiceModelStringsVersion1.String232, 232); + SignWith = dictionary.CreateString(ServiceModelStringsVersion1.String227, 227); + EncryptWith = dictionary.CreateString(ServiceModelStringsVersion1.String228, 228); + EncryptionAlgorithm = dictionary.CreateString(ServiceModelStringsVersion1.String229, 229); + CanonicalizationAlgorithm = dictionary.CreateString(ServiceModelStringsVersion1.String230, 230); + ComputedKeyAlgorithm = dictionary.CreateString(ServiceModelStringsVersion1.String231, 231); + } + } + + class UtilityDictionary + { + public XmlDictionaryString IdAttribute; + public XmlDictionaryString Namespace; + public XmlDictionaryString Timestamp; + public XmlDictionaryString CreatedElement; + public XmlDictionaryString ExpiresElement; + public XmlDictionaryString Prefix; + public XmlDictionaryString UniqueEndpointHeaderName; + public XmlDictionaryString UniqueEndpointHeaderNamespace; + + public UtilityDictionary(ServiceModelDictionary dictionary) + { + IdAttribute = dictionary.CreateString(ServiceModelStringsVersion1.String14, 14); + Namespace = dictionary.CreateString(ServiceModelStringsVersion1.String51, 51); + Timestamp = dictionary.CreateString(ServiceModelStringsVersion1.String53, 53); + CreatedElement = dictionary.CreateString(ServiceModelStringsVersion1.String54, 54); + ExpiresElement = dictionary.CreateString(ServiceModelStringsVersion1.String55, 55); + Prefix = dictionary.CreateString(ServiceModelStringsVersion1.String305, 305); + UniqueEndpointHeaderName = dictionary.CreateString(ServiceModelStringsVersion1.String306, 306); + UniqueEndpointHeaderNamespace = dictionary.CreateString(ServiceModelStringsVersion1.String307, 307); + } + } + + class WsrmFeb2005Dictionary + { + public XmlDictionaryString Identifier; + public XmlDictionaryString Namespace; + public XmlDictionaryString SequenceAcknowledgement; + public XmlDictionaryString AcknowledgementRange; + public XmlDictionaryString Upper; + public XmlDictionaryString Lower; + public XmlDictionaryString BufferRemaining; + public XmlDictionaryString NETNamespace; + public XmlDictionaryString SequenceAcknowledgementAction; + public XmlDictionaryString Sequence; + public XmlDictionaryString MessageNumber; + public XmlDictionaryString AckRequested; + public XmlDictionaryString AckRequestedAction; + public XmlDictionaryString AcksTo; + public XmlDictionaryString Accept; + public XmlDictionaryString CreateSequence; + public XmlDictionaryString CreateSequenceAction; + public XmlDictionaryString CreateSequenceRefused; + public XmlDictionaryString CreateSequenceResponse; + public XmlDictionaryString CreateSequenceResponseAction; + public XmlDictionaryString Expires; + public XmlDictionaryString FaultCode; + public XmlDictionaryString InvalidAcknowledgement; + public XmlDictionaryString LastMessage; + public XmlDictionaryString LastMessageAction; + public XmlDictionaryString LastMessageNumberExceeded; + public XmlDictionaryString MessageNumberRollover; + public XmlDictionaryString Nack; + public XmlDictionaryString NETPrefix; + public XmlDictionaryString Offer; + public XmlDictionaryString Prefix; + public XmlDictionaryString SequenceFault; + public XmlDictionaryString SequenceTerminated; + public XmlDictionaryString TerminateSequence; + public XmlDictionaryString TerminateSequenceAction; + public XmlDictionaryString UnknownSequence; + public XmlDictionaryString ConnectionLimitReached; + + public WsrmFeb2005Dictionary(ServiceModelDictionary dictionary) + { + Identifier = dictionary.CreateString(ServiceModelStringsVersion1.String15, 15); + Namespace = dictionary.CreateString(ServiceModelStringsVersion1.String16, 16); + SequenceAcknowledgement = dictionary.CreateString(ServiceModelStringsVersion1.String23, 23); + AcknowledgementRange = dictionary.CreateString(ServiceModelStringsVersion1.String24, 24); + Upper = dictionary.CreateString(ServiceModelStringsVersion1.String25, 25); + Lower = dictionary.CreateString(ServiceModelStringsVersion1.String26, 26); + BufferRemaining = dictionary.CreateString(ServiceModelStringsVersion1.String27, 27); + NETNamespace = dictionary.CreateString(ServiceModelStringsVersion1.String28, 28); + SequenceAcknowledgementAction = dictionary.CreateString(ServiceModelStringsVersion1.String29, 29); + Sequence = dictionary.CreateString(ServiceModelStringsVersion1.String31, 31); + MessageNumber = dictionary.CreateString(ServiceModelStringsVersion1.String32, 32); + AckRequested = dictionary.CreateString(ServiceModelStringsVersion1.String328, 328); + AckRequestedAction = dictionary.CreateString(ServiceModelStringsVersion1.String329, 329); + AcksTo = dictionary.CreateString(ServiceModelStringsVersion1.String330, 330); + Accept = dictionary.CreateString(ServiceModelStringsVersion1.String331, 331); + CreateSequence = dictionary.CreateString(ServiceModelStringsVersion1.String332, 332); + CreateSequenceAction = dictionary.CreateString(ServiceModelStringsVersion1.String333, 333); + CreateSequenceRefused = dictionary.CreateString(ServiceModelStringsVersion1.String334, 334); + CreateSequenceResponse = dictionary.CreateString(ServiceModelStringsVersion1.String335, 335); + CreateSequenceResponseAction = dictionary.CreateString(ServiceModelStringsVersion1.String336, 336); + Expires = dictionary.CreateString(ServiceModelStringsVersion1.String55, 55); + FaultCode = dictionary.CreateString(ServiceModelStringsVersion1.String337, 337); + InvalidAcknowledgement = dictionary.CreateString(ServiceModelStringsVersion1.String338, 338); + LastMessage = dictionary.CreateString(ServiceModelStringsVersion1.String339, 339); + LastMessageAction = dictionary.CreateString(ServiceModelStringsVersion1.String340, 340); + LastMessageNumberExceeded = dictionary.CreateString(ServiceModelStringsVersion1.String341, 341); + MessageNumberRollover = dictionary.CreateString(ServiceModelStringsVersion1.String342, 342); + Nack = dictionary.CreateString(ServiceModelStringsVersion1.String343, 343); + NETPrefix = dictionary.CreateString(ServiceModelStringsVersion1.String344, 344); + Offer = dictionary.CreateString(ServiceModelStringsVersion1.String345, 345); + Prefix = dictionary.CreateString(ServiceModelStringsVersion1.String346, 346); + SequenceFault = dictionary.CreateString(ServiceModelStringsVersion1.String347, 347); + SequenceTerminated = dictionary.CreateString(ServiceModelStringsVersion1.String348, 348); + TerminateSequence = dictionary.CreateString(ServiceModelStringsVersion1.String349, 349); + TerminateSequenceAction = dictionary.CreateString(ServiceModelStringsVersion1.String350, 350); + UnknownSequence = dictionary.CreateString(ServiceModelStringsVersion1.String351, 351); + ConnectionLimitReached = dictionary.CreateString(ServiceModelStringsVersion1.String480, 480); + } + } + + class XmlEncryptionDictionary + { + public XmlDictionaryString Namespace; + public XmlDictionaryString DataReference; + public XmlDictionaryString EncryptedData; + public XmlDictionaryString EncryptionMethod; + public XmlDictionaryString CipherData; + public XmlDictionaryString CipherValue; + public XmlDictionaryString ReferenceList; + public XmlDictionaryString Encoding; + public XmlDictionaryString MimeType; + public XmlDictionaryString Type; + public XmlDictionaryString Id; + public XmlDictionaryString CarriedKeyName; + public XmlDictionaryString Recipient; + public XmlDictionaryString EncryptedKey; + public XmlDictionaryString URI; + public XmlDictionaryString KeyReference; + public XmlDictionaryString Prefix; + public XmlDictionaryString ElementType; + public XmlDictionaryString ContentType; + public XmlDictionaryString AlgorithmAttribute; + + public XmlEncryptionDictionary(ServiceModelDictionary dictionary) + { + Namespace = dictionary.CreateString(ServiceModelStringsVersion1.String37, 37); + DataReference = dictionary.CreateString(ServiceModelStringsVersion1.String46, 46); + EncryptedData = dictionary.CreateString(ServiceModelStringsVersion1.String47, 47); + EncryptionMethod = dictionary.CreateString(ServiceModelStringsVersion1.String48, 48); + CipherData = dictionary.CreateString(ServiceModelStringsVersion1.String49, 49); + CipherValue = dictionary.CreateString(ServiceModelStringsVersion1.String50, 50); + ReferenceList = dictionary.CreateString(ServiceModelStringsVersion1.String57, 57); + Encoding = dictionary.CreateString(ServiceModelStringsVersion1.String308, 308); + MimeType = dictionary.CreateString(ServiceModelStringsVersion1.String309, 309); + Type = dictionary.CreateString(ServiceModelStringsVersion1.String59, 59); + Id = dictionary.CreateString(ServiceModelStringsVersion1.String14, 14); + CarriedKeyName = dictionary.CreateString(ServiceModelStringsVersion1.String310, 310); + Recipient = dictionary.CreateString(ServiceModelStringsVersion1.String311, 311); + EncryptedKey = dictionary.CreateString(ServiceModelStringsVersion1.String312, 312); + URI = dictionary.CreateString(ServiceModelStringsVersion1.String11, 11); + KeyReference = dictionary.CreateString(ServiceModelStringsVersion1.String313, 313); + Prefix = dictionary.CreateString(ServiceModelStringsVersion1.String314, 314); + ElementType = dictionary.CreateString(ServiceModelStringsVersion1.String315, 315); + ContentType = dictionary.CreateString(ServiceModelStringsVersion1.String316, 316); + AlgorithmAttribute = dictionary.CreateString(ServiceModelStringsVersion1.String8, 8); + } + } + + class XmlSignatureDictionary + { + public XmlDictionaryString Algorithm; + public XmlDictionaryString URI; + public XmlDictionaryString Reference; + public XmlDictionaryString Transforms; + public XmlDictionaryString Transform; + public XmlDictionaryString DigestMethod; + public XmlDictionaryString DigestValue; + public XmlDictionaryString Namespace; + public XmlDictionaryString EnvelopedSignature; + public XmlDictionaryString KeyInfo; + public XmlDictionaryString Signature; + public XmlDictionaryString SignedInfo; + public XmlDictionaryString CanonicalizationMethod; + public XmlDictionaryString SignatureMethod; + public XmlDictionaryString SignatureValue; + public XmlDictionaryString KeyName; + public XmlDictionaryString Type; + public XmlDictionaryString MgmtData; + public XmlDictionaryString Prefix; + public XmlDictionaryString KeyValue; + public XmlDictionaryString RsaKeyValue; + public XmlDictionaryString Modulus; + public XmlDictionaryString Exponent; + public XmlDictionaryString X509Data; + public XmlDictionaryString X509IssuerSerial; + public XmlDictionaryString X509IssuerName; + public XmlDictionaryString X509SerialNumber; + public XmlDictionaryString X509Certificate; + + public XmlSignatureDictionary(ServiceModelDictionary dictionary) + { + Algorithm = dictionary.CreateString(ServiceModelStringsVersion1.String8, 8); + URI = dictionary.CreateString(ServiceModelStringsVersion1.String11, 11); + Reference = dictionary.CreateString(ServiceModelStringsVersion1.String12, 12); + Transforms = dictionary.CreateString(ServiceModelStringsVersion1.String17, 17); + Transform = dictionary.CreateString(ServiceModelStringsVersion1.String18, 18); + DigestMethod = dictionary.CreateString(ServiceModelStringsVersion1.String19, 19); + DigestValue = dictionary.CreateString(ServiceModelStringsVersion1.String20, 20); + Namespace = dictionary.CreateString(ServiceModelStringsVersion1.String33, 33); + EnvelopedSignature = dictionary.CreateString(ServiceModelStringsVersion1.String34, 34); + KeyInfo = dictionary.CreateString(ServiceModelStringsVersion1.String35, 35); + Signature = dictionary.CreateString(ServiceModelStringsVersion1.String41, 41); + SignedInfo = dictionary.CreateString(ServiceModelStringsVersion1.String42, 42); + CanonicalizationMethod = dictionary.CreateString(ServiceModelStringsVersion1.String43, 43); + SignatureMethod = dictionary.CreateString(ServiceModelStringsVersion1.String44, 44); + SignatureValue = dictionary.CreateString(ServiceModelStringsVersion1.String45, 45); + KeyName = dictionary.CreateString(ServiceModelStringsVersion1.String317, 317); + Type = dictionary.CreateString(ServiceModelStringsVersion1.String59, 59); + MgmtData = dictionary.CreateString(ServiceModelStringsVersion1.String318, 318); + Prefix = dictionary.CreateString(ServiceModelStringsVersion1.String81, 81); + KeyValue = dictionary.CreateString(ServiceModelStringsVersion1.String319, 319); + RsaKeyValue = dictionary.CreateString(ServiceModelStringsVersion1.String320, 320); + Modulus = dictionary.CreateString(ServiceModelStringsVersion1.String321, 321); + Exponent = dictionary.CreateString(ServiceModelStringsVersion1.String322, 322); + X509Data = dictionary.CreateString(ServiceModelStringsVersion1.String323, 323); + X509IssuerSerial = dictionary.CreateString(ServiceModelStringsVersion1.String324, 324); + X509IssuerName = dictionary.CreateString(ServiceModelStringsVersion1.String325, 325); + X509SerialNumber = dictionary.CreateString(ServiceModelStringsVersion1.String326, 326); + X509Certificate = dictionary.CreateString(ServiceModelStringsVersion1.String327, 327); + } + } + + class Message11Dictionary + { + public XmlDictionaryString Namespace; + public XmlDictionaryString Actor; + public XmlDictionaryString FaultCode; + public XmlDictionaryString FaultString; + public XmlDictionaryString FaultActor; + public XmlDictionaryString FaultDetail; + public XmlDictionaryString FaultNamespace; + + public Message11Dictionary(ServiceModelDictionary dictionary) + { + Namespace = dictionary.CreateString(ServiceModelStringsVersion1.String481, 481); + Actor = dictionary.CreateString(ServiceModelStringsVersion1.String482, 482); + FaultCode = dictionary.CreateString(ServiceModelStringsVersion1.String483, 483); + FaultString = dictionary.CreateString(ServiceModelStringsVersion1.String484, 484); + FaultActor = dictionary.CreateString(ServiceModelStringsVersion1.String485, 485); + FaultDetail = dictionary.CreateString(ServiceModelStringsVersion1.String486, 486); + FaultNamespace = dictionary.CreateString(ServiceModelStringsVersion1.String81, 81); + } + } + + static class ActivityIdFlowStrings + { + // Main dictionary strings + public const string ActivityId = ServiceModelStringsVersion1.String425; + public const string ActivityIdNamespace = ServiceModelStringsVersion1.String426; + } + + static class AddressingStrings + { + // Main dictionary strings + public const string Action = ServiceModelStringsVersion1.String5; + public const string To = ServiceModelStringsVersion1.String6; + public const string RelatesTo = ServiceModelStringsVersion1.String9; + public const string MessageId = ServiceModelStringsVersion1.String13; + public const string Address = ServiceModelStringsVersion1.String21; + public const string ReplyTo = ServiceModelStringsVersion1.String22; + public const string Empty = ServiceModelStringsVersion1.String81; + public const string From = ServiceModelStringsVersion1.String82; + public const string FaultTo = ServiceModelStringsVersion1.String83; + public const string EndpointReference = ServiceModelStringsVersion1.String84; + public const string PortType = ServiceModelStringsVersion1.String85; + public const string ServiceName = ServiceModelStringsVersion1.String86; + public const string PortName = ServiceModelStringsVersion1.String87; + public const string ReferenceProperties = ServiceModelStringsVersion1.String88; + public const string RelationshipType = ServiceModelStringsVersion1.String89; + public const string Reply = ServiceModelStringsVersion1.String90; + public const string Prefix = ServiceModelStringsVersion1.String91; + public const string IdentityExtensionNamespace = ServiceModelStringsVersion1.String92; + public const string Identity = ServiceModelStringsVersion1.String93; + public const string Spn = ServiceModelStringsVersion1.String94; + public const string Upn = ServiceModelStringsVersion1.String95; + public const string Rsa = ServiceModelStringsVersion1.String96; + public const string Dns = ServiceModelStringsVersion1.String97; + public const string X509v3Certificate = ServiceModelStringsVersion1.String98; + public const string ReferenceParameters = ServiceModelStringsVersion1.String100; + public const string IsReferenceParameter = ServiceModelStringsVersion1.String101; + // String constants + public const string EndpointUnavailable = "EndpointUnavailable"; + public const string ActionNotSupported = "ActionNotSupported"; + public const string EndpointReferenceType = "EndpointReferenceType"; + public const string Request = "Request"; + public const string DestinationUnreachable = "DestinationUnreachable"; + public const string AnonymousUri = "http://schemas.microsoft.com/2005/12/ServiceModel/Addressing/Anonymous"; + public const string NoneUri = "http://schemas.microsoft.com/2005/12/ServiceModel/Addressing/None"; + public const string IndigoNamespace = "http://schemas.microsoft.com/serviceModel/2004/05/addressing"; + public const string ChannelTerminated = "ChannelTerminated"; + } + + static class Addressing10Strings + { + // Main dictionary strings + public const string Namespace = ServiceModelStringsVersion1.String3; + public const string Anonymous = ServiceModelStringsVersion1.String10; + public const string FaultAction = ServiceModelStringsVersion1.String99; + public const string ReplyRelationship = ServiceModelStringsVersion1.String102; + public const string NoneAddress = ServiceModelStringsVersion1.String103; + public const string Metadata = ServiceModelStringsVersion1.String104; + // String constants + public const string MessageAddressingHeaderRequired = "MessageAddressingHeaderRequired"; + public const string InvalidAddressingHeader = "InvalidAddressingHeader"; + public const string InvalidCardinality = "InvalidCardinality"; + public const string ActionMismatch = "ActionMismatch"; + public const string ProblemHeaderQName = "ProblemHeaderQName"; + public const string FaultDetail = "FaultDetail"; + public const string DefaultFaultAction = "http://www.w3.org/2005/08/addressing/soap/fault"; + } + + static class Addressing200408Strings + { + // Main dictionary strings + public const string Namespace = ServiceModelStringsVersion1.String105; + public const string Anonymous = ServiceModelStringsVersion1.String106; + public const string FaultAction = ServiceModelStringsVersion1.String107; + // String constants + public const string InvalidMessageInformationHeader = "InvalidMessageInformationHeader"; + public const string MessageInformationHeaderRequired = "MessageInformationHeaderRequired"; + public const string DefaultFaultAction = "http://schemas.xmlsoap.org/ws/2004/08/addressing/fault"; + } + + static class AddressingNoneStrings + { + // Main dictionary strings + public const string Namespace = ServiceModelStringsVersion1.String439; + } + + static class AtomicTransactionExternalStrings + { + // Main dictionary strings + public const string Prefix = ServiceModelStringsVersion1.String383; + public const string Prepare = ServiceModelStringsVersion1.String387; + public const string Prepared = ServiceModelStringsVersion1.String388; + public const string ReadOnly = ServiceModelStringsVersion1.String389; + public const string Commit = ServiceModelStringsVersion1.String390; + public const string Rollback = ServiceModelStringsVersion1.String391; + public const string Committed = ServiceModelStringsVersion1.String392; + public const string Aborted = ServiceModelStringsVersion1.String393; + public const string Replay = ServiceModelStringsVersion1.String394; + public const string CompletionCoordinatorPortType = ServiceModelStringsVersion1.String404; + public const string CompletionParticipantPortType = ServiceModelStringsVersion1.String405; + public const string CoordinatorPortType = ServiceModelStringsVersion1.String406; + public const string ParticipantPortType = ServiceModelStringsVersion1.String407; + public const string InconsistentInternalState = ServiceModelStringsVersion1.String408; + } + + static class AtomicTransactionExternal10Strings + { + // Main dictionary strings + public const string Namespace = ServiceModelStringsVersion1.String382; + public const string CompletionUri = ServiceModelStringsVersion1.String384; + public const string Durable2PCUri = ServiceModelStringsVersion1.String385; + public const string Volatile2PCUri = ServiceModelStringsVersion1.String386; + public const string CommitAction = ServiceModelStringsVersion1.String395; + public const string RollbackAction = ServiceModelStringsVersion1.String396; + public const string CommittedAction = ServiceModelStringsVersion1.String397; + public const string AbortedAction = ServiceModelStringsVersion1.String398; + public const string PrepareAction = ServiceModelStringsVersion1.String399; + public const string PreparedAction = ServiceModelStringsVersion1.String400; + public const string ReadOnlyAction = ServiceModelStringsVersion1.String401; + public const string ReplayAction = ServiceModelStringsVersion1.String402; + public const string FaultAction = ServiceModelStringsVersion1.String403; + } + + static class CoordinationExternalStrings + { + // Main dictionary strings + public const string Prefix = ServiceModelStringsVersion1.String357; + public const string CreateCoordinationContext = ServiceModelStringsVersion1.String358; + public const string CreateCoordinationContextResponse = ServiceModelStringsVersion1.String359; + public const string CoordinationContext = ServiceModelStringsVersion1.String360; + public const string CurrentContext = ServiceModelStringsVersion1.String361; + public const string CoordinationType = ServiceModelStringsVersion1.String362; + public const string RegistrationService = ServiceModelStringsVersion1.String363; + public const string Register = ServiceModelStringsVersion1.String364; + public const string RegisterResponse = ServiceModelStringsVersion1.String365; + public const string Protocol = ServiceModelStringsVersion1.String366; + public const string CoordinatorProtocolService = ServiceModelStringsVersion1.String367; + public const string ParticipantProtocolService = ServiceModelStringsVersion1.String368; + public const string Expires = ServiceModelStringsVersion1.String55; + public const string Identifier = ServiceModelStringsVersion1.String15; + public const string ActivationCoordinatorPortType = ServiceModelStringsVersion1.String374; + public const string RegistrationCoordinatorPortType = ServiceModelStringsVersion1.String375; + public const string InvalidState = ServiceModelStringsVersion1.String376; + public const string InvalidProtocol = ServiceModelStringsVersion1.String377; + public const string InvalidParameters = ServiceModelStringsVersion1.String378; + public const string NoActivity = ServiceModelStringsVersion1.String379; + public const string ContextRefused = ServiceModelStringsVersion1.String380; + public const string AlreadyRegistered = ServiceModelStringsVersion1.String381; + } + + static class CoordinationExternal10Strings + { + // Main dictionary strings + public const string Namespace = ServiceModelStringsVersion1.String356; + public const string CreateCoordinationContextAction = ServiceModelStringsVersion1.String369; + public const string CreateCoordinationContextResponseAction = ServiceModelStringsVersion1.String370; + public const string RegisterAction = ServiceModelStringsVersion1.String371; + public const string RegisterResponseAction = ServiceModelStringsVersion1.String372; + public const string FaultAction = ServiceModelStringsVersion1.String373; + } + + static class DotNetAddressingStrings + { + // Main dictionary strings + public const string Namespace = ServiceModelStringsVersion1.String108; + public const string RedirectTo = ServiceModelStringsVersion1.String109; + public const string Via = ServiceModelStringsVersion1.String110; + } + + static class DotNetAtomicTransactionExternalStrings + { + // Main dictionary strings + public const string Namespace = ServiceModelStringsVersion1.String65; + public const string Prefix = ServiceModelStringsVersion1.String409; + public const string Enlistment = ServiceModelStringsVersion1.String410; + public const string Protocol = ServiceModelStringsVersion1.String411; + public const string LocalTransactionId = ServiceModelStringsVersion1.String412; + public const string IsolationLevel = ServiceModelStringsVersion1.String413; + public const string IsolationFlags = ServiceModelStringsVersion1.String414; + public const string Description = ServiceModelStringsVersion1.String415; + public const string Loopback = ServiceModelStringsVersion1.String416; + public const string RegisterInfo = ServiceModelStringsVersion1.String417; + public const string ContextId = ServiceModelStringsVersion1.String418; + public const string TokenId = ServiceModelStringsVersion1.String419; + public const string AccessDenied = ServiceModelStringsVersion1.String420; + public const string InvalidPolicy = ServiceModelStringsVersion1.String421; + public const string CoordinatorRegistrationFailed = ServiceModelStringsVersion1.String422; + public const string TooManyEnlistments = ServiceModelStringsVersion1.String423; + public const string Disabled = ServiceModelStringsVersion1.String424; + } + + static class DotNetOneWayStrings + { + // Main dictionary strings + public const string Namespace = ServiceModelStringsVersion1.String437; + public const string HeaderName = ServiceModelStringsVersion1.String438; + } + + static class DotNetSecurityStrings + { + // Main dictionary strings + public const string Namespace = ServiceModelStringsVersion1.String162; + public const string Prefix = ServiceModelStringsVersion1.String163; + // String constants + public const string KeyRenewalNeededFault = "ExpiredSecurityContextTokenKey"; + public const string SecuritySessionAbortedFault = "SecuritySessionAborted"; + public const string SecurityServerTooBusyFault = "ServerTooBusy"; + public const string SecuritySessionFaultAction = "http://schemas.microsoft.com/ws/2006/05/security/SecureConversationFault"; + public const string SecureConversationCancelNotAllowedFault = "SecureConversationCancellationNotAllowed"; + } + + static class ExclusiveC14NStrings + { + // Main dictionary strings + public const string Namespace = ServiceModelStringsVersion1.String111; + public const string PrefixList = ServiceModelStringsVersion1.String112; + public const string InclusiveNamespaces = ServiceModelStringsVersion1.String113; + public const string Prefix = ServiceModelStringsVersion1.String114; + } + + static class MessageStrings + { + // Main dictionary strings + public const string MustUnderstand = ServiceModelStringsVersion1.String0; + public const string Envelope = ServiceModelStringsVersion1.String1; + public const string Header = ServiceModelStringsVersion1.String4; + public const string Body = ServiceModelStringsVersion1.String7; + public const string Prefix = ServiceModelStringsVersion1.String66; + public const string Fault = ServiceModelStringsVersion1.String67; + public const string MustUnderstandFault = ServiceModelStringsVersion1.String68; + public const string Namespace = ServiceModelStringsVersion1.String440; + } + + static class Message11Strings + { + // Text dictionary strings + public const string Namespace = ServiceModelStringsVersion1.String481; + public const string Actor = ServiceModelStringsVersion1.String482; + public const string FaultCode = ServiceModelStringsVersion1.String483; + public const string FaultString = ServiceModelStringsVersion1.String484; + public const string FaultActor = ServiceModelStringsVersion1.String485; + public const string FaultDetail = ServiceModelStringsVersion1.String486; + public const string FaultNamespace = ServiceModelStringsVersion1.String81; + } + + static class Message12Strings + { + // Main dictionary strings + public const string Namespace = ServiceModelStringsVersion1.String2; + public const string Role = ServiceModelStringsVersion1.String69; + public const string Relay = ServiceModelStringsVersion1.String70; + public const string FaultCode = ServiceModelStringsVersion1.String71; + public const string FaultReason = ServiceModelStringsVersion1.String72; + public const string FaultText = ServiceModelStringsVersion1.String73; + public const string FaultNode = ServiceModelStringsVersion1.String74; + public const string FaultRole = ServiceModelStringsVersion1.String75; + public const string FaultDetail = ServiceModelStringsVersion1.String76; + public const string FaultValue = ServiceModelStringsVersion1.String77; + public const string FaultSubcode = ServiceModelStringsVersion1.String78; + public const string NotUnderstood = ServiceModelStringsVersion1.String79; + public const string QName = ServiceModelStringsVersion1.String80; + } + + static class OleTxTransactionExternalStrings + { + // Main dictionary strings + public const string Namespace = ServiceModelStringsVersion1.String352; + public const string Prefix = ServiceModelStringsVersion1.String353; + public const string OleTxTransaction = ServiceModelStringsVersion1.String354; + public const string PropagationToken = ServiceModelStringsVersion1.String355; + } + + static class PeerWireStringsStrings + { + // Main dictionary strings + public const string FloodAction = ServiceModelStringsVersion1.String429; + public const string LinkUtilityAction = ServiceModelStringsVersion1.String430; + public const string HopCount = ServiceModelStringsVersion1.String431; + public const string HopCountNamespace = ServiceModelStringsVersion1.String432; + public const string PeerVia = ServiceModelStringsVersion1.String433; + public const string Namespace = ServiceModelStringsVersion1.String434; + public const string Demuxer = ServiceModelStringsVersion1.String435; + public const string PeerTo = ServiceModelStringsVersion1.String436; + } + + static class PolicyStrings + { + // Main dictionary strings + public const string Namespace = ServiceModelStringsVersion1.String428; + } + + static class SamlStrings + { + // Main dictionary strings + public const string Access = ServiceModelStringsVersion1.String251; + public const string AccessDecision = ServiceModelStringsVersion1.String252; + public const string Action = ServiceModelStringsVersion1.String5; + public const string Advice = ServiceModelStringsVersion1.String253; + public const string Assertion = ServiceModelStringsVersion1.String179; + public const string AssertionId = ServiceModelStringsVersion1.String254; + public const string AssertionIdReference = ServiceModelStringsVersion1.String255; + public const string Attribute = ServiceModelStringsVersion1.String256; + public const string AttributeName = ServiceModelStringsVersion1.String257; + public const string AttributeNamespace = ServiceModelStringsVersion1.String258; + public const string AttributeStatement = ServiceModelStringsVersion1.String259; + public const string AttributeValue = ServiceModelStringsVersion1.String260; + public const string Audience = ServiceModelStringsVersion1.String261; + public const string AudienceRestrictionCondition = ServiceModelStringsVersion1.String262; + public const string AuthenticationInstant = ServiceModelStringsVersion1.String263; + public const string AuthenticationMethod = ServiceModelStringsVersion1.String264; + public const string AuthenticationStatement = ServiceModelStringsVersion1.String265; + public const string AuthorityBinding = ServiceModelStringsVersion1.String266; + public const string AuthorityKind = ServiceModelStringsVersion1.String267; + public const string AuthorizationDecisionStatement = ServiceModelStringsVersion1.String268; + public const string Binding = ServiceModelStringsVersion1.String269; + public const string Condition = ServiceModelStringsVersion1.String270; + public const string Conditions = ServiceModelStringsVersion1.String271; + public const string Decision = ServiceModelStringsVersion1.String272; + public const string DoNotCacheCondition = ServiceModelStringsVersion1.String273; + public const string Evidence = ServiceModelStringsVersion1.String274; + public const string IssueInstant = ServiceModelStringsVersion1.String275; + public const string Issuer = ServiceModelStringsVersion1.String276; + public const string Location = ServiceModelStringsVersion1.String277; + public const string MajorVersion = ServiceModelStringsVersion1.String278; + public const string MinorVersion = ServiceModelStringsVersion1.String279; + public const string Namespace = ServiceModelStringsVersion1.String180; + public const string NameIdentifier = ServiceModelStringsVersion1.String280; + public const string NameIdentifierFormat = ServiceModelStringsVersion1.String281; + public const string NameIdentifierNameQualifier = ServiceModelStringsVersion1.String282; + public const string ActionNamespaceAttribute = ServiceModelStringsVersion1.String283; + public const string NotBefore = ServiceModelStringsVersion1.String284; + public const string NotOnOrAfter = ServiceModelStringsVersion1.String285; + public const string PreferredPrefix = ServiceModelStringsVersion1.String286; + public const string Statement = ServiceModelStringsVersion1.String287; + public const string Subject = ServiceModelStringsVersion1.String288; + public const string SubjectConfirmation = ServiceModelStringsVersion1.String289; + public const string SubjectConfirmationData = ServiceModelStringsVersion1.String290; + public const string SubjectConfirmationMethod = ServiceModelStringsVersion1.String291; + public const string HolderOfKey = ServiceModelStringsVersion1.String292; + public const string SenderVouches = ServiceModelStringsVersion1.String293; + public const string SubjectLocality = ServiceModelStringsVersion1.String294; + public const string SubjectLocalityDNSAddress = ServiceModelStringsVersion1.String295; + public const string SubjectLocalityIPAddress = ServiceModelStringsVersion1.String296; + public const string SubjectStatement = ServiceModelStringsVersion1.String297; + public const string UnspecifiedAuthenticationMethod = ServiceModelStringsVersion1.String298; + public const string NamespaceAttributePrefix = ServiceModelStringsVersion1.String299; + public const string Resource = ServiceModelStringsVersion1.String300; + public const string UserName = ServiceModelStringsVersion1.String301; + public const string UserNameNamespace = ServiceModelStringsVersion1.String302; + public const string EmailName = ServiceModelStringsVersion1.String303; + public const string EmailNamespace = ServiceModelStringsVersion1.String304; + } + + static class SecureConversationStrings + { + } + + static class SecureConversationApr2004Strings + { + // Main dictionary strings + public const string SecurityContextToken = ServiceModelStringsVersion1.String115; + public const string DerivedKeyToken = ServiceModelStringsVersion1.String39; + public const string AlgorithmAttribute = ServiceModelStringsVersion1.String8; + public const string Generation = ServiceModelStringsVersion1.String116; + public const string Label = ServiceModelStringsVersion1.String117; + public const string Length = ServiceModelStringsVersion1.String56; + public const string Nonce = ServiceModelStringsVersion1.String40; + public const string Offset = ServiceModelStringsVersion1.String118; + public const string Properties = ServiceModelStringsVersion1.String119; + public const string Identifier = ServiceModelStringsVersion1.String15; + public const string Cookie = ServiceModelStringsVersion1.String120; + public const string Prefix = ServiceModelStringsVersion1.String121; + public const string Namespace = ServiceModelStringsVersion1.String122; + public const string DerivedKeyTokenType = ServiceModelStringsVersion1.String123; + public const string SecurityContextTokenType = ServiceModelStringsVersion1.String124; + public const string SecurityContextTokenReferenceValueType = ServiceModelStringsVersion1.String124; + public const string RequestSecurityContextIssuance = ServiceModelStringsVersion1.String125; + public const string RequestSecurityContextIssuanceResponse = ServiceModelStringsVersion1.String126; + public const string RenewNeededFaultCode = ServiceModelStringsVersion1.String127; + public const string BadContextTokenFaultCode = ServiceModelStringsVersion1.String128; + } + + static class SecureConversationFeb2005Strings + { + // Main dictionary strings + public const string Namespace = ServiceModelStringsVersion1.String38; + public const string DerivedKeyToken = ServiceModelStringsVersion1.String39; + public const string Nonce = ServiceModelStringsVersion1.String40; + public const string Length = ServiceModelStringsVersion1.String56; + public const string SecurityContextToken = ServiceModelStringsVersion1.String115; + public const string AlgorithmAttribute = ServiceModelStringsVersion1.String8; + public const string Generation = ServiceModelStringsVersion1.String116; + public const string Label = ServiceModelStringsVersion1.String117; + public const string Offset = ServiceModelStringsVersion1.String118; + public const string Properties = ServiceModelStringsVersion1.String119; + public const string Identifier = ServiceModelStringsVersion1.String15; + public const string Cookie = ServiceModelStringsVersion1.String120; + public const string RenewNeededFaultCode = ServiceModelStringsVersion1.String127; + public const string BadContextTokenFaultCode = ServiceModelStringsVersion1.String128; + public const string Prefix = ServiceModelStringsVersion1.String129; + public const string DerivedKeyTokenType = ServiceModelStringsVersion1.String130; + public const string SecurityContextTokenType = ServiceModelStringsVersion1.String131; + public const string SecurityContextTokenReferenceValueType = ServiceModelStringsVersion1.String131; + public const string RequestSecurityContextIssuance = ServiceModelStringsVersion1.String132; + public const string RequestSecurityContextIssuanceResponse = ServiceModelStringsVersion1.String133; + public const string RequestSecurityContextRenew = ServiceModelStringsVersion1.String134; + public const string RequestSecurityContextRenewResponse = ServiceModelStringsVersion1.String135; + public const string RequestSecurityContextClose = ServiceModelStringsVersion1.String136; + public const string RequestSecurityContextCloseResponse = ServiceModelStringsVersion1.String137; + } + + static class SecurityAlgorithmStrings + { + // Main dictionary strings + public const string Aes128Encryption = ServiceModelStringsVersion1.String138; + public const string Aes128KeyWrap = ServiceModelStringsVersion1.String139; + public const string Aes192Encryption = ServiceModelStringsVersion1.String140; + public const string Aes192KeyWrap = ServiceModelStringsVersion1.String141; + public const string Aes256Encryption = ServiceModelStringsVersion1.String142; + public const string Aes256KeyWrap = ServiceModelStringsVersion1.String143; + public const string DesEncryption = ServiceModelStringsVersion1.String144; + public const string DsaSha1Signature = ServiceModelStringsVersion1.String145; + public const string ExclusiveC14n = ServiceModelStringsVersion1.String111; + public const string ExclusiveC14nWithComments = ServiceModelStringsVersion1.String146; + public const string HmacSha1Signature = ServiceModelStringsVersion1.String147; + public const string HmacSha256Signature = ServiceModelStringsVersion1.String148; + public const string Psha1KeyDerivation = ServiceModelStringsVersion1.String149; + public const string Ripemd160Digest = ServiceModelStringsVersion1.String150; + public const string RsaOaepKeyWrap = ServiceModelStringsVersion1.String151; + public const string RsaSha1Signature = ServiceModelStringsVersion1.String152; + public const string RsaSha256Signature = ServiceModelStringsVersion1.String153; + public const string RsaV15KeyWrap = ServiceModelStringsVersion1.String154; + public const string Sha1Digest = ServiceModelStringsVersion1.String155; + public const string Sha256Digest = ServiceModelStringsVersion1.String156; + public const string Sha512Digest = ServiceModelStringsVersion1.String157; + public const string TripleDesEncryption = ServiceModelStringsVersion1.String158; + public const string TripleDesKeyWrap = ServiceModelStringsVersion1.String159; + public const string TlsSspiKeyWrap = ServiceModelStringsVersion1.String160; + public const string WindowsSspiKeyWrap = ServiceModelStringsVersion1.String161; + // String constants + public const string StrTransform = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#STR-Transform"; + } + + static class SecurityJan2004Strings + { + // Main dictionary strings + public const string SecurityTokenReference = ServiceModelStringsVersion1.String30; + public const string Namespace = ServiceModelStringsVersion1.String36; + public const string Security = ServiceModelStringsVersion1.String52; + public const string ValueType = ServiceModelStringsVersion1.String58; + public const string TypeAttribute = ServiceModelStringsVersion1.String59; + public const string Prefix = ServiceModelStringsVersion1.String164; + public const string NonceElement = ServiceModelStringsVersion1.String40; + public const string PasswordElement = ServiceModelStringsVersion1.String165; + public const string PasswordTextName = ServiceModelStringsVersion1.String166; + public const string UserNameElement = ServiceModelStringsVersion1.String167; + public const string UserNameTokenElement = ServiceModelStringsVersion1.String168; + public const string BinarySecurityToken = ServiceModelStringsVersion1.String169; + public const string EncodingType = ServiceModelStringsVersion1.String170; + public const string Reference = ServiceModelStringsVersion1.String12; + public const string URI = ServiceModelStringsVersion1.String11; + public const string KeyIdentifier = ServiceModelStringsVersion1.String171; + public const string EncodingTypeValueBase64Binary = ServiceModelStringsVersion1.String172; + public const string EncodingTypeValueHexBinary = ServiceModelStringsVersion1.String173; + public const string EncodingTypeValueText = ServiceModelStringsVersion1.String174; + public const string X509SKIValueType = ServiceModelStringsVersion1.String175; + public const string KerberosTokenTypeGSS = ServiceModelStringsVersion1.String176; + public const string KerberosTokenType1510 = ServiceModelStringsVersion1.String177; + public const string SamlAssertionIdValueType = ServiceModelStringsVersion1.String178; + public const string SamlAssertion = ServiceModelStringsVersion1.String179; + public const string SamlUri = ServiceModelStringsVersion1.String180; + public const string RelAssertionValueType = ServiceModelStringsVersion1.String181; + public const string FailedAuthenticationFaultCode = ServiceModelStringsVersion1.String182; + public const string InvalidSecurityTokenFaultCode = ServiceModelStringsVersion1.String183; + public const string InvalidSecurityFaultCode = ServiceModelStringsVersion1.String184; + public const string KerberosHashValueType = ServiceModelStringsVersion1.String427; + // String constants + public const string SecurityProfileNamespace = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0"; + public const string X509TokenProfileNamespace = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0"; + public const string UPTokenProfileNamespace = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0"; + public const string SamlTokenProfileNamespace = "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.0"; + public const string KerberosTokenProfileNamespace = "http://www.docs.oasis-open.org/wss/2004/07/oasis-000000-wss-kerberos-token-profile-1.0"; + public const string UPTokenType = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#UsernameToken"; + public const string X509TokenType = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3"; + public const string UPTokenPasswordTextValue = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText"; + } + + static class SecurityXXX2005Strings + { + // Main dictionary strings + public const string EncryptedHeader = ServiceModelStringsVersion1.String60; + public const string Namespace = ServiceModelStringsVersion1.String61; + public const string Prefix = ServiceModelStringsVersion1.String185; + public const string SignatureConfirmation = ServiceModelStringsVersion1.String186; + public const string ValueAttribute = ServiceModelStringsVersion1.String77; + public const string TokenTypeAttribute = ServiceModelStringsVersion1.String187; + public const string ThumbprintSha1ValueType = ServiceModelStringsVersion1.String188; + public const string EncryptedKeyTokenType = ServiceModelStringsVersion1.String189; + public const string EncryptedKeyHashValueType = ServiceModelStringsVersion1.String190; + public const string SamlTokenType = ServiceModelStringsVersion1.String191; + public const string Saml20TokenType = ServiceModelStringsVersion1.String192; + public const string Saml11AssertionValueType = ServiceModelStringsVersion1.String193; + // String constants + public const string SecurityProfileNamespace = "http://docs.oasis-open.org/wss/oasis-wss-soap-message-security-1.1"; + public const string SamlTokenProfileNamespace = "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1"; + public const string X509TokenProfileNamespace = "http://docs.oasis-open.org/wss/2004/xx/oasis-2004xx-wss-x509-token-profile-1.1"; + } + + static class SerializationStrings + { + // Main dictionary strings + public const string XmlSchemaInstanceNamespace = ServiceModelStringsVersion1.String441; + public const string XmlSchemaNamespace = ServiceModelStringsVersion1.String442; + public const string Nil = ServiceModelStringsVersion1.String443; + public const string Type = ServiceModelStringsVersion1.String444; + public const string Char = ServiceModelStringsVersion1.String445; + public const string Boolean = ServiceModelStringsVersion1.String446; + public const string Byte = ServiceModelStringsVersion1.String447; + public const string UnsignedByte = ServiceModelStringsVersion1.String448; + public const string Short = ServiceModelStringsVersion1.String449; + public const string UnsignedShort = ServiceModelStringsVersion1.String450; + public const string Int = ServiceModelStringsVersion1.String451; + public const string UnsignedInt = ServiceModelStringsVersion1.String452; + public const string Long = ServiceModelStringsVersion1.String453; + public const string UnsignedLong = ServiceModelStringsVersion1.String454; + public const string Float = ServiceModelStringsVersion1.String455; + public const string Double = ServiceModelStringsVersion1.String456; + public const string Decimal = ServiceModelStringsVersion1.String457; + public const string DateTime = ServiceModelStringsVersion1.String458; + public const string String = ServiceModelStringsVersion1.String459; + public const string Base64Binary = ServiceModelStringsVersion1.String460; + public const string AnyType = ServiceModelStringsVersion1.String461; + public const string Duration = ServiceModelStringsVersion1.String462; + public const string Guid = ServiceModelStringsVersion1.String463; + public const string AnyURI = ServiceModelStringsVersion1.String464; + public const string QName = ServiceModelStringsVersion1.String465; + public const string Time = ServiceModelStringsVersion1.String466; + public const string Date = ServiceModelStringsVersion1.String467; + public const string HexBinary = ServiceModelStringsVersion1.String468; + public const string GYearMonth = ServiceModelStringsVersion1.String469; + public const string GYear = ServiceModelStringsVersion1.String470; + public const string GMonthDay = ServiceModelStringsVersion1.String471; + public const string GDay = ServiceModelStringsVersion1.String472; + public const string GMonth = ServiceModelStringsVersion1.String473; + public const string Integer = ServiceModelStringsVersion1.String474; + public const string PositiveInteger = ServiceModelStringsVersion1.String475; + public const string NegativeInteger = ServiceModelStringsVersion1.String476; + public const string NonPositiveInteger = ServiceModelStringsVersion1.String477; + public const string NonNegativeInteger = ServiceModelStringsVersion1.String478; + public const string NormalizedString = ServiceModelStringsVersion1.String479; + } + + static class TrustStrings + { + } + + static class TrustApr2004Strings + { + // Main dictionary strings + public const string CombinedHashLabel = ServiceModelStringsVersion1.String194; + public const string RequestSecurityTokenResponse = ServiceModelStringsVersion1.String195; + public const string TokenType = ServiceModelStringsVersion1.String187; + public const string KeySize = ServiceModelStringsVersion1.String196; + public const string RequestedTokenReference = ServiceModelStringsVersion1.String197; + public const string AppliesTo = ServiceModelStringsVersion1.String198; + public const string Authenticator = ServiceModelStringsVersion1.String199; + public const string CombinedHash = ServiceModelStringsVersion1.String200; + public const string BinaryExchange = ServiceModelStringsVersion1.String201; + public const string Lifetime = ServiceModelStringsVersion1.String202; + public const string RequestedSecurityToken = ServiceModelStringsVersion1.String203; + public const string Entropy = ServiceModelStringsVersion1.String204; + public const string RequestedProofToken = ServiceModelStringsVersion1.String205; + public const string ComputedKey = ServiceModelStringsVersion1.String206; + public const string RequestSecurityToken = ServiceModelStringsVersion1.String207; + public const string RequestType = ServiceModelStringsVersion1.String208; + public const string RequestSecurityTokenResponseCollection = ServiceModelStringsVersion1.String62; + public const string Context = ServiceModelStringsVersion1.String209; + public const string BinarySecret = ServiceModelStringsVersion1.String210; + public const string Type = ServiceModelStringsVersion1.String59; + public const string SpnegoValueTypeUri = ServiceModelStringsVersion1.String211; + public const string TlsnegoValueTypeUri = ServiceModelStringsVersion1.String212; + public const string Prefix = ServiceModelStringsVersion1.String213; + public const string Namespace = ServiceModelStringsVersion1.String214; + public const string RequestSecurityTokenIssuance = ServiceModelStringsVersion1.String215; + public const string RequestSecurityTokenIssuanceResponse = ServiceModelStringsVersion1.String216; + public const string RequestTypeIssue = ServiceModelStringsVersion1.String217; + public const string Psha1ComputedKeyUri = ServiceModelStringsVersion1.String218; + public const string SymmetricKeyBinarySecret = ServiceModelStringsVersion1.String219; + public const string NonceBinarySecret = ServiceModelStringsVersion1.String220; + public const string KeyType = ServiceModelStringsVersion1.String221; + public const string SymmetricKeyType = ServiceModelStringsVersion1.String222; + public const string PublicKeyType = ServiceModelStringsVersion1.String223; + public const string Claims = ServiceModelStringsVersion1.String224; + public const string InvalidRequestFaultCode = ServiceModelStringsVersion1.String225; + public const string FailedAuthenticationFaultCode = ServiceModelStringsVersion1.String182; + public const string RequestFailedFaultCode = ServiceModelStringsVersion1.String226; + public const string SignWith = ServiceModelStringsVersion1.String227; + public const string EncryptWith = ServiceModelStringsVersion1.String228; + public const string EncryptionAlgorithm = ServiceModelStringsVersion1.String229; + public const string CanonicalizationAlgorithm = ServiceModelStringsVersion1.String230; + public const string ComputedKeyAlgorithm = ServiceModelStringsVersion1.String231; + public const string UseKey = ServiceModelStringsVersion1.String232; + } + + static class TrustFeb2005Strings + { + // Main dictionary strings + public const string RequestSecurityTokenResponseCollection = ServiceModelStringsVersion1.String62; + public const string Namespace = ServiceModelStringsVersion1.String63; + public const string BinarySecretClauseType = ServiceModelStringsVersion1.String64; + public const string CombinedHashLabel = ServiceModelStringsVersion1.String194; + public const string RequestSecurityTokenResponse = ServiceModelStringsVersion1.String195; + public const string TokenType = ServiceModelStringsVersion1.String187; + public const string KeySize = ServiceModelStringsVersion1.String196; + public const string RequestedTokenReference = ServiceModelStringsVersion1.String197; + public const string AppliesTo = ServiceModelStringsVersion1.String198; + public const string Authenticator = ServiceModelStringsVersion1.String199; + public const string CombinedHash = ServiceModelStringsVersion1.String200; + public const string BinaryExchange = ServiceModelStringsVersion1.String201; + public const string Lifetime = ServiceModelStringsVersion1.String202; + public const string RequestedSecurityToken = ServiceModelStringsVersion1.String203; + public const string Entropy = ServiceModelStringsVersion1.String204; + public const string RequestedProofToken = ServiceModelStringsVersion1.String205; + public const string ComputedKey = ServiceModelStringsVersion1.String206; + public const string RequestSecurityToken = ServiceModelStringsVersion1.String207; + public const string RequestType = ServiceModelStringsVersion1.String208; + public const string Context = ServiceModelStringsVersion1.String209; + public const string BinarySecret = ServiceModelStringsVersion1.String210; + public const string Type = ServiceModelStringsVersion1.String59; + public const string SpnegoValueTypeUri = ServiceModelStringsVersion1.String233; + public const string TlsnegoValueTypeUri = ServiceModelStringsVersion1.String234; + public const string Prefix = ServiceModelStringsVersion1.String235; + public const string RequestSecurityTokenIssuance = ServiceModelStringsVersion1.String236; + public const string RequestSecurityTokenIssuanceResponse = ServiceModelStringsVersion1.String237; + public const string RequestTypeIssue = ServiceModelStringsVersion1.String238; + public const string SymmetricKeyBinarySecret = ServiceModelStringsVersion1.String239; + public const string Psha1ComputedKeyUri = ServiceModelStringsVersion1.String240; + public const string NonceBinarySecret = ServiceModelStringsVersion1.String241; + public const string RenewTarget = ServiceModelStringsVersion1.String242; + public const string CloseTarget = ServiceModelStringsVersion1.String243; + public const string RequestedTokenClosed = ServiceModelStringsVersion1.String244; + public const string RequestedAttachedReference = ServiceModelStringsVersion1.String245; + public const string RequestedUnattachedReference = ServiceModelStringsVersion1.String246; + public const string IssuedTokensHeader = ServiceModelStringsVersion1.String247; + public const string RequestTypeRenew = ServiceModelStringsVersion1.String248; + public const string RequestTypeClose = ServiceModelStringsVersion1.String249; + public const string KeyType = ServiceModelStringsVersion1.String221; + public const string SymmetricKeyType = ServiceModelStringsVersion1.String239; + public const string PublicKeyType = ServiceModelStringsVersion1.String250; + public const string Claims = ServiceModelStringsVersion1.String224; + public const string InvalidRequestFaultCode = ServiceModelStringsVersion1.String225; + public const string FailedAuthenticationFaultCode = ServiceModelStringsVersion1.String182; + public const string UseKey = ServiceModelStringsVersion1.String232; + public const string SignWith = ServiceModelStringsVersion1.String227; + public const string EncryptWith = ServiceModelStringsVersion1.String228; + public const string EncryptionAlgorithm = ServiceModelStringsVersion1.String229; + public const string CanonicalizationAlgorithm = ServiceModelStringsVersion1.String230; + public const string ComputedKeyAlgorithm = ServiceModelStringsVersion1.String231; + } + + static class UtilityStrings + { + // Main dictionary strings + public const string IdAttribute = ServiceModelStringsVersion1.String14; + public const string Namespace = ServiceModelStringsVersion1.String51; + public const string Timestamp = ServiceModelStringsVersion1.String53; + public const string CreatedElement = ServiceModelStringsVersion1.String54; + public const string ExpiresElement = ServiceModelStringsVersion1.String55; + public const string Prefix = ServiceModelStringsVersion1.String305; + public const string UniqueEndpointHeaderName = ServiceModelStringsVersion1.String306; + public const string UniqueEndpointHeaderNamespace = ServiceModelStringsVersion1.String307; + } + + static class WsrmFeb2005Strings + { + // Main dictionary strings + public const string Identifier = ServiceModelStringsVersion1.String15; + public const string Namespace = ServiceModelStringsVersion1.String16; + public const string SequenceAcknowledgement = ServiceModelStringsVersion1.String23; + public const string AcknowledgementRange = ServiceModelStringsVersion1.String24; + public const string Upper = ServiceModelStringsVersion1.String25; + public const string Lower = ServiceModelStringsVersion1.String26; + public const string BufferRemaining = ServiceModelStringsVersion1.String27; + public const string NETNamespace = ServiceModelStringsVersion1.String28; + public const string SequenceAcknowledgementAction = ServiceModelStringsVersion1.String29; + public const string Sequence = ServiceModelStringsVersion1.String31; + public const string MessageNumber = ServiceModelStringsVersion1.String32; + public const string AckRequested = ServiceModelStringsVersion1.String328; + public const string AckRequestedAction = ServiceModelStringsVersion1.String329; + public const string AcksTo = ServiceModelStringsVersion1.String330; + public const string Accept = ServiceModelStringsVersion1.String331; + public const string CreateSequence = ServiceModelStringsVersion1.String332; + public const string CreateSequenceAction = ServiceModelStringsVersion1.String333; + public const string CreateSequenceRefused = ServiceModelStringsVersion1.String334; + public const string CreateSequenceResponse = ServiceModelStringsVersion1.String335; + public const string CreateSequenceResponseAction = ServiceModelStringsVersion1.String336; + public const string Expires = ServiceModelStringsVersion1.String55; + public const string FaultCode = ServiceModelStringsVersion1.String337; + public const string InvalidAcknowledgement = ServiceModelStringsVersion1.String338; + public const string LastMessage = ServiceModelStringsVersion1.String339; + public const string LastMessageAction = ServiceModelStringsVersion1.String340; + public const string LastMessageNumberExceeded = ServiceModelStringsVersion1.String341; + public const string MessageNumberRollover = ServiceModelStringsVersion1.String342; + public const string Nack = ServiceModelStringsVersion1.String343; + public const string NETPrefix = ServiceModelStringsVersion1.String344; + public const string Offer = ServiceModelStringsVersion1.String345; + public const string Prefix = ServiceModelStringsVersion1.String346; + public const string SequenceFault = ServiceModelStringsVersion1.String347; + public const string SequenceTerminated = ServiceModelStringsVersion1.String348; + public const string TerminateSequence = ServiceModelStringsVersion1.String349; + public const string TerminateSequenceAction = ServiceModelStringsVersion1.String350; + public const string UnknownSequence = ServiceModelStringsVersion1.String351; + public const string ConnectionLimitReached = ServiceModelStringsVersion1.String480; + } + + static class XmlEncryptionStrings + { + // Main dictionary strings + public const string Namespace = ServiceModelStringsVersion1.String37; + public const string DataReference = ServiceModelStringsVersion1.String46; + public const string EncryptedData = ServiceModelStringsVersion1.String47; + public const string EncryptionMethod = ServiceModelStringsVersion1.String48; + public const string CipherData = ServiceModelStringsVersion1.String49; + public const string CipherValue = ServiceModelStringsVersion1.String50; + public const string ReferenceList = ServiceModelStringsVersion1.String57; + public const string Encoding = ServiceModelStringsVersion1.String308; + public const string MimeType = ServiceModelStringsVersion1.String309; + public const string Type = ServiceModelStringsVersion1.String59; + public const string Id = ServiceModelStringsVersion1.String14; + public const string CarriedKeyName = ServiceModelStringsVersion1.String310; + public const string Recipient = ServiceModelStringsVersion1.String311; + public const string EncryptedKey = ServiceModelStringsVersion1.String312; + public const string URI = ServiceModelStringsVersion1.String11; + public const string KeyReference = ServiceModelStringsVersion1.String313; + public const string Prefix = ServiceModelStringsVersion1.String314; + public const string ElementType = ServiceModelStringsVersion1.String315; + public const string ContentType = ServiceModelStringsVersion1.String316; + public const string AlgorithmAttribute = ServiceModelStringsVersion1.String8; + } + + static class XmlSignatureStrings + { + // Main dictionary strings + public const string Algorithm = ServiceModelStringsVersion1.String8; + public const string URI = ServiceModelStringsVersion1.String11; + public const string Reference = ServiceModelStringsVersion1.String12; + public const string Transforms = ServiceModelStringsVersion1.String17; + public const string Transform = ServiceModelStringsVersion1.String18; + public const string DigestMethod = ServiceModelStringsVersion1.String19; + public const string DigestValue = ServiceModelStringsVersion1.String20; + public const string Namespace = ServiceModelStringsVersion1.String33; + public const string EnvelopedSignature = ServiceModelStringsVersion1.String34; + public const string KeyInfo = ServiceModelStringsVersion1.String35; + public const string Signature = ServiceModelStringsVersion1.String41; + public const string SignedInfo = ServiceModelStringsVersion1.String42; + public const string CanonicalizationMethod = ServiceModelStringsVersion1.String43; + public const string SignatureMethod = ServiceModelStringsVersion1.String44; + public const string SignatureValue = ServiceModelStringsVersion1.String45; + public const string KeyName = ServiceModelStringsVersion1.String317; + public const string Type = ServiceModelStringsVersion1.String59; + public const string MgmtData = ServiceModelStringsVersion1.String318; + public const string Prefix = ServiceModelStringsVersion1.String81; + public const string KeyValue = ServiceModelStringsVersion1.String319; + public const string RsaKeyValue = ServiceModelStringsVersion1.String320; + public const string Modulus = ServiceModelStringsVersion1.String321; + public const string Exponent = ServiceModelStringsVersion1.String322; + public const string X509Data = ServiceModelStringsVersion1.String323; + public const string X509IssuerSerial = ServiceModelStringsVersion1.String324; + public const string X509IssuerName = ServiceModelStringsVersion1.String325; + public const string X509SerialNumber = ServiceModelStringsVersion1.String326; + public const string X509Certificate = ServiceModelStringsVersion1.String327; + // String constants + public const string X509Ski = "X509SKI"; + public const string TransformationParameters = "TransformationParameters"; + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/Xml/XmlBinaryNodeType.cs b/src/CoreWCF.Primitives/src/CoreWCF/Xml/XmlBinaryNodeType.cs new file mode 100644 index 000000000..d1883352a --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/Xml/XmlBinaryNodeType.cs @@ -0,0 +1,272 @@ +namespace CoreWCF.Xml +{ + // Element => StartElement Attribute* Content EndElement + // | StartElement Attribute* Text // Text must be WithEndElement + // | Array StartElement Attribute* EndElement type MB32(Count) byte[Count * sizeof(type)] + + // StartElement => ShortElementNode Name(LocalName) + // | ElementNode Name(Prefix) Name(LocalName) + // | ShortDictionaryElementNode MB32(LocalName) + // | PrefixDictionaryElement[A-Z]Node MB32(LocalName) + // | DictionaryElementNode Name(Prefix) MB32(LocalName) + // | PrefixElement[A-Z]Node Name(LocalName) + + // EndElement => EndElementNode + // Content => (Element | ArrayElement | Text | Comment)* + + // Attribute => ShortAttributeNode Name(LocalName) Text + // | AttributeNode Name(Prefix) Name(LocalName) Text + // | ShortDictionaryAttributeNode MB32(LocalName) Text + // | DictionaryAttributeNode Name(Prefix) MB32(LocalName) Text + // | ShortXmlnsAttributeNode Name(Namespace) + // | XmlnsAttributeNode Name(Prefix) Name(Namespace) + // | ShortDictionaryXmlnsAttributeNode MB32(Namespace) + // | DictionaryXmlnsAttributeNode Name(Prefix) MB32(Namespace) + // | PrefixAttribute[A-Z] Name(LocalName) Text + // | PrefixDictionaryAttribute[A-Z] MB32(LocalName) Text + + // Text => BinaryTextNode + // | CharsTextNode + // | EmptyTextNode + // | DictionaryTextNode MB32(Id) + // | ZeroTextNode + // | OneTextNode + // | TrueTextNode + // | FalseTextNode + // | Int8TextNode Int8 + // | Int16TextNode Int16 + // | Int32TextNode Int32 + // | Int64TextNode Int64 + // | FloatTextNode Float + // | DoubleTextNode Double + // | DecimalTextNode Decimal + // | DateTimeTextNode DateTime + // | StartListNode Text* EndListNode // Restriction: Cannot nest ListNode + // | UniqueIdTextNode byte[16] // byte[16] is a Guid (from Guid.ToBinary()) (urn:uuid:xxxx-xxxx-xxx) + // | GuidTextNode byte[16] // byte[16] is a Guid (from Guid.ToBinary()) (xxxx-xxxx-xxx) + // | TimeSpanNode Int64 + // | UInt64TextNode UInt64 + // | BoolTextNode Int8 + // BinaryText => Bytes8TextNode UInt8 byte* + // | Bytes16TextNode UInt16 byte* + // | Bytes32TextNode UInt31 byte* + // CharsText => Chars8TextNode UInt8 byte* // UTF8Chars + // | Chars16TextNode UInt16 byte* + // | Chars32TextNode Unt31 byte* + // | UnicodeChars8TextNode UInt8 char* + // | UnicodeChars16TextNode UInt16 char* + // | UnicodeChars32TextNode UInt31 char* + // | QNameDictionaryTextNode UInt8 MB32(LocalName) // UInt8 0-25 => 'a'-'z' + // Comment => CommentNode Name(Text) + // Name => MB32 byte* // Length, UTF8Chars + // MB32(x:x>=0x80) => byte(0x80 | (x & 0x7F)) MB32(x >> 7) + // MB32(x:x<0x80) => byte(x) + + + // In order to help differentiate text from binary (where someone mixes up format and implementation) we overlay binary + // nodes that are illegal to start a document with text characters that are legal to start a document. Specifically these values are: + // ' ' = 32 + // '\t' = 9 + // '\n' = 10 + // '\r' = 13 + // '<' = 60 + // The attribute nodes (MinAttribute to MaxAttribute) overlay all of these values and are invalid as the first byte of the document + + internal enum XmlBinaryNodeType + { + // ProcessingInstruction = 0, // Reserved (Not supported) + EndElement = 1, + Comment = 2, + Array = 3, + + MinAttribute = Array + 1, + ShortAttribute = MinAttribute + 0, + Attribute = MinAttribute + 1, + ShortDictionaryAttribute = MinAttribute + 2, + DictionaryAttribute = MinAttribute + 3, + ShortXmlnsAttribute = MinAttribute + 4, + XmlnsAttribute = MinAttribute + 5, + ShortDictionaryXmlnsAttribute = MinAttribute + 6, + DictionaryXmlnsAttribute = MinAttribute + 7, + PrefixDictionaryAttributeA = MinAttribute + 8, + PrefixDictionaryAttributeB = PrefixDictionaryAttributeA + 1, + PrefixDictionaryAttributeC = PrefixDictionaryAttributeB + 1, + PrefixDictionaryAttributeD = PrefixDictionaryAttributeC + 1, + PrefixDictionaryAttributeE = PrefixDictionaryAttributeD + 1, + PrefixDictionaryAttributeF = PrefixDictionaryAttributeE + 1, + PrefixDictionaryAttributeG = PrefixDictionaryAttributeF + 1, + PrefixDictionaryAttributeH = PrefixDictionaryAttributeG + 1, + PrefixDictionaryAttributeI = PrefixDictionaryAttributeH + 1, + PrefixDictionaryAttributeJ = PrefixDictionaryAttributeI + 1, + PrefixDictionaryAttributeK = PrefixDictionaryAttributeJ + 1, + PrefixDictionaryAttributeL = PrefixDictionaryAttributeK + 1, + PrefixDictionaryAttributeM = PrefixDictionaryAttributeL + 1, + PrefixDictionaryAttributeN = PrefixDictionaryAttributeM + 1, + PrefixDictionaryAttributeO = PrefixDictionaryAttributeN + 1, + PrefixDictionaryAttributeP = PrefixDictionaryAttributeO + 1, + PrefixDictionaryAttributeQ = PrefixDictionaryAttributeP + 1, + PrefixDictionaryAttributeR = PrefixDictionaryAttributeQ + 1, + PrefixDictionaryAttributeS = PrefixDictionaryAttributeR + 1, + PrefixDictionaryAttributeT = PrefixDictionaryAttributeS + 1, + PrefixDictionaryAttributeU = PrefixDictionaryAttributeT + 1, + PrefixDictionaryAttributeV = PrefixDictionaryAttributeU + 1, + PrefixDictionaryAttributeW = PrefixDictionaryAttributeV + 1, + PrefixDictionaryAttributeX = PrefixDictionaryAttributeW + 1, + PrefixDictionaryAttributeY = PrefixDictionaryAttributeX + 1, + PrefixDictionaryAttributeZ = PrefixDictionaryAttributeY + 1, + PrefixAttributeA = PrefixDictionaryAttributeZ + 1, + PrefixAttributeB = PrefixAttributeA + 1, + PrefixAttributeC = PrefixAttributeB + 1, + PrefixAttributeD = PrefixAttributeC + 1, + PrefixAttributeE = PrefixAttributeD + 1, + PrefixAttributeF = PrefixAttributeE + 1, + PrefixAttributeG = PrefixAttributeF + 1, + PrefixAttributeH = PrefixAttributeG + 1, + PrefixAttributeI = PrefixAttributeH + 1, + PrefixAttributeJ = PrefixAttributeI + 1, + PrefixAttributeK = PrefixAttributeJ + 1, + PrefixAttributeL = PrefixAttributeK + 1, + PrefixAttributeM = PrefixAttributeL + 1, + PrefixAttributeN = PrefixAttributeM + 1, + PrefixAttributeO = PrefixAttributeN + 1, + PrefixAttributeP = PrefixAttributeO + 1, + PrefixAttributeQ = PrefixAttributeP + 1, + PrefixAttributeR = PrefixAttributeQ + 1, + PrefixAttributeS = PrefixAttributeR + 1, + PrefixAttributeT = PrefixAttributeS + 1, + PrefixAttributeU = PrefixAttributeT + 1, + PrefixAttributeV = PrefixAttributeU + 1, + PrefixAttributeW = PrefixAttributeV + 1, + PrefixAttributeX = PrefixAttributeW + 1, + PrefixAttributeY = PrefixAttributeX + 1, + PrefixAttributeZ = PrefixAttributeY + 1, + MaxAttribute = PrefixAttributeZ, + + MinElement = MaxAttribute + 1, + ShortElement = MinElement, + Element = MinElement + 1, + ShortDictionaryElement = MinElement + 2, + DictionaryElement = MinElement + 3, + PrefixDictionaryElementA = MinElement + 4, + PrefixDictionaryElementB = PrefixDictionaryElementA + 1, + PrefixDictionaryElementC = PrefixDictionaryElementB + 1, + PrefixDictionaryElementD = PrefixDictionaryElementC + 1, + PrefixDictionaryElementE = PrefixDictionaryElementD + 1, + PrefixDictionaryElementF = PrefixDictionaryElementE + 1, + PrefixDictionaryElementG = PrefixDictionaryElementF + 1, + PrefixDictionaryElementH = PrefixDictionaryElementG + 1, + PrefixDictionaryElementI = PrefixDictionaryElementH + 1, + PrefixDictionaryElementJ = PrefixDictionaryElementI + 1, + PrefixDictionaryElementK = PrefixDictionaryElementJ + 1, + PrefixDictionaryElementL = PrefixDictionaryElementK + 1, + PrefixDictionaryElementM = PrefixDictionaryElementL + 1, + PrefixDictionaryElementN = PrefixDictionaryElementM + 1, + PrefixDictionaryElementO = PrefixDictionaryElementN + 1, + PrefixDictionaryElementP = PrefixDictionaryElementO + 1, + PrefixDictionaryElementQ = PrefixDictionaryElementP + 1, + PrefixDictionaryElementR = PrefixDictionaryElementQ + 1, + PrefixDictionaryElementS = PrefixDictionaryElementR + 1, + PrefixDictionaryElementT = PrefixDictionaryElementS + 1, + PrefixDictionaryElementU = PrefixDictionaryElementT + 1, + PrefixDictionaryElementV = PrefixDictionaryElementU + 1, + PrefixDictionaryElementW = PrefixDictionaryElementV + 1, + PrefixDictionaryElementX = PrefixDictionaryElementW + 1, + PrefixDictionaryElementY = PrefixDictionaryElementX + 1, + PrefixDictionaryElementZ = PrefixDictionaryElementY + 1, + PrefixElementA = PrefixDictionaryElementZ + 1, + PrefixElementB = PrefixElementA + 1, + PrefixElementC = PrefixElementB + 1, + PrefixElementD = PrefixElementC + 1, + PrefixElementE = PrefixElementD + 1, + PrefixElementF = PrefixElementE + 1, + PrefixElementG = PrefixElementF + 1, + PrefixElementH = PrefixElementG + 1, + PrefixElementI = PrefixElementH + 1, + PrefixElementJ = PrefixElementI + 1, + PrefixElementK = PrefixElementJ + 1, + PrefixElementL = PrefixElementK + 1, + PrefixElementM = PrefixElementL + 1, + PrefixElementN = PrefixElementM + 1, + PrefixElementO = PrefixElementN + 1, + PrefixElementP = PrefixElementO + 1, + PrefixElementQ = PrefixElementP + 1, + PrefixElementR = PrefixElementQ + 1, + PrefixElementS = PrefixElementR + 1, + PrefixElementT = PrefixElementS + 1, + PrefixElementU = PrefixElementT + 1, + PrefixElementV = PrefixElementU + 1, + PrefixElementW = PrefixElementV + 1, + PrefixElementX = PrefixElementW + 1, + PrefixElementY = PrefixElementX + 1, + PrefixElementZ = PrefixElementY + 1, + MaxElement = PrefixElementZ, + + // MinorVersion = MaxElement + 1, // Reserved (Not supported) + + MinText = 0x80, // Must be even + ZeroText = MinText, + OneText = MinText + 1 * 2, + FalseText = MinText + 2 * 2, + TrueText = MinText + 3 * 2, + Int8Text = MinText + 4 * 2, + Int16Text = MinText + 5 * 2, + Int32Text = MinText + 6 * 2, + Int64Text = MinText + 7 * 2, + FloatText = MinText + 8 * 2, + DoubleText = MinText + 9 * 2, + DecimalText = MinText + 10 * 2, + DateTimeText = MinText + 11 * 2, + Chars8Text = MinText + 12 * 2, + Chars16Text = MinText + 13 * 2, + Chars32Text = MinText + 14 * 2, + Bytes8Text = MinText + 15 * 2, + Bytes16Text = MinText + 16 * 2, + Bytes32Text = MinText + 17 * 2, + StartListText = MinText + 18 * 2, + EndListText = MinText + 19 * 2, + EmptyText = MinText + 20 * 2, + DictionaryText = MinText + 21 * 2, + UniqueIdText = MinText + 22 * 2, + TimeSpanText = MinText + 23 * 2, + GuidText = MinText + 24 * 2, + UInt64Text = MinText + 25 * 2, + BoolText = MinText + 26 * 2, + UnicodeChars8Text = MinText + 27 * 2, + UnicodeChars16Text = MinText + 28 * 2, + UnicodeChars32Text = MinText + 29 * 2, + QNameDictionaryText = MinText + 30 * 2, + + ZeroTextWithEndElement = ZeroText + 1, + OneTextWithEndElement = OneText + 1, + FalseTextWithEndElement = FalseText + 1, + TrueTextWithEndElement = TrueText + 1, + Int8TextWithEndElement = Int8Text + 1, + Int16TextWithEndElement = Int16Text + 1, + Int32TextWithEndElement = Int32Text + 1, + Int64TextWithEndElement = Int64Text + 1, + FloatTextWithEndElement = FloatText + 1, + DoubleTextWithEndElement = DoubleText + 1, + DecimalTextWithEndElement = DecimalText + 1, + DateTimeTextWithEndElement = DateTimeText + 1, + Chars8TextWithEndElement = Chars8Text + 1, + Chars16TextWithEndElement = Chars16Text + 1, + Chars32TextWithEndElement = Chars32Text + 1, + Bytes8TextWithEndElement = Bytes8Text + 1, + Bytes16TextWithEndElement = Bytes16Text + 1, + Bytes32TextWithEndElement = Bytes32Text + 1, + StartListTextWithEndElement = StartListText + 1, + EndListTextWithEndElement = EndListText + 1, + EmptyTextWithEndElement = EmptyText + 1, + DictionaryTextWithEndElement = DictionaryText + 1, + UniqueIdTextWithEndElement = UniqueIdText + 1, + TimeSpanTextWithEndElement = TimeSpanText + 1, + GuidTextWithEndElement = GuidText + 1, + UInt64TextWithEndElement = UInt64Text + 1, + BoolTextWithEndElement = BoolText + 1, + UnicodeChars8TextWithEndElement = UnicodeChars8Text + 1, + UnicodeChars16TextWithEndElement = UnicodeChars16Text + 1, + UnicodeChars32TextWithEndElement = UnicodeChars32Text + 1, + QNameDictionaryTextWithEndElement = QNameDictionaryText + 1, + MaxText = QNameDictionaryTextWithEndElement + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/XmlBuffer.cs b/src/CoreWCF.Primitives/src/CoreWCF/XmlBuffer.cs new file mode 100644 index 000000000..63bd6010e --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/XmlBuffer.cs @@ -0,0 +1,161 @@ +using System; +using System.Collections.Generic; +using System.Xml; +using CoreWCF.Runtime; +using CoreWCF.Channels; + +namespace CoreWCF +{ + internal class XmlBuffer + { + List
_sections; + byte[] _buffer; + int _offset; + BufferedOutputStream _stream; + BufferState _bufferState; + XmlDictionaryWriter _writer; + XmlDictionaryReaderQuotas _quotas; + + enum BufferState + { + Created, + Writing, + Reading, + } + + struct Section + { + int _offset; + int _size; + XmlDictionaryReaderQuotas _quotas; + + public Section(int offset, int size, XmlDictionaryReaderQuotas quotas) + { + _offset = offset; + _size = size; + _quotas = quotas; + } + + public int Offset + { + get { return _offset; } + } + + public int Size + { + get { return _size; } + } + + public XmlDictionaryReaderQuotas Quotas + { + get { return _quotas; } + } + } + + public XmlBuffer(int maxBufferSize) + { + if (maxBufferSize < 0) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(maxBufferSize), maxBufferSize, + SR.ValueMustBeNonNegative)); + int initialBufferSize = Math.Min(512, maxBufferSize); + _stream = new BufferManagerOutputStream(SR.XmlBufferQuotaExceeded, initialBufferSize, maxBufferSize, + BufferManager.CreateBufferManager(0, int.MaxValue)); + _sections = new List
(1); + } + + public int BufferSize + { + get + { + Fx.Assert(_bufferState == BufferState.Reading, "Buffer size shuold only be retrieved during Reading state"); + return _buffer.Length; + } + } + + public int SectionCount + { + get { return _sections.Count; } + } + + public XmlDictionaryWriter OpenSection(XmlDictionaryReaderQuotas quotas) + { + if (_bufferState != BufferState.Created) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateInvalidStateException()); + _bufferState = BufferState.Writing; + _quotas = new XmlDictionaryReaderQuotas(); + quotas.CopyTo(_quotas); + if (_writer != null) + + { + + // We always want to Dispose of the writer now; previously, writers could be reassigned + + // to a new stream, with a new dictionary and session. + + var thisWriter = _writer; + + thisWriter.Dispose(); + + _writer = null; + + } + + _writer = XmlDictionaryWriter.CreateBinaryWriter(_stream, XD.Dictionary, null, true); + return _writer; + } + + public void CloseSection() + { + if (_bufferState != BufferState.Writing) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateInvalidStateException()); + _writer.Dispose(); + _writer = null; + _bufferState = BufferState.Created; + int size = (int)_stream.Length - _offset; + _sections.Add(new Section(_offset, size, _quotas)); + _offset += size; + } + + public void Close() + { + if (_bufferState != BufferState.Created) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateInvalidStateException()); + _bufferState = BufferState.Reading; + int bufferSize; + _buffer = _stream.ToArray(out bufferSize); + _writer = null; + _stream = null; + } + + Exception CreateInvalidStateException() + { + return new InvalidOperationException(SR.XmlBufferInInvalidState); + } + + public XmlDictionaryReader GetReader(int sectionIndex) + { + if (_bufferState != BufferState.Reading) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateInvalidStateException()); + Section section = _sections[sectionIndex]; + XmlDictionaryReader reader = XmlDictionaryReader.CreateBinaryReader(_buffer, section.Offset, section.Size, XD.Dictionary, section.Quotas); + reader.MoveToContent(); + return reader; + } + + public void WriteTo(int sectionIndex, XmlWriter writer) + { + if (_bufferState != BufferState.Reading) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateInvalidStateException()); + XmlDictionaryReader reader = GetReader(sectionIndex); + try + { + writer.WriteNode(reader, false); + } + finally + { + reader.Dispose(); + } + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/XmlReaderExtensions.cs b/src/CoreWCF.Primitives/src/CoreWCF/XmlReaderExtensions.cs new file mode 100644 index 000000000..ca73eee63 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/XmlReaderExtensions.cs @@ -0,0 +1,31 @@ +using System; +using System.Xml; + +namespace CoreWCF +{ + + internal static class XmlReaderExtensions + { + internal static string ReadElementString(this XmlReader reader) + { + if (reader.MoveToContent() != XmlNodeType.Element) + { + var lineInfo = reader as IXmlLineInfo; + throw new XmlException(SR.Format(SR.Xml_InvalidNodeType, reader.NodeType.ToString()), null, lineInfo?.LineNumber ?? 0, lineInfo?.LinePosition ?? 0); + } + + return reader.ReadElementContentAsString(); + } + + internal static string ReadElementString(this XmlReader reader, string localname, string ns) + { + if (reader.MoveToContent() != XmlNodeType.Element) + { + var lineInfo = reader as IXmlLineInfo; + throw new XmlException(SR.Format(SR.Xml_InvalidNodeType, reader.NodeType.ToString()), null, lineInfo?.LineNumber ?? 0, lineInfo?.LinePosition ?? 0); + } + + return reader.ReadElementContentAsString(localname, ns); + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/XmlSerializerFormatAttribute.cs b/src/CoreWCF.Primitives/src/CoreWCF/XmlSerializerFormatAttribute.cs new file mode 100644 index 000000000..4c38519d6 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/XmlSerializerFormatAttribute.cs @@ -0,0 +1,64 @@ +using System; + +namespace CoreWCF +{ + internal sealed class XmlSerializerFormatAttribute : Attribute + { + bool supportFaults = false; + OperationFormatStyle style; + bool isStyleSet; + OperationFormatUse use; + + public bool SupportFaults + { + get { return supportFaults; } + set { supportFaults = value; } + } + + public OperationFormatStyle Style + { + get { return style; } + set + { + ValidateOperationFormatStyle(value); + style = value; + isStyleSet = true; + } + } + + public OperationFormatUse Use + { + get { return use; } + set + { + ValidateOperationFormatUse(value); + use = value; + if (!isStyleSet && IsEncoded) + Style = OperationFormatStyle.Rpc; + } + } + + internal bool IsEncoded + { + get { return use == OperationFormatUse.Encoded; } + set { use = value ? OperationFormatUse.Encoded : OperationFormatUse.Literal; } + } + + static internal void ValidateOperationFormatStyle(OperationFormatStyle value) + { + if (!OperationFormatStyleHelper.IsDefined(value)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(value))); + } + } + + static internal void ValidateOperationFormatUse(OperationFormatUse value) + { + if (!OperationFormatUseHelper.IsDefined(value)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(value))); + } + } + } + +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/CoreWCF/XmlUtil.cs b/src/CoreWCF.Primitives/src/CoreWCF/XmlUtil.cs new file mode 100644 index 000000000..72b1ed846 --- /dev/null +++ b/src/CoreWCF.Primitives/src/CoreWCF/XmlUtil.cs @@ -0,0 +1,141 @@ +using System; +using System.Globalization; +using System.Xml; +using CoreWCF.Runtime; + +namespace CoreWCF +{ + internal static class XmlUtil + { + public const string XmlNs = "http://www.w3.org/XML/1998/namespace"; + public const string XmlNsNs = "http://www.w3.org/2000/xmlns/"; + public const string XmlSerializerSchemaInstanceNamespace = "http://www.w3.org/2001/XMLSchema-instance"; + public const string XmlSerializerSchemaNamespace = "http://www.w3.org/2001/XMLSchema"; + + public static string GetXmlLangAttribute(XmlReader reader) + { + string xmlLang = null; + if (reader.MoveToAttribute("lang", XmlNs)) + { + xmlLang = reader.Value; + reader.MoveToElement(); + } + + if (xmlLang == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.XmlLangAttributeMissing)); + + return xmlLang; + } + + public static void ReadContentAsQName(XmlReader reader, out string localName, out string ns) + { + ParseQName(reader, reader.ReadContentAsString(), out localName, out ns); + } + + public static bool IsWhitespace(char ch) + { + return (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n'); + } + + public static string TrimEnd(string s) + { + int i; + for (i = s.Length; i > 0 && IsWhitespace(s[i - 1]); i--) ; + + if (i != s.Length) + { + return s.Substring(0, i); + } + + return s; + } + + public static string TrimStart(string s) + { + int i; + for (i = 0; i < s.Length && IsWhitespace(s[i]); i++) ; + + if (i != 0) + { + return s.Substring(i); + } + + return s; + } + + public static string Trim(string s) + { + int i; + for (i = 0; i < s.Length && IsWhitespace(s[i]); i++) ; + + if (i >= s.Length) + { + return string.Empty; + } + + int j; + for (j = s.Length; j > 0 && IsWhitespace(s[j - 1]); j--) ; + + Fx.Assert(j > i, "Logic error in XmlUtil.Trim()."); + + if (i != 0 || j != s.Length) + { + return s.Substring(i, j - i); + } + return s; + } + + public static void ParseQName(XmlReader reader, string qname, out string localName, out string ns) + { + int index = qname.IndexOf(':'); + string prefix; + if (index < 0) + { + prefix = ""; + localName = TrimStart(TrimEnd(qname)); + } + else + { + if (index == qname.Length - 1) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.Format(SR.InvalidXmlQualifiedName, qname))); + prefix = TrimStart(qname.Substring(0, index)); + localName = TrimEnd(qname.Substring(index + 1)); + } + ns = reader.LookupNamespace(prefix); + if (ns == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.Format(SR.UnboundPrefixInQName, qname))); + } + + // This code was copied from XmlDictionaryReader.ReadElementContentAsDateTime which is an internal method + public static DateTime ReadElementContentAsDateTime(this XmlDictionaryReader reader) + { + bool isEmptyElement = reader.IsStartElement() && reader.IsEmptyElement; + DateTime value; + + if (isEmptyElement) + { + reader.Read(); + try + { + value = DateTime.Parse(string.Empty, NumberFormatInfo.InvariantInfo); + } + catch (ArgumentException exception) + { + throw new XmlException(SR.Format(SR.XmlInvalidConversion, string.Empty, "DateTime"), exception); + } + catch (FormatException exception) + { + throw new XmlException(SR.Format(SR.XmlInvalidConversion, string.Empty, "DateTime"), exception); + } + } + else + { + reader.ReadStartElement(); + value = reader.ReadContentAsDateTimeOffset().DateTime; + reader.ReadEndElement(); + } + + return value; + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/src/Resources/Strings.resx b/src/CoreWCF.Primitives/src/Resources/Strings.resx new file mode 100644 index 000000000..b5ac05e2f --- /dev/null +++ b/src/CoreWCF.Primitives/src/Resources/Strings.resx @@ -0,0 +1,7987 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + No IPEndpoints were found for host {0}. + + + No DNS entries exist for host {0}. + + + Attribute '{0}' is required on element '{1}'. + + + Crypto algorithm {0} not supported in this context. + + + The custom crypto algorithm '{0}' obtained using CryptoConfig is not a valid or supported hash algorithm. + + + The client credential entered was invalid. + + + Either the client credential was invalid or there was an error collecting the client credentials by the SSPI. + + + The custom crypto algorithm '{0}' obtained using CryptoConfig is not a valid or supported asymmetric signature algorithm. + + + The security token serializer must be specified on the security token provider. + + + The key length '{0}' is not a multiple of 8 for symmetric keys. + + + The channel behaviors configured for the issuer address '{0}' cannot contain a behavior of type '{1}'. + + + Operation Action={0} + + + The security token manager cannot create a token provider for requirement '{0}'. + + + The security token manager cannot create a token authenticator for requirement '{0}'. + + + The signature verification failed. Please see inner exception for fault details. + + + The security token manager cannot create a token serializer for security token version '{0}'. + + + The supporting signature is not signed with a derived key. The binding's supporting token parameter '{0}' requires key derivation. + + + The primary signature is not signed with a derived key. The binding's primary token parameter '{0}' requires key derivation. + + + The primary signature is not signed with a key derived from the encrypted key. The binding's token parameter '{0}' requires key derivation. + + + The message is not encrypted with a key derived from the encrypted key. The binding's token parameter '{0}' requires key derivation. + + + The DataProtectionSecurityStateEncoder is unable to decode the byte array. Ensure that a 'UserProfile' is loaded, if this is a 'web farm scenario' ensure all servers are running as the same user with the roaming profiles or provide a custom SecurityStateEncoder'. + + + The DataProtectionSecurityStateEncoder is unable to encode the byte array. Ensure that a 'UserProfile' is loaded, if this is a 'web farm scenario' ensure all servers are running as the same user with the roaming profiles or provide a custom SecurityStateEncoder'. + + + The message is not encrypted with a key derived from the encryption token. The binding's token parameter '{0}' requires key derivation. + + + The security token manager requires the security binding element to be specified in order to create a token authenticator for requirement '{0}'. + + + The security token manager requires the security binding element to be specified in order to create a token provider for requirement '{0}'. + + + The security session received an unexpected close response from the other party. + + + The security session received an unexpected close from the other party. + + + The service was unable to verify the cipher strengths negotiated as part of the SSL handshake. + + + SecurityVersion.WSSecurityJan2004 does not support header encryption. Header with name '{0}' and namespace '{1}' is configured for encryption. Consider using SecurityVersion.WsSecurity11 and above or use transport security to encrypt the full message. + + + The Header ('{0}', '{1}') was encrypted but not signed. All encrypted headers outside the security header should be signed. + + + Unable to obtain XmlDictionaryReaderQuotas from the Binding. If you have specified a custom EncodingBindingElement, verify that the EncodingBindingElement can handle XmlDictionaryReaderQuotas in its GetProperty<T>() method. + + + SecurityVersion.WSSecurityJan2004 does not support header decryption. Use SecurityVersion.WsSecurity11 and above or use transport security to encrypt the full message. + + + Unable to decrypt an encrypted data block. Please verify that the encryption algorithm and keys used by the sender and receiver match. + + + The authenticate method in the ServiceAuthenticationManager returned null. If you do not want to return any authorization policies in the collection then return an empty ReadOnlyCollection instead. + + + There was an error serializing the security token. Please see the inner exception for more details. + + + There was an error creating the security key identifier clause from the security token XML. Please see the inner exception for more details. + + + There was an error deserializing the security token XML. Please see the inner exception for more details. + + + The token requirement '{0}' does not specify the target address. This is required by the token manager for creating the corresponding security token provider. + + + The derived key has not been computed for the security token. + + + The binding ('{0}', '{1}') has been configured with a security algorithm suite '{2}' that is incompatible with the issued token key size '{3}' specified on the binding. + + + The IssuedToken security authentication mode requires the issued token to contain a symmetric key. + + + The binding ('{0}', '{1}') uses an Issued Token with Bearer Key Type in a invalid context. The Issued Token with a Bearer Key Type can only be used as a Signed Supporting token or a Signed Encrypted Supporting token. See the SecurityBindingElement.EndpointSupportingTokenParameters property. + + + Policy for multiple issuer endpoints was retrieved from '{0}' but the relying party's policy does not specify which issuer endpoint to use. One of the endpoints was selected as the issuer endpoint to use. If you are using svcutil, the other endpoints will be available in commented form in the configuration as <alternativeIssuedTokenParameters>. Check the configuration to ensure that the right issuer endpoint was selected. + + + The AuthenticationManager cannot be added to the binding parameters because the binding parameters already contains a AuthenticationManager '{0}'. If you are configuring a custom AuthenticationManager for the service, please first remove any existing AuthenticationManagers from the behaviors collection before adding the custom AuthenticationManager. + + + The AuthenticationSchemes cannot be added to the binding parameters because the binding parameters already contains AuthenticationSchemes '{0}'. If you are configuring custom AuthenticationSchemes for the service, please first remove any existing AuthenticationSchemes from the behaviors collection before adding custom AuthenticationSchemes. + + + Unable to find a SecurityBindingElement. + + + The ServiceCredentials cannot be added to the binding parameters because the binding parameters already contains a SecurityCredentialsManager '{0}'. If you are configuring custom credentials for the service, please first remove any existing ServiceCredentials from the behaviors collection before adding the custom credential. + + + The ClientCredentials cannot be added to the binding parameters because the binding parameters already contains a SecurityCredentialsManager '{0}'. If you are configuring custom credentials for the channel, please first remove any existing ClientCredentials from the behaviors collection before adding the custom credential. + + + The binding ('{0}', '{1}') has been configured with a MutualCertificateDuplexBindingElement that requires a client certificate. The client certificate is currently missing. + + + The binding ('{0}', '{1}') is configured with a security token parameter '{2}' that has an incompatible security token inclusion mode '{3}'. Specify an alternate security token inclusion mode (for example, '{4}'). + + + Unable to create a bi-directional (request-reply or duplex) channel for security negotiation. Please ensure that the binding is capable of creating a bi-directional channel. + + + There are too many active security negotiations or secure conversations at the service. Please retry later. + + + There are too many pending secure conversations on the server. Please retry later. + + + The RequestSecurityToken message does not match the endpoint filters the service '{0}' is expecting incoming messages to match. This may be because the RequestSecurityToken was intended to be sent to a different service. + + + The security session requires a security token authenticator that implements '{0}'. '{1}' does not implement '{0}'. + + + The security session requires a security token resolver that implements '{1}'. The security token resolver '{0}' does not implement '{1}'. + + + The session security token authenticator returned a token of type '{0}'. The token type expected is '{1}'. + + + The session security token provider returned a token of type '{0}'. The token type expected is '{1}'. + + + The security standards manager was not specified on '{0}'. + + + The security negotiation message with action '{0}' is larger than the maximum allowed buffer size '{1}'. If you are using a streamed transport consider increasing the maximum buffer size on the transport. + + + The channel demuxer Open failed previously with exception '{0}'. + + + The security channel listener was not specified on '{0}'. + + + ExtendedProtectionPolicy specified a PolicyEnforcement of 'Always' which is not supported for the authentication mode requested. This prevents the ExtendedProtectionPolicy from being enforced. For StandardBindings use a SecurityMode of TransportWithMessageCredential and a ClientCredential type of Windows. For CustomBindings use SspiNegotiationOverTransport or KerberosOverTransport. Alternatively, specify a PolicyEnforcement of 'Never'. + + + ExtendedProtectionPolicy specified a PolicyEnforcement of 'Always' and a ChannelBinding was not found. This prevents the ExtendedProtectionPolicy from being enforced. Change the binding to make a ChannelBinding available, for StandardBindings use a SecurityMode of TransportWithMessageCredential and a ClientCredential type of Windows. For CustomBindings use SspiNegotiationOverTransport or KerberosOverTransport. Alternatively, specify a PolicyEnforcement of 'Never'. + + + The security settings lifetime manager was not specified on '{0}'. + + + The listener is not accepting new secure conversations because it is closing. + + + The server is not accepting new secure conversations currently because it is closing. Please retry later. + + + The cipher key negotiated by SSL is too small ('{0}' bits). Keys of such lengths are not allowed as they may result in information disclosure. Please configure the initiator machine to negotiate SSL cipher keys that are '{1}' bits or longer. + + + The length ('{0}' bytes) of the derived key's Nonce exceeds the maximum length ('{1}' bytes) allowed. + + + The length ('{0}' bytes) of the derived key's Label exceeds the maximum length ('{1}' bytes) allowed. + + + The derived key's Offset ('{0}' bytes) exceeds the maximum offset ('{1}' bytes) allowed. + + + The derived key's generation ('{0}') and length ('{1}' bytes) result in a key derivation offset that is greater than the maximum offset ('{2}' bytes) allowed. + + + The number of derived keys in the message has exceeded the maximum allowed number '{0}'. + + + The number of encrypted keys in the message has exceeded the maximum allowed number '{0}'. + + + Unable to finish reading Base64 data as the given buffer quota has been exceeded. Buffer quota: {0}. Consider increasing the MaxReceivedMessageSize quota on the TransportBindingElement. Please note that a very high value for MaxReceivedMessageSize will result in buffering a large message and might open the system to DOS attacks. + + + Manual addressing is not supported with message level security. Configure the binding ('{0}', '{1}') to use transport security or to not do manual addressing. + + + The target service address was not specified on '{0}'. + + + The issued token cache was not specified on '{0}'. + + + The security algorithm suite was not specified on '{0}'. + + + A security token ('{0}', '{1}') was found outside the security header. The message may have been altered in transit. + + + The SecurityTokenProvider '{0}' could not resolve the token. + + + A secure conversation cancellation is not allowed by the binding. + + + The security binding element for bootstrap security was not specified on '{0}'. + + + The context for building the issuer channel was not specified on '{0}'. + + + The binding to use to communicate to the federation service at '{0}' is not specified. + + + It is likely that certificate '{0}' may not have a private key that is capable of key exchange or the process may not have access rights for the private key. Please see inner exception for detail. + + + The certificate '{0}' must have a private key. The process must have access rights for the private key. + + + No outgoing EndpointAddress is available to check the identity on a message to be sent. + + + No outgoing EndpointAddress is available to check the identity on a received reply. + + + No signing token is available to do an incoming identity check. + + + The PSHA1 key length '{0}' is invalid. + + + Clone() was not implemented properly by '{0}'. The cloned object was '{1}'. + + + The issued token is of unexpected type '{0}'. Expected token type '{1}'. + + + The service operation '{0}' that belongs to the contract with the '{1}' name and the '{2}' namespace does not allow impersonation. + + + The RequestSecurityTokenResponse has multiple RequestedSecurityToken elements. + + + The RequestSecurityTokenResponse has multiple RequestedProofToken elements. + + + The proof token XML element is not expected in the response. + + + The key length '{0}' requested is invalid. + + + The security token parameters to use for the issued token are not set on '{0}'. + + + The message could not be processed because the action '{0}' is invalid or unrecognized. + + + Token inclusion mode '{0}' is not supported. + + + The policy to import a process cannot import a binding for contract ({0},{1}). The protection requirements for the binding are not compatible with a binding already imported for the contract. You must reconfigure the binding. + + + The symmetric security protocol can either be configured with a symmetric token provider and a symmetric token authenticator or an asymmetric token provider. It cannot be configured with both. + + + ClientCredentialType.None is not valid for the TransportWithMessageCredential security mode. Specify a message credential type or use a different security mode. + + + The security session id '{0}' is already present in the filter table. + + + A supporting token that satisfies parameters '{0}' and attachment mode '{1}' was not provided. + + + The supporting token provided for parameters '{0}' did not endorse the primary signature. + + + The supporting token provided for parameters '{0}' was not signed as part of the primary signature. + + + The supporting token provided for parameters '{0}' was not encrypted. + + + A basic token is not expected in the security header in this context. + + + The request for security token could not be satisfied because authentication failed. + + + The caller was not authenticated by the service. + + + The request for security token has invalid or malformed elements. + + + A signed supporting token is not expected in the security header in this context. + + + Security token parameters must be specified with supporting tokens for each message. + + + The signature token '{0}' is not the same token as the encryption token '{1}'. + + + The reverting operation failed with the exception '{0}'. + + + Unrecognized supporting token '{0}' was encountered. + + + More than one supporting signature was encountered using the same supporting token '{0}'. + + + An unsecured or incorrectly secured fault was received from the other party. See the inner FaultException for the fault code and detail. + + + At least one security token in the message could not be validated. + + + The message could not be processed. This is most likely because the action '{0}' is incorrect or because the message contains an invalid or expired security context token or because there is a mismatch between bindings. The security context token would be invalid if the service aborted the channel due to inactivity. To prevent the service from aborting idle sessions prematurely increase the Receive timeout on the service endpoint's binding. + + + The security context token is expired or is not valid. The message was not processed. + + + Transport security negotiation failed due to an underlying IO error: {0}. + + + The security negotiation with '{0}' cannot be initiated because the confidential endpoint address header ('{1}', '{2}') cannot be encrypted during the course of the negotiation. + + + An error occurred when processing the security tokens in the message. + + + An error occurred when verifying security for the message. + + + The service does not allow you to log on anonymously. + + + Obtaining metadata from issuer '{0}' failed with error '{1}'. + + + Importing metadata from issuer '{0}' failed with error '{1}'. + + + Multiple correlation tokens were found in the security correlation state. + + + No correlation token was found in the security correlation state. + + + Multiple supporting token authenticators with the token parameter type equal to '{0}' cannot be specified. If more than one Supporting Token of the same type is expected in the response, then configure the supporting token collection with just one entry for that SecurityTokenParameters. The SecurityTokenAuthenticator that gets created from the SecurityTokenParameters will be used to authenticate multiple tokens. It is not possible to add SecurityTokenParameters of the same type in the SupportingTokenParameters collection or repeat it across EndpointSupportingTokenParameters and OperationSupportingTokenParameters. + + + A leg of the federated security chain contains multiple IssuedSecurityTokenParameters. The InfoCard system only supports one IssuedSecurityTokenParameters for each leg. + + + An unrecognized token authenticator '{0}' was used for token processing. + + + The SecurityTokenParameters and SecurityToken tuple specified for use in the security header must both be null or must both be non-null. + + + The CloneCore method of {0} type returned an invalid result. + + + Certificate-based client authentication is not supported in TransportCredentialOnly security mode. Select the Transport security mode. + + + BasicHttp binding requires that BasicHttpBinding.Security.Message.ClientCredentialType be equivalent to the BasicHttpMessageCredentialType.Certificate credential type for secure messages. Select Transport or TransportWithMessageCredential security for UserName credentials. + + + The client must provide key entropy in key entropy mode '{0}'. + + + A Proof Token was found in the response that was returned by the Security Token Service for a Bearer Key Type token request. Note that Proof Tokens should not be generated when a Bearer Key Type request is made. + + + Bearer Key Type is not supported with WSFederationHttpBinding. Please use WS2007FederationHttpBinding. + + + Unable to create Key Type element for the Key Type '{0}'. This might be due to a wrong version of MessageSecurityVersion set on the SecurityBindingElement. + + + The issuer cannot provide key entropy or a proof token in key entropy mode '{0}'. + + + The client cannot provide key entropy in key entropy mode '{0}'. + + + The issuer must provide a proof token in key entropy mode '{0}'. + + + The issuer must provide a computed key in key entropy mode '{0}'. + + + The issuer must provide key entropy in key entropy mode '{0}'. + + + The issuer cannot provide a computed key in key entropy mode '{0}'. + + + The computed key algorithm '{0}' is not supported. + + + The ReplayWindow and ClockSkew cannot be the maximum possible value when replay detection is enabled. + + + The session channel must be opened before the session ID can be accessed. + + + The binding ('{0}','{1}') for contract ('{2}','{3}') has been configured with an incompatible security version that does not support unattached references to EncryptedKeys. Use '{4}' or higher as the security version for the binding. + + + The '{0}','{1}' binding for the '{2}','{3}' contract is configured with a security version that does not support external references to X.509 tokens using the certificate's thumbprint value. Use '{4}' or higher as the security version for the binding. + + + The SecurityBinding for the ('{0}','{1}') binding for the ('{2}','{3}') contract only supports the OneWay operation. + + + Cannot map Windows user '{0}' to a UserPrincipalName that can be used for S4U impersonation. + + + Resolving an External reference token requires appropriate SecurityTokenParameters to be specified. + + + The SecurityContextSecurityToken's key needs to be renewed. + + + The client's security session was not able to close its output session within the configured timeout ({0}). + + + Client is unable to finish the security negotiation within the configured timeout ({0}). The current negotiation leg is {1} ({2}). + + + Client is unable to request the security session within the configured timeout ({0}). + + + The service's security session was not able to close its output session within the configured timeout ({0}). + + + The service's security session did not receive a 'close' message from the client within the configured timeout ({0}). + + + The client's security session did not receive a 'close response' message from the service within the configured timeout ({0}). + + + Cannot renew the security session key. + + + Cannot renew the security session key. Session Key Renewal is not supported. + + + Error parsing SecurityContextSecurityToken Cookie XML. + + + The SecurityContextSecurityToken's Cookie element either does not contain '{0}' or has a wrong value for it. + + + Error decoding the Cookie element of SecurityContextSecurityToken. + + + Issuing cookie SecurityContextSecurityToken is not supported. + + + Security policy import failed. The security policy contains supporting token requirements at the operation scope. The contract description does not specify the action for the request message associated with this operation. + + + Signature confirmation is not expected in the security header. + + + The signature confirmation elements cannot occur after the primary signature. + + + Signature confirmation was expected to be present in the security header. + + + The SecurityVersion '{0}' does not support signature confirmation. Use a later SecurityVersion. + + + The protocol factory must support Request/Reply security in order to offer signature confirmation. + + + Not all the signatures in the request message were confirmed in the reply message. + + + The request did not have any signatures but the reply has signature confirmations. + + + There are too many renewed session keys that have not been used. + + + The session key must be renewed before it can secure application messages. + + + The token's crypto collection has multiple objects of type '{0}'. + + + The token's crypto collection does not support algorithm '{0}'. + + + SymmetricSecurityBindingElement cannot build a channel or listener factory. The ProtectionTokenParameters property is required but not set. Binding element configuration: {0} + + + AsymmetricSecurityBindingElement cannot build a channel or listener factory. The InitiatorTokenParameters property is required but not set. Binding element configuration: {0} + + + AsymmetricSecurityBindingElement cannot build a channel or listener factory. The RecipientTokenParameters property is required but not set. Binding element configuration: {0} + + + The service cannot cache the negotiation state as the capacity '{0}' has been reached. Retry the request. + + + Internal SSL error (refer to Win32 status code for details). Check the server certificate to determine if it is capable of key exchange. + + + The key rollover interval cannot be greater than the key renewal interval. + + + The request message must be protected. This is required by an operation of the contract ('{0}','{1}'). The protection must be provided by the binding ('{2}','{3}'). + + + The response message must be protected. This is required by an operation of the contract ('{0}', '{1}'). The protection must be provided by the binding ('{2}', '{3}'). + + + The contract ('{0}','{1}') contains some unknown header ('{2}','{3}') which cannot be secured. Please choose ProtectionLevel.None for this header. + + + The binding ('{0}','{1}') supports streaming which cannot be configured together with message level security. Consider choosing a different transfer mode or choosing the transport level security. + + + The supporting token in the renew message has a different generation '{0}' than the current session token's generation '{1}'. + + + Security Support Provider Interface (SSPI) authentication failed. The server may not be running in an account with identity '{0}'. If the server is running in a service account (Network Service for example), specify the account's ServicePrincipalName as the identity in the EndpointAddress for the server. If the server is running in a user account, specify the account's UserPrincipalName as the identity in the EndpointAddress for the server. + + + For this security protocol, the incoming signing token must be an EncryptedKey. + + + The security session was terminated This may be because no messages were received on the session for too long. + + + No AppliesTo element is present in the deserialized RequestSecurityToken/RequestSecurityTokenResponse. + + + Symmetric Key length {0} is not supported by the algorithm suite '{1}'. + + + For replay detection to be done ProtectionLevel must be Sign or EncryptAndSign. + + + Can't infer an external reference for '{0}' token type. + + + Unable to create Attached or Unattached reference for '{0}'. + + + The configured Trust version does not support sessions. Use WSTrustFeb2005 or above. + + + The configured WS-Trust version does not support issued tokens. WS-Trust February 2005 or later is required. + + + The binding ('{0}','{1}') for contract ('{2}','{3}') supports impersonation only on Windows 2003 Server and newer version of Windows. Use SspiNegotiated authentication and a binding with Secure Conversation with cancellation enabled. + + + Impersonation using the client token is not possible. The binding ('{0}', '{1}') for contract ('{2}', '{3}') uses the Username Security Token for client authentication with a Membership Provider registered. Use a different type of security token for the client. + + + Cannot establish a reliable session without secure conversation. Enable secure conversation. + + + Failed to revert impersonation. {0} + + + In order to flow a transaction, flowing issued tokens must also be supported. + + + The configured SecurityVersion does not support signature confirmation. Use WsSecurity11 or above. + + + The configured SecureConversation version does not support sessions. Use WSSecureConversationFeb2005 or above. + + + SOAP security negotiation failed. See inner exception for more details. + + + SOAP security negotiation with '{0}' for target '{1}' failed. See inner exception for more details. + + + The one-way operation returned a fault message. The reason for the fault was '{0}'. + + + The one-way operation returned a fault message with Action='{0}'. + + + The one-way operation returned a non-null message with Action='{0}'. + + + Cannot find the security session with the ID '{0}'. + + + The SecurityContextSecurityToken with Context-id={0} (generation-id={1}) has expired. + + + The SecurityContextSecurityToken with Context-id={0} (no key generation-id) has expired. + + + Security sessions require all messages to be signed. + + + Required timestamp missing in security header. + + + The request message in the request context received from channel '{0}' is null. + + + The key effective and expiration times must be bounded by the token effective and expiration times. + + + The valid from time is greater than the valid to time. + + + No session token was present in the message. + + + Key length '{0}' is not a multiple of 8 for symmetric keys. + + + Invalid binary representation of an X.509 certificate. + + + Security policy export failed. The binding contains a TransportSecurityBindingElement but no transport binding element that implements ITransportTokenAssertionProvider. Policy export for such a binding is not supported. Make sure the transport binding element in the binding implements the ITransportTokenAssertionProvider interface. + + + Cannot import the security policy. The protection requirements for the secure conversation bootstrap binding are not supported. Protection requirements for the secure conversation bootstrap must require both the request and the response to be signed and encrypted. + + + Cannot import the policy. The value of the attribute '{0}' must be either 'true', 'false', '1' or '0'. The following error occurred: '{1}'. + + + The security policy expert failed. The provided transport token assertion of type '{0}' did not create a transport token assertion to include the sp:TransportBinding security policy assertion. + + + Message security policy for the '{0}' action requires confidentiality without integrity. Confidentiality without integrity is not supported. + + + The primary signature must be encrypted. + + + A symmetric crypto could not be created from token '{0}'. + + + The key size requirements for the '{0}' algorithm suite are not met by the '{1}' token which has key size of '{2}'. + + + The received message does not meet the required message protection order '{0}'. + + + Primary signature must be computed before supporting token signatures. + + + Element to sign must have id. + + + The token Serializer cannot serialize '{0}'. If this is a custom type you must supply a custom serializer. + + + Signing without primary signature requires timestamp. + + + This operation cannot be done after processing is started. + + + The recursive policy fetching limit has been reached. Check to determine if there is a loop in the federation service chain. + + + The ('{0}', '{1}') signed header contains the ('{2}', '{3}') attribute. The expected attribute is ('{4}', '{5}'). + + + The address of the security token issuer is not specified. An explicit issuer address must be specified in the binding for target '{0}' or the local issuer address must be configured in the credentials. + + + More than one SecurityBindingElement found in the binding ('{0}', '{1}) for contract ('{2}', '{3}'). Only one SecurityBindingElement is allowed. + + + ClientCredentials cannot create a local token provider for token requirement {0}. + + + A security policy was imported for the endpoint. The security policy contains requirements that cannot be represented in a Windows Communication Foundation configuration. Look for a comment about the SecurityBindingElement parameters that are required in the configuration file that was generated. Create the correct binding element with code. The binding configuration that is in the configuration file is not secure. + + + The configuration schema is insufficient to describe the non-standard configuration of the following security binding element: + + + The wsdl schema that was used to create this configuration file contained a 'RequireIssuerSerialReference' assertion for a X509Token. This can not be represented in configuration, you will need to programatically adjust the appropriate X509SecurityTokenParameters.X509KeyIdentifierClauseType to X509KeyIdentifierClauseType.IssuerSerial. The default of X509KeyIdentifierClauseType.Thumbprint will be used, which may cause interop issues. + + + The security protocol '{0}' cannot do replay detection. + + + Security processor was unable to find a security header with actor '{0}' in the message. This might be because the message is an unsecured fault or because there is a binding mismatch between the communicating parties. This can occur if the service is configured for security and the client is not using security. + + + Security processor was unable to find a security header in the message. This might be because the message is an unsecured fault or because there is a binding mismatch between the communicating parties. This can occur if the service is configured for security and the client is not using security. + + + No primary signature available for supporting token signature verification. + + + Supporting token signatures not expected. + + + Cannot read the token from the '{0}' element with the '{1}' namespace for BinarySecretSecurityToken, with a '{2}' ValueType. If this element is expected to be valid, ensure that security is configured to consume tokens with the name, namespace and value type specified. + + + Element '{0}' with namespace '{1}' not found. + + + Expected element '{0}' or element '{1}' (from namespace '{2}'). + + + AcceleratedTokenAuthenticator does not expect RequestSecurityTokenResponse from the client. + + + The '{0}' binding with the '{1}' namespace is configured to issue cookie security context tokens. COM+ Integration services does not support cookie security context tokens. + + + The signature must be in the security header. + + + The '{0}' required message part was not signed. + + + The '{0}', '{1}' required message part was not signed. + + + The '{0}' required message part was not encrypted. + + + The '{0}', '{1}' required message part was not encrypted. + + + Signature verification failed. + + + Cannot issue the token type '{0}'. + + + There is no negotiation message to send. + + + The issued token has an invalid key size '{0}'. + + + Cannot determine the key size of the issued token. + + + The negotiation has not yet completed. + + + The negotiation has already completed. + + + Request Message is missing a MessageID header. One is required to correlate a reply. + + + Cannot create a security session. Retry later. + + + The security session with id '{0}' is already pending. + + + No security session with id '{0}' is pending. + + + No security session listener was found for message with action '{0}'. + + + The session token was not closed by the server. + + + '{0}' protocol can only be used by the Initiator. + + + '{0}' protocol can only be used at the Recipient. + + + Unexpected code path for server security application, sending outgoing message on Recipient. + + + Only body return values are supported currently for protection, MessagePartDescription was specified. + + + Unknown token attachment mode: {0}. + + + Security protocol must be '{0}', type is: '{1}'.; + + + The initial request context was already specified. Can not create two for same message. + + + {0}.OnCloseMessageReceived when state == Created. + + + Shutdown request was not received. + + + Unknown filter type: '{0}'. + + + Standards manager of filter does not match that of filter table. Can not have two different filters. + + + Session filter's isStrictMode differs from filter table's isStrictMode. + + + SecuritySessionServerSettings.CreateAcceptor, channelAcceptor must be null, can not create twice. + + + Invalid TransactionFlowOption value. + + + Security token manager could not parse token with name '{0}', namespace '{1}', valueType '{2}'. + + + Security negotiation message has incorrect action '{0}'. + + + The specified key size {0} is invalid. The key size must be between {1} and {2}. + + + Could not get token information (error=0x{0:X}). + + + Unexpected end of file. + + + The security timestamp is invalid because its creation time ('{0}') is greater than or equal to its expiration time ('{1}'). + + + The security timestamp is stale because its expiration time ('{0}') is in the past. Current time is '{1}' and allowed clock skew is '{2}'. + + + The security timestamp is invalid because its creation time ('{0}') is in the future. Current time is '{1}' and allowed clock skew is '{2}'. + + + The security timestamp is stale because its creation time ('{0}') is too far back in the past. Current time is '{1}', maximum timestamp lifetime is '{2}' and allowed clock skew is '{3}'. + + + The nonce is invalid or replayed. + + + Message part specification must be made constant before being set. + + + Issuer entropy is not BinarySecretSecurityToken or WrappedKeySecurityToken. + + + No RequestSecurityTokenResponse elements were found. + + + The SecurityContextSecurityToken does not have a cookie. + + + TokenProvider returned token of incorrect type '{0}'. + + + {0} is not available in deserialized RequestSecurityToken. + + + {0} is only available in a deserialized RequestSecurityToken. + + + {0} is not available in deserialized RequestSecurityTokenResponse. + + + {0} is only available in a deserialized RequestSecurityTokenResponse. + + + The RequestSecurityTokenResponseCollection received has more than one RequestSecurityTokenResponse element. Only one RequestSecurityTokenResponse element was expected. + + + VirtualPathExtension is not allowed to be removed. + + + The protocol '{0}' is not supported. + + + The BaseUriWithWildcard object has invalid fields after deserialization. + + + Registered relativeAddress '{0}' in configuration file is not a valid one. Possible causes could be : You specified an empty addreess or an absolute address (i.e., starting with '/' or '\\'), or the address contains invalid character[s]. The supported relativeAddress formats are "[folder/]filename" or "~/[folder/]filename". + + + '{0}' is an absolute address. The supported relativeAddress formats are "[subfolder/]filename" or "~/[subfolder/]filename". + + + Cannot create security binding element based on the configuration data. When secure conversation authentication mode is selected, the secure conversation bootstrap binding element must also be specified. + + + Setting minFreeMemoryPercentageToActivateService requires full trust privilege. Please change the application's trust level or remove this setting from the configuration file. + + + This service requires ASP.NET compatibility and must be hosted in IIS. Either host the service in IIS with ASP.NET compatibility turned on in web.config or set the AspNetCompatibilityRequirementsAttribute.AspNetCompatibilityRequirementsMode property to a value other than Required. + + + The '{0}' protocol binding '{1}' specifies an invalid port number '{2}'. + + + The protocol binding '{0}' does not conform to the syntax for '{1}'. The following is an example of valid '{1}' protocol bindings: '{2}'. + + + The protocol binding '{0}' is not valid for '{1}'. This might be because the port number is out of range. + + + There is no compatible TransportManager found for URI '{0}'. This may be because you have used an absolute address that points outside of the virtual application. Please use a relative address instead. + + + There is no compatible TransportManager found for URI '{0}'. This may be because you have used an absolute address that points outside of the virtual application, or the binding settings of the endpoint do not match those that have been set by other services or endpoints. Note that all bindings for the same protocol should have the same settings in the same application. + + + '{0}' cannot be invoked within the current hosting environment. This API requires that the calling application be hosted in IIS or WAS. + + + The requested service, '{0}' could not be activated. See the server's diagnostic trace logs for more information. + + + The value for the Service attribute was not provided in the ServiceHost directive. + + + The service endpoint failed to listen on the URI '{0}' because access was denied. Verify that the current user is granted access in the appropriate allowAccounts section of SMSvcHost.exe.config. + + + The service endpoint failed to listen on the URI '{0}' because the shared memory section was not found. Verify that the '{1}' service is running. + + + The TransportManager failed to listen on the supplied URI using the {0} service: {1}. + + + failed to start the service ({0}). Refer to the Event Log for more details + + + failed to start the service because it is disabled. An administrator can enable it by running 'sc.exe config {0} start= demand'. + + + failed to start the service. Refer to the Event Log for more details + + + failed to look up the service process in the SCM ({0}) + + + failed to look up the service SID in the SCM ({0}) + + + failed to read the service's endpoint with native error code {0}. See inner exception for details + + + the service failed the security checks + + + failed to retrieve the UserSid of the service process ({0}) + + + failed to retrieve the UserSid of the current process + + + failed to retrieve the LogonSid of the service process ({0}) + + + failed to establish a data connection to the service + + + failed to create a data connection to the service + + + failed to establish the data connection because of an I/O error + + + the version is not supported by the service + + + failed to grant the PROCESS_DUP_HANDLE access right to the target service's account SID '{0}'. + + + the URI is too long + + + the quota was exceeded + + + the protocol is not supported + + + the URI is already registered with the service + + + the service failed to listen + + + The message could not be dispatched to the service at address '{0}'. Refer to the server Event Log for more details + + + The message could not be dispatched because the service at the endpoint address '{0}' is unavailable for the protocol of the address. + + + The endpoint address for the NT service '{0}' read from shared memory is empty. + + + The message could not be dispatched because the transport manager has been stopped. This can happen if the application is being recycled or disabled. + + + The '{0}' from the '{1}' namespace is empty and does not specify a valid identity claim. + + + '{0}' from namespace '{1}' is not expected. Expecting element '{2}' from namespace '{3}' + + + '{0}' from namespace '{1}' is not expected to appear more than once + + + An unsupported security policy assertion was detected during the security policy import: {0} + + + The extensions cannot contain an Identity if one is supplied as a constructor argument. + + + Value '{0}' provided for '{1}' from namespace '{2}' is an invalid absolute URI. + + + The binding ('{0}','{1}') for contract ('{2}','{3}') is configured with SecureConversation, but the authentication mode is not able to provide the request/reply-based integrity and confidentiality required for the negotiation. + + + The '{0}'.'{1}' binding for the '{2}'.'{3}' contract is configured with an authentication mode that requires transport level integrity and confidentiality. However the transport cannot provide integrity and confidentiality. + + + The contract operation '{0}' requires Windows identity for automatic impersonation. A Windows identity that represents the caller is not provided by binding ('{1}','{2}') for contract ('{3}','{4}'. + + + A listen URI must be specified in order to open this {0}. + + + Channel interface type '{0}' is not supported. + + + This property cannot be changed after the transport manager has been opened. + + + This operation is only valid after the transport manager has been opened. + + + Unrecognized identity type Name='{0}', Namespace='{1}'. + + + Cannot read the Identity element. The Identity type is not supported or the Identity element is empty. + + + Cannot load the X.509 certificate identity specified in the configuration. + + + The ClaimType '{0}' is not recognized. Expected ClaimType '{1}'. + + + An AsyncCallback threw an exception. + + + You cannot Send messages on a channel after CloseOutputSession has been called. + + + The communication object, {0}, cannot be modified while it is in the {1} state. + + + The communication object, {0}, cannot be modified unless it is in the Created state. + + + The communication object, {0}, is in the {1} state. Communication objects cannot be used for communication unless they are in the Opened state. + + + The communication object, {0}, cannot be used for communication because it is in the Faulted state. + + + The communication object, {0}, cannot be used for communication because it is in the Faulted state: {1} + + + The communication object, {0}, cannot be used for communication because it has been Aborted. + + + The communication object, {0}, cannot be used for communication because it has been Aborted: {1} + + + The communication object, {0}, has overridden the virtual function {1} but it does not call version defined in the base class. + + + The communication object, {0}, is not part of WCF and is in an unsupported state '{1}'. This indicates an internal error in the implementation of that communication object. + + + The communication object, {0}, cannot be used due to an error that occurred during close. + + + A call to IChannelFactory.CreateChannel made on an object of type {0} failed because Open has not been called on this object. + + + Cannot modify channel parameters because the {0} is in the {1} state. This operation is only supported in the Created state. + + + Cannot propagate channel parameters because the {0} is in the {1} state. This operation is only supported in the Opening or Opened state when the collection is locked. + + + Binding '{0}' is not configured properly. OneWayBindingElement requires an inner binding element that supports IRequestChannel/IReplyChannel or IDuplexSessionChannel. + + + The specified channel type {0} is not supported by this channel manager. + + + SecurityContext for the UltimateReceiver role is missing from the SecurityContextProperty of the request message with action '{0}'. + + + Cannot start impersonation because the SecurityContext for the UltimateReceiver role from the request message with the '{0}' action is not mapped to a Windows identity. + + + Unexpected internal enum value: {0}. + + + Invalid decoder state machine. + + + Operation property of OperationAttributeGenerationContext is required to generate an attribute based on settings. + + + The username/password Membership provider {0} specified in the configuration is invalid. No such provider was found registered under system.web/membership/providers. + + + The RoleProvider {0} specified in the configuration is invalid. No such provider was found registered under system.web/roleManager/providers. + + + The {0} object has been disposed. + + + The XmlReader used for the body of the message must be positioned on an element. + + + A property with the name '{0}' already exists. + + + A property with the name '{0}' is not present. + + + The message header with name '{0}' and namespace '{1}' is already present in the set of understood headers. + + + The message header with name '{0}' and namespace '{1}' is not present in the set of understood headers. + + + Multiple headers with name '{0}' and namespace '{1}' found. + + + Multiple headers with name '{0}' and namespace '{1}' and role '{2}' found. + + + Multiple RelatesTo headers with relationship '{0}' found. Only one is allowed per relationship. + + + Additional XML content is present in the fault detail element. Only a single element is allowed. + + + The body of the message cannot be read because it is empty. + + + Message is closed. + + + The operation cannot be completed because the stream is closed. + + + The body writer returned from OnCreateBufferedCopy was not buffered. + + + The body writer does not support writing more than once because it is not buffered. + + + KeySize element not present in RequestSecurityTokenResponse. + + + A reply message cannot be created because the request message does not have a MessageID. + + + There is not a header with name {0} and namespace {1} in the message. + + + MessageBuffer is closed. + + + The text encoding '{0}' used in the text message format is not supported. + + + At least one fault reason must be specified. + + + The translation set cannot contain nulls. + + + The fault does not have detail information. + + + Expected XML qualified name, found '{0}'. + + + Unbound prefix used in qualified name '{0}'. + + + ... + + + ... stream ... + + + ... Error reading body: {0}: {1} ... + + + The fault reason does not contain any text translations. + + + Client cannot determine the Service Principal Name based on the identity in the target address '{0}' for the purpose of SspiNegotiation/Kerberos. The target address identity must be a UPN identity (like acmedomain\\alice) or SPN identity (like host/bobs-machine). + + + Required xml:lang attribute value is missing. + + + Unrecognized charSet '{0}' in contentType. + + + Unrecognized contentType ({0}). Expected: {1}. + + + Cannot process contentType. + + + The envelope version of the incoming message ({0}) does not match that of the encoder ({1}). Make sure the binding is configured with the same version as the expected messages. + + + The message version of the outgoing message ({0}) does not match that of the encoder ({1}). Make sure the binding is configured with the same version as the message. + + + MessageVersion '{0}' not supported by MTOM encoder. + + + Read is not supported on this stream. + + + Seek is not supported on this stream. + + + An asynchronous write is pending on the stream. Ensure that there are no uncompleted asynchronous writes before attempting the next write. + + + A newly accepted connection did not receive initialization data from the sender within the configured ChannelInitializationTimeout ({0}). As a result, the connection will be aborted. If you are on a highly congested network, or your sending machine is heavily loaded, consider increasing this value or load-balancing your server. + + + The remote endpoint of the socket ({0}) did not respond to a close request within the allotted timeout ({1}). It is likely that the remote endpoint is not calling Close after receiving the EOF signal (null) from Receive. The time allotted to this operation may have been a portion of a longer timeout. + + + A graceful close was attempted on the socket, but the other side ({0}) is still sending data. + + + The pipe cannot be closed while a write to the pipe is pending. + + + The shutdown indicator could not be written to the pipe. The application on the other end of the pipe may not be listening for it. The pipe will still be closed. + + + The shutdown indicator was not received from the pipe. The application on the other end of the pipe may not have sent it. The pipe will still be closed. + + + The pipe name could not be obtained for the pipe URI: {0} + + + The pipe name could not be obtained for {0}. + + + The pipe was not able to be set to message mode: {0} + + + The pipe could not close gracefully. This may be caused by the application on the other end of the pipe exiting. + + + The pipe cannot be written to because it is already in the process of shutting down. + + + The read from the pipe expected just a signal, but received actual data. + + + The pipe cannot be written to or read from because it is already in the process of being closed. + + + Server cannot accept pipe: {0} + + + Cannot listen on pipe '{0}': {1} + + + Cannot listen on pipe name '{0}' because another pipe endpoint is already listening on that name. + + + Cannot listen on pipe '{0}' because the pipe name could not be reserved: {1} + + + The pipe listener has been disposed. + + + Connections cannot be created until the pipe has started listening. Call Listen() before attempting to accept a connection. + + + A pipe endpoint exists for '{0}', but the connect failed: {1} + + + Cannot connect to endpoint '{0}'. + + + Cannot connect to endpoint '{0}' within the allotted timeout of {1}. The time allotted to this operation may have been a portion of a longer timeout. + + + Cannot connect to endpoint '{0}' within the allotted timeout of {1}. The server has likely reached the MaxConnections quota and is too busy to accept new connections. The time allotted to this operation may have been a portion of a longer timeout. + + + The pipe endpoint '{0}' could not be found on your local machine. + + + URIs used with pipes must use the scheme: 'net.pipe'. + + + The pipe write did not write all the bytes. + + + The operation cannot be completed because the pipe was closed. This may have been caused by the application on the other end of the pipe exiting. + + + The read from the pipe did not complete within the allotted timeout of {0}. The time allotted to this operation may have been a portion of a longer timeout. + + + The write to the pipe did not complete within the allotted timeout of {0}. The time allotted to this operation may have been a portion of a longer timeout. + + + The pipe connection was aborted because an asynchronous read from the pipe did not complete within the allotted timeout of {0}. The time allotted to this operation may have been a portion of a longer timeout. + + + The pipe connection was aborted because an asynchronous write to the pipe did not complete within the allotted timeout of {0}. The time allotted to this operation may have been a portion of a longer timeout. + + + There was an error writing to the pipe: {0}. + + + There was an error reading from the pipe: {0}. + + + Unrecognized error {0} (0x{1}) + + + {0} ({1}, 0x{2}) + + + There is already a write in progress for the pipe. Wait for the first operation to complete before attempting to write again. + + + There is already a read in progress for the pipe. Wait for the first operation to complete before attempting to read again. + + + There was an error duplicating the. + + + The Session value '{0}' is invalid. Please specify 'CurrentSession','ServiceSession' or a valid non-negative Windows Session Id. + + + The package full name '{0}' is invalid. + + + The socket was aborted because an asynchronous receive from the socket did not complete within the allotted timeout of {0}. The time allotted to this operation may have been a portion of a longer timeout. + + + The socket connection was aborted because an asynchronous send to the socket did not complete within the allotted timeout of {0}. The time allotted to this operation may have been a portion of a longer timeout. + + + This operation is not valid until security negotiation is complete. + + + Error while reading message framing format at position {0} of stream (state: {1}) + + + More data was expected, but EOF was reached. + + + Expected record type '{0}', found '{1}'. + + + Framing major version {0} is not supported. + + + Framing mode {0} is not supported. + + + Specified size is too large for this implementation. + + + The framing via size ({0}) exceeds the quota. + + + The framing via ({0}) is not a valid URI. + + + The framing fault size ({0}) exceeds the quota. + + + The framing content type size ({0}) exceeds the quota. + + + The value cannot be accessed because it has not yet been fully decoded. + + + An attempt was made to decode a value after the framing stream was ended. + + + Stream Security is required at {0}, but no security context was negotiated. This is likely caused by the remote endpoint missing a StreamSecurityBindingElement from its binding. + + + The binary encoder session information exceeded the maximum size quota ({0}). To increase this quota, use the MaxSessionSize property on the BinaryMessageEncodingBindingElement. + + + The binary encoder session is not valid. There was an error decoding a previous message. + + + The binary encoder session information is not properly formed. + + + The channel received an unexpected fault input message while closing. The fault reason given is: '{0}' + + + The channel received an unexpected fault input message with Action = '{0}' while closing. You should only close your channel when you are not expecting any more input messages. + + + The channel received an unexpected input message with Action '{0}' while closing. You should only close your channel when you are not expecting any more input messages. + + + The maximum message size quota for incoming messages ({0}) has been exceeded. To increase the quota, use the MaxReceivedMessageSize property on the appropriate binding element. + + + The maximum message size quota for outgoing messages ({0}) has been exceeded. + + + The maximum message size quota for incoming messages has been exceeded for the remote channel. See the server logs for more details. + + + TimeoutStream requires an inner Stream that supports timeouts; its CanTimeout property must be true. + + + The filter already exists in the filter table. + + + An internal error has occurred. Unexpected error modifying filter table. + + + The number of XML infoset nodes inspected by the navigator has exceeded the quota ({0}). + + + Value cannot be negative. + + + The set of actions cannot be empty. + + + The prefix '{0}' is not defined. + + + Multiple filters matched. + + + The type of IMessageFilterTable created for a particular Filter type must always be the same. + + + The MessageFilterTable state is corrupt. The requested lookup cannot be performed. + + + The IMessageFilterTable created for a Filter cannot be a MessageFilterTable or a subclass of MessageFilterTable. + + + NodeQuota must be greater than 0. + + + Parameter value cannot be an empty string. + + + Required inner element '{0}' was not found. + + + Invalid attribute on the XPath. + + + When present, the dialect attribute must have the value '{0}'. + + + Could not compile the XPath expression '{0}' with the given XsltContext. + + + XmlReader not positioned at a start element. + + + The position is not valid for this navigator. + + + Cannot call '{0}' on a non-atomized navigator. + + + XML unique ID not supported. + + + A filter has attempted to access the body of a Message. Use a MessageBuffer instead if body filtering is required. + + + Not allowed to override prefix '{0}'. + + + The function '{0}' is not implemented. + + + XPathNavigator positions cannot be compared. + + + XPathNavigator must be a SeekableXPathNavigator. + + + Context node is not supported in node sequences. + + + IXsltContextFunction return type '{0}' not supported. + + + IXsltContextVariable type '{0}' not supported. + + + IXsltContextVariables cannot return null. + + + The argument to an IXsltContextFunction could not be converted to a string. + + + An internal error has occurred. Item already exists. + + + Positioned before first element. + + + Positioned after last element. + + + The XPathNodeIterator has been invalidated. XPathNodeIterators passed as arguments to IXsltContextFunctions are only valid within the function. They cannot be cached for later use or returned as the result of the function. + + + The string value can't be determined because the XPathNodeIterator has been moved past the first node. + + + {0} {1} + + + Addressing10 ({0}) + + + Addressing200408 ({0}) + + + AddressingNone ({0}) + + + Addressing Version '{0}' is not supported. + + + The '{0}' addressing mode is not supported. + + + Soap11 ({0}) + + + Soap12 ({0}) + + + EnvelopeNone ({0}) + + + The IMessageProperty could not be copied. CreateCopy returned null. + + + Unrecognized message version. + + + Unrecognized envelope version: {0}. + + + Envelope Version '{0}' is not supported. + + + Cannot detect WS-Addressing version. EndpointReference does not start with an Element. + + + Envelope Version '{0}' does not support adding Message Headers. + + + Addressing Version '{0}' does not support adding WS-Addressing headers. + + + The element '{0}' in namespace '{1}' is not valid. This either means that element '{0}' is a duplicate element, or that it is not a legal extension because extension elements cannot be in the addressing namespace. + + + The '{0}' header cannot be added because it does not support the specified message version '{1}'. + + + This message cannot support the operation because it has been copied. + + + This message cannot support the operation because it has been written. + + + This message cannot support the operation because it has been read. + + + An internal error has occurred. Invalid MessageState. + + + The body reader is in ReadState '{0}' and cannot be consumed. + + + The size necessary to buffer the XML content exceeded the buffer quota. + + + An internal error has occurred. The XML buffer is not in the correct state to perform the operation. + + + A body element was not found inside the message envelope. + + + The version of the header(s) ({0}) differs from the version of the message ({1}). + + + Manual addressing is enabled on this factory, so all messages sent must be pre-addressed. + + + A one-way header was expected on this message and none was found. It is possible that your bindings are mismatched. + + + Receive on local address {0} timed out after {1}. The time allotted to this operation may have been a portion of a longer timeout. + + + Receive timed out after {0}. The time allotted to this operation may have been a portion of a longer timeout. + + + WaitForMessage timed out after {0}. The time allotted to this operation may have been a portion of a longer timeout. + + + Receive timed out after {0}. The time allotted to this operation may have been a portion of a longer timeout. + + + Receive request timed out after {0}. The time allotted to this operation may have been a portion of a longer timeout. + + + Receive request on local address {0} timed out after {1}. The time allotted to this operation may have been a portion of a longer timeout. + + + Sending to via {0} timed out after {1}. The time allotted to this operation may have been a portion of a longer timeout. + + + Close timed out after {0}. Increase the timeout value passed to the call to Close or increase the CloseTimeout value on the Binding. The time allotted to this operation may have been a portion of a longer timeout. + + + Open timed out after {0} while establishing a transport session to {1}. The time allotted to this operation may have been a portion of a longer timeout. + + + Request timed out after {0} while establishing a transport connection to {1}. The time allotted to this operation may have been a portion of a longer timeout. + + + Connecting to via {0} timed out after {1}. Connection attempts were made to {2} of {3} available addresses ({4}). Check the RemoteAddress of your channel and verify that the DNS records for this endpoint correspond to valid IP Addresses. The time allotted to this operation may have been a portion of a longer timeout. + + + The request channel timed out attempting to send after {0}. Increase the timeout value passed to the call to Request or increase the SendTimeout value on the Binding. The time allotted to this operation may have been a portion of a longer timeout. + + + The request channel timed out while waiting for a reply after {0}. Increase the timeout value passed to the call to Request or increase the SendTimeout value on the Binding. The time allotted to this operation may have been a portion of a longer timeout. + + + The policy being imported for contract '{0}:{1}' contains multiple HTTP authentication scheme assertions. Since at most one such assertion is allowed, policy import has failed. This may be resolved by updating the policy to contain no more than one HTTP authentication scheme assertion. + + + More than one '{0}' objects were found in the BindingParameters of the BindingContext. This is usually caused by having multiple '{0}' objects in a CustomBinding. Remove all but one of these elements. + + + The '{0}' can only be used with HTTP (or HTTPS) transport. + + + The value specified, '{0}', for the If-Modified-Since header does not parse into a valid date. Check the property value and ensure that it is of the proper format. + + + The SOAP action specified on the message, '{0}', does not match the action specified on the HttpRequestMessageProperty, '{1}'. + + + The SOAP action specified on the message, '{0}', does not match the action specified in the content-type of the HttpRequestMessageProperty, '{1}'. + + + The SOAP action specified on the message, '{0}', does not match the HTTP SOAP Action, '{1}'. + + + An error ({0}) occurred while parsing the content type of the HTTP request. The content type was: {1}. + + + The HTTP service located at {0} is unavailable. This could be because the service is too busy or because no endpoint was found listening at the specified address. Please ensure that the address is correct and try accessing the service again later. + + + The HTTP request to '{0}' was aborted. This may be due to the local channel being closed while the request was still in progress. If this behavior is not desired, then update your code so that it does not close the channel while request operations are still in progress. + + + The HTTP request to '{0}' has exceeded the allotted timeout of {1}. The time allotted to this operation may have been a portion of a longer timeout. + + + The HTTP request to '{0}' has exceeded the allotted timeout of {1} while reading the response. The time allotted to this operation may have been a portion of a longer timeout. + + + An error ({0}) occurred while transmitting data over the HTTP channel. + + + An error occurred while receiving the HTTP response to {0}. This could be due to the service endpoint binding not using the HTTP protocol. This could also be due to an HTTP request context being aborted by the server (possibly due to the service shutting down). See server logs for more details. + + + An error occurred while making the HTTP request to {0}. This could be due to the fact that the server certificate is not configured properly with HTTP.SYS in the HTTPS case. This could also be caused by a mismatch of the security binding between the client and the server. + + + HTTP request streaming cannot be used in conjunction with HTTP authentication. Either disable request streaming or specify anonymous HTTP authentication. + + + A reply has already been sent from this RequestContext. + + + Unable to start the HTTP listener. The URI provided, '{0}', is invalid for listening. Check the base address of your service and verify that it is a valid URI. + + + The requestContext has been aborted. + + + The receive context, {0}, is in the {1} state. Receive contexts cannot be used for sending delayed acks unless they are in the Received state. + + + The receive context, {0}, is in an unsupported state '{1}'. This indicates an internal error in the implementation of that receive context. + + + The receive context, {0}, cannot be used for sending delayed acks because it is in the Faulted state. + + + Invalid HostNameComparisonMode value: {0}. + + + Invalid data buffer. + + + A security session renew response was received with an invalid action '{0}'. + + + A security session close response was received with an invalid action '{0}', + + + TransactedBatchingBehavior cannot be used when ReceiveContext is being used. + + + Could not formulate request message for security session operation '{0}'. + + + There is no handler registered for session token issuance event. + + + There is no handler registered for session token renew event. + + + The identity of the security session renew message does not match the identity of the session token. + + + The RequestSecurityToken has an invalid or unspecified RequestType '{0}'. + + + The RequestSecurityToken must specify a CloseTarget. + + + Secure channel cannot be opened because security negotiation with the remote endpoint has failed. This may be due to absent or incorrectly specified EndpointIdentity in the EndpointAddress used to create the channel. Please verify the EndpointIdentity specified or implied by the EndpointAddress correctly identifies the remote endpoint. + + + The CloseTarget specified '{0}' does not identify the security token that signed the message. + + + The renew security session message does not have the session token as a supporting token. + + + The RequestSecurityToken must specify a RenewTarget. + + + There is no endorsing session token that matches the specified RenewTarget '{0}'. + + + Invalid format for encrypted body. + + + The EncryptedData or EncryptedKey is in an invalid state for this operation. + + + No signature message parts were specified for messages with the '{0}' action. + + + No encryption message parts were specified for messages with the '{0}' action. + + + The receiver sent back a security session fault message. Retry the request. + + + The Inner listener factory of {0} must be set before this operation. + + + Cannot create security binding element based on configuration data. The secure conversation bootstrap requires another secure conversation which is not supported. + + + Cannot open ChannelFactory as the inner channel factory was not set during the initialization process. + + + Duplex security is not supported by the security protocol factory '{0}'. + + + Request-reply security is not supported by the security protocol factory '{0}'. + + + The security protocol factory must be set before this operation is performed. + + + Security session protocol factory must be set before this operation is performed. + + + Security channel or listener factory creation failed. Secure conversation security token parameters do not specify the bootstrap security binding element. + + + The required '{0}' property on the '{1}' security protocol factory is not set or has an invalid value. + + + The protocol factory cannot create a protocol. + + + The identity check failed for the outgoing message. The expected identity is '{0}' for the '{1}' target endpoint. + + + The identity check failed for the incoming message. The expected identity is '{0}' for the '{1}' target endpoint. + + + The Identity check failed for the incoming message. The remote endpoint did not provide a domain name system (DNS) claim and therefore did not satisfied DNS identity '{0}'. This may be caused by lack of DNS or CN name in the remote endpoint X.509 certificate's distinguished name. + + + The Identity check failed for the outgoing message. The remote endpoint did not provide a domain name system (DNS) claim and therefore did not satisfied DNS identity '{0}'. This may be caused by lack of DNS or CN name in the remote endpoint X.509 certificate's distinguished name. + + + Identity check failed for incoming message. The expected DNS identity of the remote endpoint was '{0}' but the remote endpoint provided DNS claim '{1}'. If this is a legitimate remote endpoint, you can fix the problem by explicitly specifying DNS identity '{1}' as the Identity property of EndpointAddress when creating channel proxy. + + + Identity check failed for outgoing message. The expected DNS identity of the remote endpoint was '{0}' but the remote endpoint provided DNS claim '{1}'. If this is a legitimate remote endpoint, you can fix the problem by explicitly specifying DNS identity '{1}' as the Identity property of EndpointAddress when creating channel proxy. + + + The serialized token version {0} is unsupported. + + + The RequestSecurityTokenResponseCollection does not contain an authenticator. + + + The negotiation RequestSecurityTokenResponse has a different context from the authenticator RequestSecurityTokenResponse. + + + The recipient did not provide its certificate. This certificate is required by the TLS protocol. Both parties must have access to their certificates. + + + The authenticator was not included in the final leg of negotiation. + + + The RequestSecurityTokenResponse CombinedHash is incorrect. + + + The certificate for the client has not been provided. The certificate can be set on the ClientCredentials or ServiceCredentials. + + + The client certificate is not provided. Specify a client certificate in ServiceCredentials. + + + The client certificate is not provided. Specify a client certificate in ClientCredentials. + + + The service certificate is not provided. Specify a service certificate in ServiceCredentials. + + + The service certificate is not provided for target '{0}'. Specify a service certificate in ClientCredentials. + + + The username is not provided. Specify username in ClientCredentials. + + + Object is read-only. + + + Element {0} cannot be empty. + + + XML child node {0} of type {1} is unexpected for element {2}. + + + The context-id={0} (generation-id={1}) is already registered with SecurityContextSecurityTokenAuthenticator. + + + The context-id={0} (no key generation-id) is already registered with SecurityContextSecurityTokenAuthenticator. + + + There is no SecurityContextSecurityToken with context-id={0} (generation-id={1}) registered with SecurityContextSecurityTokenAuthenticator. + + + There is no SecurityContextSecurityToken with context-id={0} (no key generation-id) registered with SecurityContextSecurityTokenAuthenticator. + + + The SecurityContextSecurityToken has an invalid Cookie. The following error occurred when processing the Cookie: '{0}'. + + + The SecurityContextSecurityToken with context-id={0} (key generation-id={1}) is not registered. + + + The SecurityContextSecurityToken with context-id={0} (key generation-id={1}) has expired. + + + The SecurityContextSecurityToken with context-id={0} (no key generation-id) has expired. + + + The SecurityContextSecurityToken does not have a context-id. + + + For sending a message on server side composite duplex channels, the message must have either the 'Via' property or the 'To' header set. + + + The 'Via' property on the message is set to Anonymous Uri '{0}'. Please set the 'Via' property to a non-anonymous address as message cannot be addressed to anonymous Uri on server side composite duplex channels. + + + The 'To' header on the message is set to Anonymous Uri '{0}'. Please set the 'To' header to a non-anonymous address as message cannot be addressed to anonymous Uri on server side composite duplex channels. + + + This SecurityProtocol instance was not set up to process outgoing messages. + + + This SecurityProtocol instance was not set up to process incoming messages. + + + The token provider cannot get tokens for target '{0}'. + + + Key derivation algorithm '{0}' is not supported. + + + Cannot find the correlation state for applying security to reply at the responder. + + + The reply was not signed with the required signing token. + + + Encryption not expected for this message. + + + A signature is not expected for this message. + + + The QName is invalid. + + + The ICrypto implementation '{0}' is not supported. + + + On DuplexSecurityProtocolFactory, the same protocol factory cannot be set for the forward and reverse directions. + + + The algorithm '{0}' is not accepted for operation '{1}' by algorithm suite {2}. + + + '{0}' does not support '{1}' creation. + + + Cannot create an ICrypto interface from the '{0}' token for signature verification. + + + Message security verification failed. + + + Transport secured messages should have the 'To' header specified. + + + The message received over Transport security was missing the 'To' header. + + + The message received over Transport security has unsigned 'To' header. + + + More than one 'To' header specified in a message secured by Transport Security. + + + Received security header contains unexpected token '{0}'. + + + Cannot find the X.509 certificate using the following search criteria: StoreName '{0}', StoreLocation '{1}', FindType '{2}', FindValue '{3}'. + + + Cannot find The X.509 certificate using the following search criteria: StoreName '{0}', StoreLocation '{1}', FindType '{2}', FindValue '{3}' for target '{4}'. + + + Found multiple X.509 certificates using the following search criteria: StoreName '{0}', StoreLocation '{1}', FindType '{2}', FindValue '{3}'. Provide a more specific find value. + + + Found multiple X.509 certificates using the following search criteria: StoreName '{0}', StoreLocation '{1}', FindType '{2}', FindValue '{3}' for target '{4}'. Provide a more specific find value. + + + The KeyInfo clause is missing or empty in EncryptedKey. + + + The EncryptedKey clause was not wrapped with the required encryption token '{0}'. + + + The message was not encrypted with the required encryption token. + + + The timestamp must occur first in this security header layout. + + + The timestamp must occur last in this security header layout. + + + Only one primary signature is allowed in a security header. + + + The signing token {0} has no keys. The security token is used in a context that requires it to perform cryptographic operations, but the token contains no cryptographic keys. Either the token type does not support cryptographic operations, or the particular token instance does not contain cryptographic keys. Check your configuration to ensure that cryptographically disabled token types (for example, UserNameSecurityToken) are not specified in a context that requires cryptographic operations (for example, an endorsing supporting token). + + + The signing token {0} has no key that supports the algorithm suite {1}. + + + Delayed security application has already been completed. + + + Cannot resolve KeyInfo in derived key token for resolving source token: KeyInfoClause '{0}'. + + + KeyInfo clause '{0}' resolved to token '{1}', which does not contain a Symmetric key that can be used for derivation. + + + Cannot resolve KeyInfo for verifying signature: KeyInfo '{0}', available tokens '{1}'. + + + Cannot resolve KeyInfo for unwrapping key: KeyInfo '{0}', available tokens '{1}'. + + + Cannot resolve KeyInfo for decryption: KeyInfo '{0}', available tokens '{1}'. + + + An empty value was found for the required base-64 attribute name '{0}', namespace '{1}'. + + + The security header element '{0}' with the '{1}' id must be signed. + + + The '{0}' security token with the '{1}' attachment mode must be signed. + + + The '{0}' security token with the '{1}' attachment mode must be encrypted. + + + Operation '{0}' is not valid in message body state '{1}'. + + + EncryptedKey with ReferenceList is not allowed according to the current settings. + + + Cannot find a token authenticator for the '{0}' token type. Tokens of that type cannot be accepted according to current security settings. + + + No signature was created because not part of the message matched the supplied message part specification. + + + Supporting SecurityToken cannot be written without encryption. + + + The '{0}' id occurred twice in the message that is supplied for verification. + + + Canonicalization algorithm '{0}' is not supported. + + + The KeyInfo value was not found in the encrypted item to find the decrypting token. + + + No KeyInfo in signature to find verification token. + + + Security header is empty. + + + The encryption method is missing in encrypted data. + + + The Encrypted Header and the Security Header '{0}' attribute did not match. Encrypted Header: {1}. Security Header: {2}. + + + At most one reference list is supported with default policy check. + + + At most one signature is supported with default policy check. + + + Unexpected encrypted element in security header. + + + Id is missing in encrypted item in security header. + + + The supplied token manager cannot create a token reference. + + + The timestamp element added to security header to sign has no id. + + + An encrypted header must have an id. + + + The data reference '{0}' could not be resolved in the received message. + + + A timestamp element has already been set for this security header. + + + More than one Timestamp element was present in security header. + + + The incoming message was signed with a token which was different from what used to encrypt the body. This was not expected. + + + Cannot create the '{0}' symmetric algorithm from the token. + + + Unrecognized encoding occurred while reading the binary security token. + + + Cannot resolve reference URI '{0}' in signature to compute digest. + + + No timestamp is available in the security header to do replay detection. + + + No signature is available in the security header to provide the nonce for replay detection. + + + There is no namespace binding for prefix '{0}' in scope. + + + Derived Key Token cannot derive key from the secret. + + + Both offset and generation cannot be specified for Derived Key Token. + + + Either offset or generation must be specified for Derived Key Token. + + + DerivedKeyToken requires a reference to a token. + + + DerivedKey length ({0}) exceeds the allowed settings ({1}). + + + The Implicit derived key clause '{0}' specifies a derivation key length ({1}) which exceeds the allowed maximum length ({2}). + + + The received derived key token has a invalid offset value specified. Value: {0}. The value should be greater than or equal to zero. + + + The received derived key token has a invalid generation value specified. Value: {0}. The value should be greater than or equal to zero. + + + The XML element {0} does not have a child of type {1}. + + + RequestedSecurityToken not specified in RequestSecurityTokenResponse. + + + Binary encoding {0} is not supported. + + + Invalid key encryption algorithm {0}. + + + The asynchronous result object used to end this operation was not the object that was returned when the operation was initiated. + + + Unable to create token reference. + + + null + + + The specified nonce is too short. The minimum required nonce length is 4 bytes. + + + There is no binary negotiation to send to the other party. + + + Security negotiation failure because an incorrect Context attribute specified in RequestSecurityToken/RequestSecurityTokenResponse from the other party. + + + No binary negotiation was received from the other party. + + + The proof token was not wrapped correctly in the RequestSecurityTokenResponse. + + + Final RSTR from other party does not contain a service token. + + + The Security Support Provider Interface (SSPI) negotiation failed. + + + Cannot authenticate the other party. + + + Incoming binary negotiation has invalid ValueType {0}. + + + The channel is not open. + + + Security negotiation failed because the remote party did not send back a reply in a timely manner. This may be because the underlying transport connection was aborted. + + + SecurityVersion must be WsSecurity10 or WsSecurity11. + + + Creation time must be before expiration time. + + + Negotiation state already exists for context '{0}'. + + + Cannot find the negotiation state for the context '{0}'. + + + Send cannot be called when the session does not expect output. + + + The session was closed before message transfer was complete. + + + The item cannot be added. The maximum cache size is ({0} items). + + + The server's X509SecurityTokenProvider cannot be null. + + + Expected binary secret of type {0} but got secret of type {1}. + + + The '{0}' username token has an unsupported password type. + + + Unrecognized identity property type: '{0}'. + + + There was no channel that could accept the message with action '{0}'. + + + There was no endpoint listening at {0} that could accept the message. This is often caused by an incorrect address or SOAP action. See InnerException, if present, for more details. + + + This factory buffers messages, so the message sizes must be in the range of an integer value. + + + For TransferMode.Buffered, MaxReceivedMessageSize and MaxBufferSize must be the same value. + + + MaxBufferSize must not exceed MaxReceivedMessageSize. + + + This Factory buffers messages, so the message sizes must be in the range of a int value. + + + URI {0} could not be set because its size ({1}) exceeds the max supported size ({2}). + + + Expecting first char - c - to be in set [Char.IsLetter(c) && c == '_', found '{0}'. + + + Expecting all chars - c - of id to be in set [Char.IsLetter(c), Char.IsNumber(c), '.', '_', '-'], found '{0}'. + + + HTTP could not register URL {0}. Another application has already registered this URL with HTTP.SYS. + + + HTTP could not register URL {0}. Your process does not have access rights to this namespace (see http://go.microsoft.com/fwlink/?LinkId=70353 for details). + + + HTTP could not register URL {0} because TCP port {1} is being used by another application. + + + HTTP could not register URL {0} because the MaxEndpoints quota has been exceeded. To correct this, either close other HTTP-based services, or increase your MaxEndpoints registry key setting (see http://go.microsoft.com/fwlink/?LinkId=70352 for details). + + + The remote server returned an unexpected response: ({0}) {1}. + + + The number of bytes available is inconsistent with the HTTP Content-Length header. There may have been a network error or the client may be sending invalid requests. + + + A response was received from a one-way send over the underlying IRequestChannel. Make sure the remote endpoint has a compatible binding at its endpoint (one that contains OneWayBindingElement). + + + The receiver returned an error indicating that the content type was missing on the request to {0}. See the inner exception for more information. + + + Duplex channel to {0} was aborted during the open process. + + + Operation was aborted while establishing a connection to {0}. + + + The incoming message contains a SOAP header representing the WS-Addressing '{0}', yet the HTTP transport is configured with AddressingVersion.None. As a result, the message is being dropped. If this is not desired, then update your HTTP binding to support a different AddressingVersion. + + + There is a problem with the XML that was received from the network. See inner exception for more details. + + + An IPv4 address was specified ({0}), but IPv4 is not enabled on this machine. + + + An IPv6 address was specified ({0}), but IPv6 is not enabled on this machine. + + + Cannot find a unique port number that is available for both IPv4 and IPv6. + + + There is already a listener on IP endpoint {0}. This could happen if there is another application already listening on this endpoint or if you have multiple service endpoints in your service host with the same IP endpoint but with incompatible binding configurations. + + + Insufficient winsock resources available to complete socket connection initiation. + + + Insufficient memory avaliable to complete the operation. + + + Could not connect to {0}. TCP error code {1}: {2}. + + + Could not connect to {0}. The connection attempt lasted for a time span of {3}. TCP error code {1}: {2}. + + + A TCP error ({0}: {1}) occurred while listening on IP Endpoint={2}. + + + A TCP error ({0}: {1}) occurred while transmitting data. + + + A TCP error ({0}: {1}) occurred while transmitting data. The local IP address and port is {2}. The remote IP address and port is {3}. + + + The socket connection was aborted by your local machine. This could be caused by a channel Abort(), or a transmission error from another thread using this socket. + + + The HTTP request context was aborted while writing the response. As a result, the response may not have been completely written to the network. This can be remedied by gracefully closing the request context rather than aborting it. + + + The socket connection was aborted. This could be caused by an error processing your message or a receive timeout being exceeded by the remote host, or an underlying network resource issue. Local socket timeout was '{0}'. + + + The socket connection was aborted. This could be caused by an error processing your message or a receive timeout being exceeded by the remote host, or an underlying network resource issue. Local socket timeout was '{0}'. The local IP address and port is {1}. The remote IP address and port is {2}. + + + The socket transfer timed out after {0}. You have exceeded the timeout set on your binding. The time allotted to this operation may have been a portion of a longer timeout. + + + The socket transfer timed out after {0}. You have exceeded the timeout set on your binding. The time allotted to this operation may have been a portion of a longer timeout. The local IP address and port is {1}. The remote IP address and port is {2}. + + + The socket connection has been disposed. + + + The socket listener has been disposed. + + + The socket listener is not listening. + + + No duplex session listener was listening at {0}. This could be due to an incorrect via set on the client or a binding mismatch. + + + The entry found in AuthenticationManager's CustomTargetNameDictionary for {0} does not match the requested identity of {1}. + + + An HTTP Content-Type header is required for SOAP messaging and none was found. + + + Content Type {0} was sent to a service expecting {1}. The client and service bindings may be mismatched. + + + The content type {0} of the response message does not match the content type of the binding ({1}). If using a custom encoder, be sure that the IsContentTypeSupported method is implemented properly. The first {2} bytes of the response were: '{3}'. + + + The content type {0} of the message is not supported by the encoder. + + + The binding specified requires that the to and via URIs must match because the Addressing Version is set to None. The to URI specified was '{0}'. The via URI specified was '{1}'. + + + The server challenged this request and streamed requests cannot be resubmitted. To enable HTTP server challenges, set your TransferMode to Buffered or StreamedResponse. + + + Content Type {0} was not supported by service {1}. The client and service bindings may be mismatched. + + + Server faulted with code '{0}'. + + + Content type '{0}' is too long to be processed by the remote host. See the server logs for more details. + + + Via '{0}' is too long to be processed by the remote host. See the server logs for more details. + + + The .Net Framing mode being used is not supported by '{0}'. See the server logs for more details. + + + The .Net Framing version being used is not supported by '{0}'. See the server logs for more details. + + + The requested upgrade is not supported by '{0}'. This could be due to mismatched bindings (for example security enabled on the client and not on the server). + + + Server '{0}' sent back a fault indicating it is too busy to process the request. Please retry later. Please see the inner exception for fault details. + + + Server '{0}' sent back a fault indicating it is in the process of shutting down. Please see the inner exception for fault details. + + + Server '{0}' is too busy to process this request. Try again later. + + + Protocol Type {0} was sent to a service that does not support that type of upgrade. + + + .Net Framing upgrade request for {0} was sent to a service that is not setup to receive upgrades. + + + You have tried to create a channel to a service that does not support .Net Framing. + + + You have tried to create a channel to a service that does not support .Net Framing. It is possible that you are encountering an HTTP endpoint. + + + An error occurred while transmitting data. + + + The server rejected the upgrade request. + + + The server at {0} rejected the session-establishment request. + + + Cannot resolve the host name of URI \"{0}\" using DNS. + + + The '{0}' authentication scheme has been specified on the HTTP factory. However, the factory only supports specification of exactly one authentication scheme. Valid authentication schemes are Digest, Negotiate, NTLM, Basic, or Anonymous. + + + The value specified for the AuthenticationScheme property on the HttpTransportBindingElement ('{0}') is not allowed when building a ChannelFactory. If you used a standard binding, ensure the ClientCredentialType is not set to HttpClientCredentialType.InheritedFromHost, a value which is invalid on a client. If you set the value to '{0}' directly on the HttpTransportBindingElement, please set it to Digest, Negotiate, NTLM, Basic, or Anonymous. + + + The '{0}' authentication scheme has been specified for the proxy on the HTTP factory. However, the factory only supports specification of exactly one authentication scheme. Valid authentication schemes are Digest, Negotiate, NTLM, Basic, or Anonymous. + + + The remote HTTP server did not satisfy the mutual authentication requirement. + + + The HTTP request is unauthorized with client authentication scheme '{0}'. The authentication header received from the server was '{1}'. + + + The HTTP request with client authentication scheme '{0}' failed with '{1}' status. + + + The HTTP request was forbidden with client authentication scheme '{0}'. + + + The provided URI scheme '{0}' is invalid; expected '{1}'. + + + The HTTPS listener factory was configured to require a client certificate and the '{0}' authentication scheme. However, only one form of client authentication can be required at once. + + + Could not find an appropriate transport manager for listen URI '{0}'. + + + The specified channel listener at '{0}' is not registered with this transport manager. + + + The HTTPS channel factory does not support explicit specification of an identity in the EndpointAddress unless the authentication scheme is NTLM or Negotiate. + + + The endpoint identity specified when creating the HTTPS channel to '{0}' contains multiple server certificates. However, the HTTPS transport only supports the specification of a single server certificate. In order to create an HTTPS channel, please specify no more than one server certificate in the endpoint identity. + + + The server certificate with name '{0}' failed identity verification because its thumbprint ('{1}') does not match the one specified in the endpoint identity ('{2}'). As a result, the current HTTPS request has failed. Please update the endpoint identity used on the client or the certificate used by the server. + + + A registration already exists for URI '{0}'. + + + Could not establish secure channel for SSL/TLS with authority '{0}'. + + + Could not establish trust relationship for the SSL/TLS secure channel with authority '{0}'. + + + Could not find a compatible transport manager for URI '{0}'. + + + The SPN for the responding server at URI '{0}' could not be determined. + + + The remote server did not satisfy the mutual authentication requirement. + + + Transfer mode {0} is not supported by {1}. + + + The token provider of type '{0}' did not return a token of type '{1}'. Check the credential configuration. + + + The required UserNameSecurityToken was not provided. + + + The following remote identity failed verification: '{0}'. + + + You cannot specify an explicit Proxy Address as well as UseDefaultWebProxy=true in your HTTP Transport Binding Element. + + + The HTTP proxy authentication credential specified an impersonation level restriction ({0}) that is stricter than the restriction for target server authentication ({1}). + + + The HTTP proxy authentication credential specified an mutual authentication requirement ({0}) that is stricter than the requirement for target server authentication ({1}). + + + The NTLM authentication scheme was specified, but the target credential does not allow NTLM. + + + The impersonation level '{0}' was specified, yet HTTP Digest authentication can only support 'Impersonation' level when used with an explicit credential. + + + The scheme parameter must not be empty. + + + The protection level '{0}' was specified, yet SSL transport security only supports EncryptAndSign. + + + {0}. This often indicates that a service that HTTP.SYS depends upon (such as httpfilter) is not started. + + + {0}. This often indicates that the HTTP client has prematurely closed the underlying TCP connection. + + + Opening the channel timed out after {0}. The time allotted to this operation may have been a portion of a longer timeout. + + + Opening the {0} channel timed out after {1}. The time allotted to this operation may have been a portion of a longer timeout. + + + TimeSpan must be greater than TimeSpan.Zero. + + + TimeSpan cannot be less than TimeSpan.Zero. + + + The value of this argument must be non-negative. + + + The value of this argument must be positive. + + + The value of this argument must be greater than 0. + + + The value of this argument must fall within the range {0} to {1}. + + + The specified offset exceeds the upper bound of the buffer ({0}). + + + The specified offset exceeds the buffer size ({0} bytes). + + + The specified size exceeds the remaining buffer space ({0} bytes). + + + The space needed for encoding ({0} bytes) exceeds the message frame offset. + + + {0} returned true from OnTryCreateFaultMessage, but did not return a fault message. + + + {0} returned false from OnTryCreateFaultMessage, but returned a non-null fault message. + + + {0} returned true from OnTryCreateException, but did not return an Exception. + + + {0} returned false from OnTryCreateException, but returned a non-null Exception (See InnerException for details). + + + Policy chain contains self issued URI or a managed issuer in the wrong position. + + + The Binding with name {0} failed validation because it contains a BindingElement with type {1} which is not supported in partial trust. Consider using BasicHttpBinding or WSHttpBinding, or hosting your application in a full-trust environment. + + + The WSHttpBinding with name {0} failed validation because it contains a BindingElement with type {1} which is not supported in partial trust. Consider disabling the message security and reliable session options, using BasicHttpBinding, or hosting your application in a full-trust environment. + + + The Binding with name {0} failed validation because the Binding type {1} is not supported in partial trust. Consider using BasicHttpBinding or WSHttpBinding, or hosting your application in a full-trust environment. + + + The Service with name '{0}' could not be constructed because the application does not have permission to construct the type: both the Type and its default parameter-less constructor must be public. + + + The Method with name '{1}' in Type '{0}' could not be invoked because the application does not have permission to invoke the method: both the Method and its containing Type must be public. + + + Access to performance counters is denied. Application may be running in partial trust. Either disable performance counters or configure the application to run in full trust. + + + Performance counter instance names may not be unique. See "http://go.microsoft.com/fwlink/?LinkId=524462" for details. + + + Access to windows management instrumentation (WMI) is denied. Application may be running in partial trust. Either disable WMI or configure the application to run in full trust. + + + Unable to log messages. Application may be running in partial trust. Either disable message logging or configure the application to run in full trust. + + + The 'scopeName' argument to the InstanceKey constructor must be a non-empty string which indicates the scope of uniqueness for the key. Durable services use the service namespace and name as the scope of uniqueness. + + + The 'provider' argument to the InstanceKey constructor must be a non-empty string which identifies the source of the key data. The 'provider' argument can be null, in which case the default correlation provider name is used. + + + The 'Name' property cannot be set on an invalid InstanceKey. + + + The type {0} is not a supported result type. + + + The result cannot be represented as a nodeset. Only results of type XPathResultType.NodeSet can be represented as nodesets. + + + Message with id {0} was not in a locked state. + + + Validity of message with id {0} has expired. + + + The StreamUpgradeInitiator specified ({0}) is not supported by this IStreamUpgradeChannelBindingProvider implementation. The most likely cause of this is passing a StreamUpgradeInitiator that was not created by the StreamUpgradeProvider associated with the current IStreamUpgradeChannelBindingProvider implementation. + + + The StreamUpgradeAcceptor specified ({0}) is not supported by this IStreamUpgradeChannelBindingProvider implementation. The most likely cause of this is passing a StreamUpgradeAcceptor that was not created by the StreamUpgradeProvider associated with this IStreamUpgradeChannelBindingProvider implementation. + + + The StreamUpgradeProvider {0} does not support the specified ChannelBindingKind ({1}). + + + Extended protection is not supported on this platform. Please install the appropriate patch or change the ExtendedProtectionPolicy on the Binding or BindingElement to a value with a PolicyEnforcement value of "Never" or "WhenSupported". + + + The Authentication Scheme "Basic" does not support Extended Protection. Please use a different authentication scheme or disable the ExtendedProtectionPolicy on the Binding or BindingElement by creating a new ExtendedProtectionPolicy with a PolicyEnforcement value of "Never". + + + CustomChannelBindings are not supported. Please remove the CustomChannelBinding from the ExtendedProtectionPolicy". + + + ClientCredentialType '{0}' can only be used on the server side, not the client side. Please use one of the following values instead 'None, Basic, Client, Digest, Ntlm, Windows'. + + + When authentication schemes 'Basic' and also '{0}' are enabled, the value of IncludeWindowsGroups for Windows ('{1}') and UserName authentication ('{2}') must match. Please consider using the same value in both places. + + + The authentication schemes cannot be inherited from the host for binding '{0}'. No AuthenticationScheme was specified on the ServiceHost or in the virtual application in IIS. This may be resolved by enabling at least one authentication scheme for this virtual application in IIS, through the ServiceHost.Authentication.AuthenticationSchemes property or in the configuration at the <serviceAuthenticationManager> element. + + + The authentication schemes configured on the host ('{0}') do not allow those configured on the binding '{1}' ('{2}'). Please ensure that the SecurityMode is set to Transport or TransportCredentialOnly. Additionally, this may be resolved by changing the authentication schemes for this application through the IIS management tool, through the ServiceHost.Authentication.AuthenticationSchemes property, in the application configuration file at the <serviceAuthenticationManager> element, by updating the ClientCredentialType property on the binding, or by adjusting the AuthenticationScheme property on the HttpTransportBindingElement. + + + Object type must be an enum with the flag attribute. '{0}' is not an enum - or the flag attribute is not set. Please use an enum type with the flag attribute instead. + + + Object type must be an enum with the flag attribute and may only contain powers of two for the flags enum values or a combination of such values. Please use an enum type according to these rules. + + + There is no pending asynchronous write on this stream. Ensure that there is pending write on the stream or verify that the implementation does not try to complete the same operation multiple times. + + + Cannot write to a buffer which is currently being flushed. + + + An asynchronous write was called on the stream without a free buffer. + + + The transport configured on this binding does not appear to support the CompressionFormat specified ({0}) on the message encoder. To resolve this issue, set the CompressionFormat on the {1} to '{2}' or use a different transport. + + + The value '{1}' is not supported in this context for the binding security property '{0}'. + + + The value '{1}' is not supported in this context for the binding property '{0}'. + + + The value of MaxPendingAccepts should not be larger than {0}. + + + The initialization process of the request message timed out after {0}. To increase this quota, use the '{1}' property on the '{2}'. + + + The value '{1}' for the '{0}' property is not supported in Windows Store apps. + + + The remote endpoint requested an address for acknowledgements that is not the same as the address for application messages. The channel could not be opened because this is not supported. Ensure the endpoint address used to create the channel is identical to the one the remote endpoint was set up with. + + + The address for acknowledgements must be the same as the address for application messages. Verify that your endpoint is configured to use the same URI for these two addresses. + + + The {0}:{1} assertion is not supported. + + + An unexpected error occurred while attempting to close the output half of the duplex reliable session. + + + The remote endpoint sent conflicting requests to create a reliable session. The conflicting requests have inconsistent filter criteria such as address or action. The reliable session has been faulted. + + + The remote endpoint sent conflicting requests to create a reliable session. The remote endpoint requested both a one way and a two way session. The reliable session has been faulted. + + + A message with action {0} could not be parsed. + + + The request to create a reliable session has been refused by the RM Destination. {0} The channel could not be opened. + + + The endpoint processing requests to create a reliable session only supports sessions in which the AcksTo Uri and the Endpoint Uri are the same. + + + The endpoint processing requests to create a reliable session only supports sessions in which the AcksTo Uri and the ReplyTo Uri are the same. + + + The endpoint at {0} processes duplex sessions. The create sequence request must contain an offer for a return sequence. This is likely caused by a binding mismatch. + + + The endpoint at {0} processes input sessions. The create sequence request must not contain an offer for a return sequence. This is likely caused by a binding mismatch. + + + The request to create a reliable session contains an invalid wsrm:IncompleteSequenceBehavior value. This is a WS-ReliableMessaging protocol violation. + + + The request to create a reliable session contains the wsse:SecurityTokenReference but does not carry a wsrm:UsesSequenceSTR header. This is a WS-ReliableMessaging protocol violation. The session could not be created. + + + The endpoint at {0} processes reply sessions. The create sequence request must contain an offer for a return sequence. This is likely caused by a binding mismatch. + + + The RM Destination requires the WS-SecureConversation protocol in the binding. This is likely caused by a binding mismatch. + + + The endpoint processing requests to create a reliable session does not support sessions that use SSL. This is likely caused by a binding mismatch. The session could not be created. + + + The request to create a reliable session carries a wsrm:UsesSequenceSTR header, but does not contain the wsse:SecurityTokenReference. This is a WS-ReliableMessaging protocol violation. The session could not be created. + + + The message is not a valid SOAP message. The body contains more than 1 root element. + + + The remote endpoint replied to a request for a two way session with an offer for a one way session. This is likely caused by a binding mismatch. The channel could not be opened. + + + The client requested creation of a two way session. A one way session was created. The session cannot continue without as a one way session. This is likely caused by a binding mismatch. + + + The response to the request to create a reliable session contains an invalid wsrm:IncompleteSequenceBehavior value. This is a WS-ReliableMessaging protocol violation. + + + The remote endpoint replied to a request for a one way session with an offer for a two way session. This is a WS-ReliableMessaging protocol violation. The channel could not be opened. + + + A return sequence was not offered by the create sequence request. The create sequence response cannot accept a return sequence. + + + The remote endpoint replied to a request for a two way session with an offer for a one way session. This is a WS-ReliableMessaging protocol violation. The channel could not be opened. + + + A return sequence was offered by the create sequence request but the create sequence response did not accept this sequence. + + + The WS-RM policy under the namespace {0} requires the wsrmp:ExactlyOnce, wsrmp:AtLeastOnce, or wsrmp:AtMostOnce assertion. Nothing was found. + + + The WS-RM policy under the namespace {0} requires the wsrmp:ExactlyOnce, wsrmp:AtLeastOnce, or wsrmp:AtMostOnce assertion. The {1} element under the {2} namespace was found. + + + The remote endpoint sent a TerminateSequence protocol message before fully acknowledging all messages in the reply sequence. This is a violation of the reliable request reply protocol. The reliable session was faulted. + + + The remote endpoint has closed the underlying secure session before the reliable session fully completed. The reliable session was faulted. + + + The underlying secure session has faulted before the reliable session fully completed. The reliable session was faulted. + + + The remote endpoint has errantly sent a TerminateSequence protocol message before the sequence finished. + + + The {0}:{1} element requires a {2}:{3} child element but has the {4} child element under the {5} namespace. + + + The {0}:{1} element requires a {2}:{3} child element but has no child elements. + + + The remote endpoint specified two different last message numbers. The reliable session is in an inconsistent state since it cannot determine the actual last message. The reliable session was faulted. + + + The SequenceAcknowledgement violates the cumulative acknowledgement invariant. + + + A violation of acknowledgement protocol has been detected. An InvalidAcknowledgement fault was sent to the remote endpoint and the reliable session was faulted. + + + An acknowledgement was received indicating the remaining buffer space on the remote endpoint is {0}. This number cannot be less than zero. The reliable session was faulted. + + + A message was received with a sequence number of {0}. Sequence numbers cannot be less than 1. The reliable session was faulted. + + + An acknowledgement range starting at {0} and ending at {1} was received. This is an invalid acknowledgement range. The reliable session was faulted. + + + The remote endpoint responded to the {0} request with a response with action {1}. The response must be a {0}Response with action {2}. The channel could not be opened. + + + The remote endpoint responded to the {0} request with a response with action {1}. The response must be a {0}Response with action {2}. The channel was faulted. + + + The {0} request's response was a message with action {1}. The response must be a {0}Response with action {2}. The reliable session cannot continue. + + + A message was received with a sequence number higher than the sequence number of the last message in this sequence. This is a violation of the sequence number protocol. The reliable session was faulted. + + + The value for wsrm:MessageNumber exceeds the value of the MessageNumber accompanying a LastMessage element in this Sequence. + + + Binding validation failed because the TransportBindingElement's ManualAddressing property was set to true on a binding that is configured to create reliable sessions. This combination is not supported and the channel factory or service host was not opened. + + + The maximum retry count has been exceeded with no response from the remote endpoint. The reliable session was faulted. This is often an indication that the remote endpoint is no longer available. + + + A problem occurred while reading a message. See inner exception for details. + + + The maximum message number for this sequence has been exceeded. The reliable session was faulted. + + + The maximum value for wsrm:MessageNumber has been exceeded. + + + The {0} assertion's Milliseconds attribute does not fall within the range this binding uses. The ReliableSessionBindingElement could not be created. + + + The remote endpoint did not include a final acknowledgement in the reply to the close sequence request message. This is a violation of the WS-ReliableMessaging protocol. The reliable session was faulted. + + + The wsa:MessageId header must be present on a wsrm:{0} message. + + + The returned wsrm:{0}Response message was missing the required wsa:RelatesTo header. This is a violation of the WS-Addressing request reply protocol. The reliable session was faulted. + + + The wsa:ReplyTo header must be present on a wsrm:{0} message. + + + More than one version of the {0} assertion was found. The ReliableSessionBindingElement could not be created. + + + The endpoint only processes messages using the WS-ReliableMessaging protocol. The message sent to the endpoint does not have an action or any headers used by the protocol and cannot be processed. + + + A message with action {0} is an empty message. This message cannot be processed because the body of this WS-ReliableMessaging protocol message must carry information pertaining to a reliable session. + + + The action {0} is not supported by this endpoint. Only WS-ReliableMessaging February 2005 messages are processed by this endpoint. + + + The remote endpoint closed the session before acknowledging all responses. All replies could not be delivered. The reliable session was faulted. + + + The remote endpoint returned a {0}Response when the {0} request had not been sent. This is a WS-ReliableMessaging protocol violation. The reliable session was faulted. + + + The {0}Response was received when the {0} request had not been sent. This is a WS-ReliableMessaging protocol violation. The reliable session cannot continue. + + + The remote endpoint failed to include a required SequenceAcknowledgement header on a reliable reply message. The reliable session was faulted. + + + Due to a request context abort call, the reliable reply session channel potentially has a gap in its reply sequence. The ExactlyOnce assurance can no longer be satisfied. The reliable session was faulted. + + + The required {0} attribute is missing from the {1} element in the {2} assertion. The ReliableSessionBindingElement could not be created. + + + The {0} assertion's required Milliseconds attribute is not schema compliant. Milliseconds must be convertible to an unsigned long. The ReliableSessionBindingElement could not be created. + + + The endpoint at {0} has stopped accepting wsrm sessions. + + + The Sequence is closed and cannot accept new messages. + + + The RM Source could not transfer the last message within the timeout the user specified. + + + The server received a TerminateSequence message before all reply sequence messages were acknowledged. This is a violation of the reply sequence acknowledgement protocol. + + + The wsrm:TerminateSequence protocol message was transmitted before the sequence was successfully completed. + + + The inactivity timeout of ({0}) has been exceeded. + + + Two different wsrm:LastMsgNumber values were specified. Because of this the reliable session cannot complete. + + + The user specified maximum retry count for a particular message has been exceeded. Because of this the reliable session cannot continue. + + + The CloseSequence request's reply message must carry a final acknowledgement. This is a violation of the WS-ReliableMessaging protocol. The reliable session cannot continue. + + + Due to a user abort the reliable session cannot continue. + + + The necessary size to buffer a sequence message has exceeded the configured buffer quota. Because of this the reliable session cannot continue. + + + The session has stopped waiting for a particular reply. Because of this the reliable session cannot continue. + + + A reply message was received with no acknowledgement. + + + All of the reply sequence's messages must be acknowledged prior to closing the request sequence. This is a violation of the reply sequence's delivery guarantee. The session cannot continue. + + + The user of the remote endpoint's reliable session expects no more messages and a new message arrived. Due to this the reliable session cannot continue. + + + The wsrm:LastMsgNumber value is too small. A message with a larger sequence number has already been received. + + + The RM destination received an acknowledgement message. The RM destination does not process acknowledgement messages. + + + The RM source received an AckRequested message. The RM source does not process AckRequested messages. + + + The RM source received an CloseSequence message. The RM source does not process CloseSequence messages. + + + The RM destination received an CloseSequenceResponse message. The RM destination does not process CloseSequenceResponse messages. + + + The RM source received a CreateSequence request. The RM source does not process CreateSequence requests. + + + The RM destination received multiple CreateSequence requests with different OfferId values over the same session. + + + The RM destination received a CreateSequenceResponse message. The RM destination does not process CreateSequenceResponse messages. + + + The RM source received multiple CreateSequenceResponse messages with different sequence identifiers over the same session. + + + The RM source received a TerminateSequence message. The RM source does not process TerminateSequence messages. + + + The RM destination received a TerminateSequenceResponse message. The RM destination does not process TerminateSequenceResponse messages. + + + The RM source does not support an RM destination initiated close since messages can be lost. The reliable session cannot continue. + + + The RM source does not support an RM destination initiated termination since messages can be lost. The reliable session cannot continue. + + + An unknown error occurred while trying to add a sequence message to the window. + + + The remote endpoint specified a last message number that is smaller than a sequence number that has already been seen. The reliable session is in an inconsistent state since it cannot determine the actual last message. The reliable session was faulted. + + + The message could not be transferred within the allotted timeout of {0}. There was no space available in the reliable channel's transfer window. The time allotted to this operation may have been a portion of a longer timeout. + + + The close operation did not complete within the allotted timeout of {0}. The time allotted to this operation may have been a portion of a longer timeout. + + + The open operation did not complete within the allotted timeout of {0}. The time allotted to this operation may have been a portion of a longer timeout. + + + The operation did not complete within the allotted timeout of {0}. The time allotted to this operation may have been a portion of a longer timeout. + + + The request operation did not complete within the allotted timeout of {0}. The time allotted to this operation may have been a portion of a longer timeout. + + + The send operation did not complete within the allotted timeout of {0}. The time allotted to this operation may have been a portion of a longer timeout. + + + The remote endpoint sent an unexpected ack. Simplex servers do not process acks. + + + The remote endpoint sent an unexpected request for an ack. Simplex clients do not send acks and do not process requests for acks. + + + The remote endpoint sent an unexpected close sequence message. Simplex clients do not process this message. + + + The remote endpoint sent an unexpected close sequence response message. Simplex servers do not process this message. + + + The remote endpoint sent an unexpected request to create a sequence. Clients do not process requests for a sequence. + + + The remote endpoint sent an unexpected create sequence response. Servers do not process this message. + + + The remote endpoint sent inconsistent requests to create the same sequence. The OfferId values are not identical. + + + The remote endpoint sent inconsistent responses to the same create sequence request. The sequence identifiers are not identical. + + + The remote endpoint sent an unexpected terminate sequence message. Simplex clients do not process this message. + + + The remote endpoint sent an unexpected terminate sequence response message. Simplex servers do not process this message. + + + The remote endpoint replied to the request for a sequence with a response that could not be parsed. See inner exception for details. The channel could not be opened. + + + The value of wsrm:Identifier is not a known Sequence identifier. + + + The remote endpoint no longer recognizes this sequence. This is most likely due to an abort on the remote endpoint. {0} The reliable session was faulted. + + + The remote endpoint has sent a message containing an unrecognized sequence identifier. The reliable session was faulted. + + + The remote endpoint has sent an unrecognized fault with namespace, {0}, name {1}, and reason {2}. The reliable session was faulted. + + + The remote endpoint has sent an unrecognized fault with namespace, {0}, name {1}, and reason {2}. The channel could not be opened. + + + The remote endpoint closed the sequence before message transfer was complete. This is not supported since all messages could not be transferred. The reliable session was faulted. + + + The remote endpoint terminated the sequence before message transfer was complete. This is not supported since all messages could not be transferred. The reliable session was faulted. + + + The remote endpoint has sent an fault message with an unexpected sequence identifier over a session. The fault may be intended for a different session. The fault reason is: {0} The reliable session was faulted. + + + Binding validation failed because the WSHttpBinding does not support reliable sessions over transport security (HTTPS). The channel factory or service host could not be opened. Use message security for secure reliable messaging over HTTP. + + + The sequence has been terminated by the remote endpoint. {0} The reliable session was faulted. + + + An error occurred while processing a message. {0} + + + The returned {0}Response was carrying the a wsa:RelatesTo header that does not correlate with the wsa:MessageId header on the {0} request. This is a violation of the WS-Addressing request reply protocol. The reliable session cannot continue. + + + The remote endpoint has responded to a {0} request message with an invalid reply. The reply has a wsa:RelatesTo header with an unexpected identifier. The reliable session cannot continue. + + + The remote endpoint sent a wsrm:{0} request message with a wsa:ReplyTo address containing a URI which is not equivalent to the remote address. This is not supported. The reliable session was faulted. + + + The wsrm:{0} request message's wsa:ReplyTo address containing a URI which is not equivalent to the remote address. This is not supported. The reliable session was faulted. + + + The incoming message is not a WS-ReliableMessaging 1.1 message and could not be processed. + + + The RM server requires the use of WS-ReliableMessaging 1.1 protocol. This is likely caused by a binding mismatch. + + + The operations {0} and {1} have the same action ({2}). Every operation must have a unique action value. + + + Cannot create a typed message due to action mismatch, expecting {0} encountered {1} + + + Part {1} in message {0} cannot be exported with RPC or encoded since its type is anonymous. + + + The IAsyncResult returned from Begin and the IAsyncResult supplied to the Callback are on different objects. These are required to be the same object. + + + Binding name cannot be null or empty. + + + Value '{0}' provided for {1} property is an invalid URI. + + + Parameter value '{0}' is an invalid URI. + + + Header name cannot be null or empty. + + + Could not find a base address that matches scheme {0} for the endpoint with binding {1}. Registered base address schemes are [{2}]. + + + The scheme '{0}' used by binding {1} does not match the required scheme '{2}'. + + + Only a '{0}' using '{1}' or '{2}' is supported in this scenario. + + + MessageVersion '{0}' is not supported in this scenario. Only MessageVersion '{1}' is supported. + + + The binding associated with ServiceMetadataBehavior or ServiceDebugBehavior is not supported. The inner binding elements used by this binding must support IReplyChannel. Verify that HttpGetBinding/HttpsGetBinding (on ServiceMetadataBehavior) and HttpHelpPageBinding/HttpsHelpPageBinding (on ServiceDebugBehavior) are supported. + + + Method '{0}' in class '{1}' has bad parameter metadata: a pass-by-reference parameter is marked with the 'in' but not the 'out' parameter mode. + + + Method '{0}' in class '{1}' has bad parameter metadata: a pass-by-value parameter is marked with the 'out' parameter mode. + + + When calling the CreateFromPolicy method, the policy argument must be an XmlElement instance with LocalName '{1}' and NamespaceUri '{0}'. This XmlElement has LocalName '{3}' and NamespaceUri '{2}'. + + + The URI supplied to ServiceMetadataBehavior via the ExternalMetadataLocation property or the externalMetadataLocation attribute in the serviceMetadata section in config must be a relative URI or an absolute URI with an http or https scheme. '{0}' was specified, which is a absolute URI with {1} scheme. + + + The URL supplied to ServiceMetadataBehavior via the ExternalMetadataLocation property or the externalMetadataLocation attribute in the serviceMetadata section in config was a relative URL and there is no base address with which to resolve it. '{0}' was specified. + + + There was a problem reading the MetadataSet argument: a MetadataSection instance with identifier '{0}' and dialect '{1}' has a Metadata property whose type does not match the dialect. The expected Metadata type for this dialect is '{2}' but was found to be '{3}'. + + + Metadata contains a reference that cannot be resolved: '{0}'. + + + The MaximumResolvedReferences property of MetadataExchangeClient must be greater than or equal to one. '{0}' was specified. + + + The MetadataExchangeClient was not supplied with a MetadataReference or MetadataLocation from which to get metadata. You must supply one to the constructor, to the GetMetadata method, or to the BeginGetMetadata method. + + + The MetadataExchangeClient could not create an IChannelFactory for: address='{0}', dialect='{1}', and identifier='{2}'. + + + The MetadataExchangeClient could not create an HttpWebRequest for: address='{0}', dialect='{1}', and identifier='{2}'. + + + The MetadataExchangeClient instance could not be initialized because no Binding is available for scheme '{0}'. You can supply a Binding in the constructor, or specify a configurationName. + + + The TransactionProtocol setting was not understood. A supported protocol must be specified. + + + The MetadataResolver cannot recieve an empty contracts argument to the Resolve or BeginResolve methods. You must supply at least one ContractDescription. + + + The ContractDescriptions in contracts must all have unique Name and Namespace pairs. More than one ContractDescription had the pair Name='{0}' and Namespace='{1}'. + + + The contracts argument to the Resolve or BeginResolve methods cannot contain a null ContractDescription. + + + The binding specified to do metadata exchange does not contain a TransportBindingElement. + + + The binding (Name={0}, Namespace={1}) does not contain a TransportBindingElement. + + + Body object cannot be null in message {0} + + + Type {0} cannot inherit from any class other than object to be used as body object in RPC style. + + + Type {0} implements interface {1} which is not supported for body object in RPC style. + + + CallbackBehaviorAttribute can only be run as a behavior on an endpoint with a duplex contract. Contract '{0}' is not duplex, as it contains no callback operations. + + + This operation would deadlock because the reply cannot be received until the current Message completes processing. If you want to allow out-of-order message processing, specify ConcurrencyMode of Reentrant or Multiple on {0}. + + + In order to use the contract '{0}' with DuplexChannelFactory, the contract must specify a valid callback contract. If your contract does not have a callback contract, consider using ChannelFactory instead of DuplexChannelFactory. + + + The dispatch instance for duplex callbacks cannot be activated - you must provide an instance. + + + ServiceHostBase's AddBaseAddress method cannot be called after the InitializeDescription method has completed. + + + Cannot make a call on this channel because a call to Open() is in progress. + + + The MetadataExchangeClient can only get metadata from absolute addresses. It cannot get metadata from '{0}'. + + + The MetadataExchangeClient can only get metadata from http or https addresses when using MetadataExchangeClientMode HttpGet. It cannot get metadata from '{0}'. + + + The MetadataExchangeClient can only get metadata from http and https MetadataLocations. It cannot get metadata from '{0}'. + + + The configured policy specifies more than one TransactionProtocol across the operations. A single TransactionProtocol for each endpoint must be specified. + + + Generating message contract since the operation {0} is neither RPC nor document wrapped. + + + Generating message contract since the wrapper namespace ({1}) of message {0} does not match the default value ({2}) + + + Generating message contract since the wrapper name ({1}) of message {0} does not match the default value ({2}) + + + Generating message contract since element name {0} from namespace {1} is not marked nillable + + + Generating message contract since message {0} requires protection. + + + Headers are not supported in RPC encoded format. Headers are ignored in message {0}. + + + Generating message contract since message {0} has headers + + + Generating message contract since the operation {0} has untyped Message as argument or return type + + + Generating message contract since message part namespace ({0}) does not match the default value ({1}) + + + There are two contracts listening on the same binding ({2}) and address with conflicting settings. Specifically, the contract '{0}' specifies SessionMode.NotAllowed while the contract '{1}' specifies SessionMode.Required. You should either change one of the SessionMode values or specify a different address (or ListenUri) for each endpoint. + + + This collection does not support setting extensions by index. Please consider using the InsertItem or RemoveItem methods. + + + This ChannelDispatcher is not currently attached to the provided ServiceHost. + + + Cannot add a ChannelDispatcher to more than one ServiceHost. + + + Cannot open ChannelDispatcher because it is not attached to a ServiceHost. + + + Cannot open ChannelDispatcher because it is does not have a MessageVersion set. + + + The ChannelDispatcher at '{0}' is unable to open its IChannelListener as there are no endpoints for the ChannelDispatcher. + + + The ChannelDispatcher at '{0}' with contract(s) '{1}' is unable to open its IChannelListener. + + + The type argument passed to the generic ChannelFactory class must be an interface type. + + + ApplyConfiguration requires that the Endpoint property be initialized. Either provide a valid ServiceEndpoint in the CreateDescription method or override the ApplyConfiguration method to provide an alternative implementation. + + + CreateFactory requires that the Endpoint property be initialized. Either provide a valid ServiceEndpoint in the CreateDescription method or override the CreateFactory method to provide an alternative implementation. + + + An operation marked as IsTerminating has already been invoked on this channel, causing the channel's connection to terminate. No more operations may be invoked on this channel. Please re-create the channel to continue communication. + + + This channel can no longer be used to send messages as the output session was auto-closed due to a server-initiated shutdown. Either disable auto-close by setting the DispatchRuntime.AutomaticInputSessionShutdown to false, or consider modifying the shutdown protocol with the remote server. + + + Array of type {0} is not supported. + + + Can only store into ArgBuilder or LocalBuilder. Got: {0}. + + + Expecting End {0}. + + + {0} is not assignable from {1}. + + + No conversion possible to {0}. + + + CODEGEN: {0} + + + Internal Error: Unrecognized constant type {0}. + + + This collection does not support setting items by index. + + + This operation is not supported because the collection is read-only. + + + The collection of type {0} does not support values of type {1}. + + + Top level XML element with name {0} in namespace {1} cannot reference {2} type because it already references a different type ({3}). Use a different operation name or MessageBodyMemberAttribute to specify a different name for the Message or Message parts. + + + Duplicate top level XML Schema type with name {0} in namespace {1}. + + + The value of OperationContext.Current is not the OperationContext value installed by this OperationContextScope. + + + ContractDescription's Name must be a non-empty string. + + + ContractDescription '{0}' has zero operations; a contract must have at least one operation. + + + ContractDescription '{0}' has zero IsInitiating=true operations; a contract must have at least one IsInitiating=true operation. + + + The service class of type {0} both defines a ServiceContract and inherits a ServiceContract from type {1}. Contract inheritance can only be used among interface types. If a class is marked with ServiceContractAttribute, it must be the only type in the hierarchy with ServiceContractAttribute. Consider moving the ServiceContractAttribute on type {1} to a separate interface that type {1} implements. + + + The service class of type {0} both defines a ServiceContract and inherits a ServiceContract from type {1}. Contract inheritance can only be used among interface types. If a class is marked with ServiceContractAttribute, then another service class cannot derive from it. + + + SynchronizedReadOnlyCollection's CopyTo only works if the underlying list implements ICollection. + + + The callback contract of contract {0} either does not exist or does not define any operations. If this is not a duplex contract, consider using ChannelFactory instead of DuplexChannelFactory. + + + This CreateChannel overload cannot be called on this instance of DuplexChannelFactory, as the DuplexChannelFactory was not initialized with an InstanceContext. Please call the CreateChannel overload that takes an InstanceContext. + + + This CreateChannel overload cannot be called on this instance of DuplexChannelFactory, as the DuplexChannelFactory was initialized with a Type and no valid InstanceContext was provided. Please call the CreateChannel overload that takes an InstanceContext. + + + This CreateChannel overload cannot be called on this instance of DuplexChannelFactory, as the InstanceContext provided to the DuplexChannelFactory does not contain a valid UserObject. + + + The InstanceContext provided to the ChannelFactory contains a UserObject that does not implement the CallbackContractType '{0}'. + + + ChannelFactory does not support the contract {0} as it defines a callback contract with one or more operations. Please consider using DuplexChannelFactory instead of ChannelFactory. + + + The CustomBinding on the ServiceEndpoint with contract '{0}' lacks a TransportBindingElement. Every binding must have at least one binding element that derives from TransportBindingElement. + + + The Scheme cannot be computed for this binding because this CustomBinding lacks a TransportBindingElement. Every binding must have at least one binding element that derives from TransportBindingElement. + + + The formatter threw an exception while trying to deserialize the message: {0} + + + This operation is not possible since the dictionary is empty. + + + The type or member named '{0}' could not be loaded because it has two incompatible attributes: '{1}' and '{2}'. To fix the problem, remove one of the attributes from the type or member. + + + The endpoint's address is not specified. + + + The endpoint's contract is not specified. + + + The endpoint's binding is not specified. + + + The channel is configured to use interactive initializer '{0}', but the channel was Opened without calling DisplayInitializationUI. Call DisplayInitializationUI before calling Open or other methods on this channel. + + + AllowInitializationUI was set to false for this channel, but the channel is configured to use the '{0}' as an interactive initializer. + + + This is a Windows&#169; Communication Foundation service.<BR/><BR/><B>Metadata publishing for this service is currently disabled.</B><BR/><BR/>If you have access to the service, you can enable metadata publishing by completing the following steps to modify your web or application configuration file:<BR/><BR/>1. Create the following service behavior configuration, or add the &lt;serviceMetadata&gt; element to an existing service behavior configuration: + + + 2. Add the behavior configuration to the service: + + + Note: the service name must match the configuration name for the service implementation.<BR/><BR/>3. Add the following endpoint to your service configuration: + + + Note: your service must have an http base address to add this endpoint.<BR/><BR/>The following is an example service configuration file with metadata publishing enabled: + + + For more information on publishing metadata please see the following documentation: <a href="http://go.microsoft.com/fwlink/?LinkId=65455">http://go.microsoft.com/fwlink/?LinkId=65455</a>. + + + Note: the service name must match the configuration name for the service implementation. + + + Add the following endpoint. + + + Note: your service must have an http base address to add this endpoint. + + + Add the following element to your service behavior configuration. + + + <P class='intro'><B>C#</B></P> + + + <P class='intro'><B>Visual Basic</B></P> + + + Service + + + {0} Service + + + You have created a service.<P class='intro'>To test this service, you will need to create a client and use it to call the service. You can do this using the svcutil.exe tool from the command line with the following syntax:</P> + + + You have created a service.<P class='intro'>To test this service, you will need to create a client and use it to call the service; however, metadata publishing via ?WSDL is currently disabled. This can be enabled via the service's configuration file. </P> + + + This will generate a configuration file and a code file that contains the client class. Add the two files to your client application and use the generated client class to call the Service. For example:<BR/> + + + Use the 'client' variable to call operations on the service. + + + Always close the client. + + + The service encountered an error. + + + Operation '{0}' could not be loaded as it uses an unsupported combination of Use and Style settings: Document with Encoded. To fix the problem, change the Use setting to Literal or change the Style setting to Rpc. + + + Fault could not be loaded as the Use setting is Encoded and it references a schema definition using Element attribute. To fix the problem, change the Use setting to Literal. + + + Message part {0} in namespace {1} appears more than once in Message. + + + This service has multiple endpoints listening at '{0}' which share the same initiating action '{1}'. As a result, messages with this action would be dropped since the dispatcher would not be able to determine the correct endpoint for handling the message. Please consider hosting these Endpoints at separate ListenUris. + + + The IEndpointBehavior '{0}' cannot be used on the server side; this behavior can only be applied to clients. + + + Cannot add EndpointDispatcher to more than one ChannelDispatcher. + + + This EndpointDispatcher is not currently attached to the provided ChannelDispatcher. + + + Error creating a reader for the MTOM message + + + Error in deserializing body of request message for operation '{0}'. + + + Error in deserializing body of request message for operation '{0}'. {1} + + + Error in deserializing body of reply message for operation '{0}'. + + + Error in deserializing body of reply message for operation '{0}'. {1} + + + There was an error in serializing body of message {0}: '{1}'. Please see InnerException for more details. + + + There was an error in deserializing one of the headers in message {0}. Please see InnerException for more details. + + + There was an error in serializing one of the headers in message {0}: '{1}'. Please see InnerException for more details. + + + Server returned an invalid SOAP Fault. Please see InnerException for more details. + + + An error occurred while loading attribute '{0}' on type '{1}'. Please see InnerException for more details. + + + An error occurred while loading attribute '{0}' on method '{1}' in type '{2}'. Please see InnerException for more details. + + + An error occurred while loading attribute '{0}' on parameter {1} of method '{2}' in type '{3}'. Please see InnerException for more details. + + + An error occurred while loading attribute '{0}'. Please see InnerException for more details. + + + --- End of inner ExceptionDetail stack trace --- + + + An ExceptionDetail, likely created by IncludeExceptionDetailInFaults=true, whose value is: + + + Internal Error: Message must be a valid IMethodCallMessage. + + + The specified ContractDescription could not be exported to WSDL because the Type property of the MessagePartDescription with name '{1}' in the OperationDescription with name '{0}' is not set. The Type property must be set in order to create WSDL. + + + Fault named {0} in operation {1} cannot be imported. {2} + + + In operation {0}, more than one fault is declared with detail type {1} + + + In operation {0}, more than one fault is declared with element name {1} in namespace {2} + + + {0}: {1} (Fault Detail is equal to {2}). + + + The creator of this fault did not specify a Reason. + + + In operation {0}, the schema type corresponding to the fault detail type {1} is anonymous. Please set Fault name explicitly to export anonymous types. + + + Header name mismatch in member {1} of type {0}. The header name found in the description is {2}. The element name deduced by the formatter is {3}. This mismatch can happen if the ElementName specified in XmlElementAttribute or XmlArrayAttribute does not match the name specified in the MessageHeaderAttribute or MessageHeaderArrayAttribute or the member name. + + + Header name mismatch in operation {0} from contract {1}:{2}. The header name found in the description is {3}. The element name deduced by the formatter is {4}. This mismatch can happen if the ElementName specified in XmlElementAttribute or XmlArrayAttribute does not match the name specified in the MessageHeaderAttribute or MessageHeaderArrayAttribute or the member name. + + + Header namespace mismatch in member {1} of type {0}. The header namespace found in the description is {2}. The element namespace deduced by the formatter is {3}. This mismatch can happen if the Namespace specified in XmlElementAttribute or XmlArrayAttribute does not match the namespace specified in the MessageHeaderAttribute or MessageHeaderArrayAttribute or the contract namespace. + + + Header namespace mismatch in operation {0} from contract {1}:{2}. The header namespace found in the description is {3}. The element namespace deduced by the formatter is {4}. This mismatch can happen if the Namespace specified in XmlElementAttribute or XmlArrayAttribute does not match the namespace specified in the MessageHeaderAttribute or MessageHeaderArrayAttribute or the contract namespace. + + + The header '{0}' from the namespace '{1}' was not understood by the recipient of this message, causing the message to not be processed. This error typically indicates that the sender of this message has enabled a communication protocol that the receiver cannot process. Please ensure that the configuration of the client's binding is consistent with the service's binding. + + + Message {0} must not have headers to be used in RPC encoded style. + + + This value cannot be changed after the ServiceHost has opened. + + + This value cannot be changed after the ChannelFactory has opened. + + + This value cannot be changed after the first ClientBase of type '{0}' has been created. + + + {0} cannot be changed after the ServiceHost has opened. + + + Operation {0} binding {1} has extra part {2} that is not present in other bindings + + + Style {1} on header {0} does not match expected style {2}. + + + All parts of message in operation {0} must either contain type or element. + + + Style {1} inferred from messages in operation {0} does not match expected style {2} specified via bindings. + + + Bindings for operation {0} cannot specify different use and style values. Binding {1} specifies use {2} and style {3} while binding {4} specifies use {5} and style {6}. + + + Extensions for operation {0} in binding {1} cannot specify different use values. + + + Message bindings for operation {0} in binding {1} cannot specify different use values. + + + Fault bindings for operation {0} in binding {1} cannot specify different use values. + + + Service implementation object invoked with wrong number of input parameters, operation expects {0} parameters but was called with {1} parameters. + + + Service implementation object invoked with null input parameters, but operation expects {0} parameters. + + + The InstanceContext has no provider for creating Service implementation objects. + + + This OperationContextScope is being disposed out of order. + + + The server was unable to process the request due to an internal error. For more information about the error, either turn on IncludeExceptionDetailInFaults (either from ServiceBehaviorAttribute or from the <serviceDebug> configuration behavior) on the server in order to send the exception information back to the client, or turn on tracing as per the Microsoft .NET Framework SDK documentation and inspect the server trace logs. + + + The client was unable to process the callback request due to an internal error. For more information about the error, either turn on IncludeExceptionDetailInFaults (either from CallbackBehaviorAttribute or from the <clientDebug> configuration behavior) on the client in order to send the exception information back to the server, or turn on tracing as per the Microsoft .NET Framework SDK documentation and inspect the client trace logs. + + + IAsyncResult's State must be the state argument passed to your Begin call. + + + IAsyncResult not provided or of wrong type. + + + The CallbackContract {0} is invalid because it is not an interface type. + + + Invalid IContextChannel passed to OperationContext. Must be either a server dispatching channel or a client proxy channel. + + + This OperationContextScope is being disposed on a different thread than it was created. + + + OperationFormatter encountered an invalid Message body. Expected to find node type 'Element' with name '{0}' and namespace '{1}'. Found node type '{2}' with name '{3}' and namespace '{4}' + + + The OperationFormatter could not deserialize any information from the Message because the Message is empty (IsEmpty = true). + + + There was an error while trying to serialize parameter {0}:{1}. The InnerException message was '{2}'. Please see InnerException for more details. + + + There was an error while trying to deserialize parameter {0}:{1}. Please see InnerException for more details. + + + There was an error while trying to deserialize parameter {0}:{1}. The InnerException message was '{2}'. Please see InnerException for more details. + + + The operation {0} either has a parameter or a return type that is attributed with MessageContractAttribute. In order to represent the request message using a Message Contract, the operation must have a single parameter attributed with MessageContractAttribute. In order to represent the response message using a Message Contract, the operation's return value must be a type that is attributed with MessageContractAttribute and the operation may not have any out or ref parameters. + + + MessageHeaderArrayAttribute found on member {0} is not a single dimensional array. + + + Outgoing request message for operation '{0}' specified Action='{1}', but contract for that operation specifies Action='{2}'. The Action specified in the Message must match the Action in the contract, or the operation contract must specify Action='*'. + + + Outgoing reply message for operation '{0}' specified Action='{1}', but contract for that operation specifies ReplyAction='{2}'. The Action specified in the Message must match the ReplyAction in the contract, or the operation contract must specify ReplyAction='*'. + + + In order to use Streams with the MessageContract programming model, the type {0} must have a single member with MessageBodyMember attribute and the member type must be Stream. + + + For request in operation {0} to be a stream the operation must have a single parameter whose type is Stream. + + + For response in operation {0} to be a stream the operation must have a single out parameter or return value whose type is Stream. + + + Buffer size must be at least {0} bytes. + + + The PrimitiveOperationFormatter was given a parameter or return type which it does not support. + + + The static CreateChannel method cannot be used with the contract {0} because that contract defines a callback contract. Please try using one of the static CreateChannel overloads on DuplexChannelFactory<TChannel>. + + + XmlSerializer attribute {0} is not valid in {1}. Only SoapElement attribute is supported. + + + XmlSerializer attribute {0} is not valid in {1}. Only XmlElement, XmlArray, XmlArrayItem and XmlAnyElement attributes are supported in MessageContract when IsWrapped is false. + + + XmlSerializer attribute {0} is not valid in {1}. Only XmlElement, XmlArray, XmlArrayItem, XmlAnyAttribute and XmlAnyElement attributes are supported when IsWrapped is true. + + + {0} must contain either a single ServiceKnownTypeAttribute that refers to a method or a set of ServiceKnownTypeAttributes, each specifying a valid type + + + The return type of method {1} in type {2} must be IEnumerable<Type> to be used by ServiceKnownTypeAttribute in {0} + + + ServiceKnownTypeAttribute in {0} refers to a method {1} that does not exist in type {2} + + + KnownType cannot be null in operation {0} + + + The type {1} defines a MessageContract but also derives from a type {0} that does not define a MessageContract. All of the objects in the inheritance hierarchy of {1} must defines a MessageContract. + + + The message cannot be deserialized into MessageContract type {0} since it does not have a default (parameterless) constructor. + + + MessageOperationFormatter cannot serialize faults. + + + The value '{0}' is not valid for the Location property. The Location property must be a valid absolute or relative URI. + + + Method {0} is not supported on this proxy, this can happen if the method is not marked with OperationContractAttribute or if the interface type is not marked with ServiceContractAttribute. + + + Callback method {0} is not supported, this can happen if the method is not marked with OperationContractAttribute or if its interface type is not the target of the ServiceContractAttribute's CallbackContract. + + + ServiceHost implementation type {0} does not implement ServiceContract {1}. + + + A DispatchOperation (or ClientOperation) can only be added to its parent DispatchRuntime (or ClientRuntime). + + + No Action header was found with namespace '{0}' for the given message. + + + Calling Post() on '{0}' resulted in multiple callbacks. This indicates a problem in '{0}'. + + + The callback passed to operation '{0}' was called more than once. This indicates an internal error in the implementation of that operation. + + + Method {0} in type {1} has more than one header part of type array of XmlElement. + + + A ServiceContract has more the one operation with an Action of "*". A ServiceContract can have at most one operation an Action = "*". + + + The Service contains multiple ServiceEndpoints with different ContractDescriptions which each have Name='{0}' and Namespace='{1}'. Either provide ContractDescriptions with unique Name and Namespaces, or ensure the ServiceEndpoints have the same ContractDescription instance. + + + Part {1}:{0} is repeating and is not supported in Soap Encoding. + + + The Name property must be a non-empty string. + + + The ConfigurationName property must be a non-empty string. + + + Cannot handle invocation of {0} on interface {1} because the OperationSelector on ClientRuntime is null. + + + The service type provided could not be loaded as a service because it does not have a default (parameter-less) constructor. To fix the problem, add a default constructor to the type, or pass an instance of the type to the host. + + + The contract specified by type '{0}' is ambiguous. The type derives from at least two different types that each define its own service contract. For this type to be used as a contract type, exactly one of its inherited contracts must be more derived than any of the others. + + + Extension {0} prevented call to operation '{1}' from replying by setting the reply to null. + + + Formatter {0} returned a null reply message for call to operation '{1}'. + + + The operation '{0}' could not be completed because the sessionful channel timed out waiting to receive a message. To increase the timeout, either set the receiveTimeout property on the binding in your configuration file, or set the ReceiveTimeout property on the Binding directly. + + + {0} must be a relative URI or an absolute URI with scheme '{1}'. '{2}' is an absolute URI with scheme '{3}'. + + + The HttpGetEnabled property of ServiceMetadataBehavior is set to true and the HttpGetUrl property is a relative address, but there is no http base address. Either supply an http base address or set HttpGetUrl to an absolute address. + + + The HttpsGetEnabled property of ServiceMetadataBehavior is set to true and the HttpsGetUrl property is a relative address, but there is no https base address. Either supply an https base address or set HttpsGetUrl to an absolute address. + + + The ChannelDispatcher with ListenUri '{0}' has endpoints with the following contracts: {1}. Metadata endpoints cannot share ListenUris. The conflicting endpoints were either specified in AddServiceEndpoint() calls, in a config file, or a combination of AddServiceEndpoint() and config. + + + Service implementation type is an interface or abstract class and no implementation object was provided. + + + This property sets EnableFaults on the client. To set EnableFaults on the server, use ChannelDispatcher's EnableFaults. + + + This property sets ManualAddressing on the client. To set ManualAddressing on the server, use ChannelDispatcher's ManualAddressing. + + + TransactedBatchingBehavior validation failed. Service or client cannot be started. Transacted batching is not supported for session contracts. Remove transacted batching behavior from the endpoint or define a non-sessionful contract. + + + TransactedBatchingBehavior validation failed. Service cannot be started. Transacted batching requires ServiceBehavior.ReleaseServiceInstanceOnTransactionComplete to be false. + + + The service implementation object was not initialized or is not available. + + + The WS-Addressing "none" value is not valid for the August 2004 version of WS-Addressing. + + + An object that is not an exception was thrown. + + + The operation '{0}' cannot be the first operation to be called because IsInitiating is false. + + + There was no CLR type specified for parameter {0}, preventing the operation from being generated. + + + The one-way operation '{1}' on ServiceContract '{0}' is configured for transaction flow. Transactions cannot be flowed over one-way operations. + + + The incoming message with action could not be processed because it is targeted at a request-reply operation, but cannot be replied to as the MessageId property is not set. + + + OperationBehaviorAttribute can only go on the service class, it cannot be put on the ServiceContract interface. Method '{0}' on type '{1}' violates this. + + + The ReleaseInstanceMode property on OperationBehaviorAttribute can only be set on non-callback operations. Method '{0}' violates this. + + + Method '{0}' has OperationContractAttribute, but enclosing type '{1}' does not have ServiceContractAttribute. OperationContractAttribute can only be used on methods in ServiceContractAttribute types or on their CallbackContract types. + + + Method '{1}' has {0}, but enclosing type '{2}' does not have ServiceContractAttribute. {0} can only be used on methods in ServiceContractAttribute types. + + + OperationDescription's Name must be a non-empty string. + + + All parameter names used in operations that make up a service contract must not be null. + + + OperationDescription '{0}' is invalid because its Messages property contains an invalid number of MessageDescription instances. Each OperationDescription must have one or two messages. + + + There was a mismatch between the number of supplied arguments and the number of expected arguments. Specifically, the argument '{0}' has '{1}' elements while the argument '{2}' has '{3}' elements. + + + The 'parameters' argument must be an array that contains a single Message object. + + + The 'parameters' argument must be either null or an empty array. + + + The 'parameters' argument must be an array of one element. + + + Message part name {0} is not unique in an RPC Message. + + + The contract '{0}' has at least one operation annotated with '{1}', but the binding used for the contract endpoint at address '{2}' does not support required binding property '{3}'. Please ensure that the binding used for the contract supports the ReceiveContext capability. + + + Required message property '{0}' is missing from the IncomingProperties collections of the received message. Ensure that when the receive context is enabled on the binding, the created channel ensures that '{0}' is present on all received messages. + + + The request message has ReplyTo='{0}' but IContextChannel.LocalAddress is '{1}'. When ManualAddressing is false, these values must be the same, null, or EndpointAddress.AnonymousAddress. Enable ManualAddressing or avoid setting ReplyTo on the message. + + + The request message has FaultTo='{0}' but IContextChannel.LocalAddress is '{1}'. When ManualAddressing is false, these values must be the same, null, or EndpointAddress.AnonymousAddress. Enable ManualAddressing or avoid setting FaultTo on the message. + + + The request message has From='{0}' but IContextChannel.LocalAddress is '{1}'. When ManualAddressing is false, these values must be the same, null, or EndpointAddress.AnonymousAddress. Enable ManualAddressing or avoid setting From on the message. + + + The request message has ReplyTo='{0}' but IContextChannel.RemoteAddress is '{1}'. When ManualAddressing is false, these values must be the same, null, or EndpointAddress.AnonymousAddress because sending a reply to a different address than the original sender can create a security risk. If you want to process such messages, enable ManualAddressing. + + + The request message has FaultTo='{0}' but IContextChannel.RemoteAddress is '{1}'. When ManualAddressing is false, these values must be the same, null, or EndpointAddress.AnonymousAddress because sending a reply to a different address than the original sender can create a security risk. If you want to process such messages, enable ManualAddressing. + + + The request message has From='{0}' but IContextChannel.RemoteAddress is '{1}'. When ManualAddressing is false, these values must be the same, null, or EndpointAddress.AnonymousAddress because sending a reply to a different address than the original sender can create a security risk. If you want to process such messages, enable ManualAddressing. + + + A message was received with a WS-Addressing ReplyTo or FaultTo header targeted at the "None" address. These values are not valid for request-reply operations. Please consider using a one-way operation or enabling ManualAddressing if you need to support ReplyTo or FaultTo values of "None." + + + This request operation did not receive a reply within the configured timeout ({0}). The time allotted to this operation may have been a portion of a longer timeout. This may be because the service is still processing the operation or because the service was unable to send a reply message. Please consider increasing the operation timeout (by casting the channel/proxy to IContextChannel and setting the OperationTimeout property) and ensure that the service is able to connect to the client. + + + This request operation sent to {0} did not receive a reply within the configured timeout ({1}). The time allotted to this operation may have been a portion of a longer timeout. This may be because the service is still processing the operation or because the service was unable to send a reply message. Please consider increasing the operation timeout (by casting the channel/proxy to IContextChannel and setting the OperationTimeout property) and ensure that the service is able to connect to the client. + + + A reply message was received for operation '{0}' with action '{1}'. However, your client code requires action '{2}'. + + + Required runtime property '{0}' is not initialized on DispatchRuntime. Do not remove ServiceBehaviorAttribute from ServiceDescription.Behaviors or ensure that you include a third-party service behavior that supplies this value. + + + The MetadataExchangeClient has resolved more than MaximumResolvedReferences. + + + The 'result' argument must be of type Message. + + + Could not revert impersonation on current thread. Continuing would compromise system security. Terminating process. + + + RPC Message {1} in operation {0} has an invalid body name {2}. It must be {3} + + + RPC Message {1} in operation {0} must have a single MessageBodyMember. + + + There was a problem loading the XSD documents provided: a reference to a schema element with name '{0}' and namespace '{1}' could not be resolved because the element definition could not be found in the schema for targetNamespace '{1}'. Please check the XSD documents provided and try again. + + + There was a problem loading the XSD documents provided: a reference to a schema type with name '{0}' and namespace '{1}' could not be resolved because the type definition could not be found in the schema for targetNamespace '{1}'. Please check the XSD documents provided and try again. + + + Service description message '{1}' from target namespace '{2}' does not contain part named '{0}'. + + + Schema with target namespace '{0}' could not be found. + + + SecurityContextProperty is missing from the request Message, this may indicate security is configured incorrectly. + + + The server did not provide a meaningful reply; this might be caused by a contract mismatch, a premature session shutdown or an internal server error. + + + Endpoints cannot be added after the ServiceHost has been opened/faulted/aborted/closed. + + + Endpoints cannot be added before the Description property has been initialized. + + + ApplyConfiguration requires that the Description property be initialized. Either provide a valid ServiceDescription in the CreateDescription method or override the ApplyConfiguration method to provide an alternative implementation. + + + LoadConfigurationSection requires that the Description property be initialized. Provide a valid ServiceDescription in the CreateDescription method. + + + InitializeRuntime requires that the Description property be initialized. Either provide a valid ServiceDescription in the CreateDescription method or override the InitializeRuntime method to provide an alternative implementation. + + + InitializeDescription must be called with a serviceType or singletonInstance parameter. + + + Header properties cannot be set in MessageHeaderAttribute of {0} as its type is MessageHeader<T>. + + + An exception has been thrown when reading the stream. + + + The message containing this stream has been closed. Note that request streams cannot be accessed after the service operation returns. + + + The message containing this stream has been closed. + + + This channel cannot send any more messages because IsTerminating operation '{0}' has already been called. + + + Throttle limit must be greater than zero. To disable, set to Int32.MaxValue. + + + The timeout value provided was not of a recognized format. Please see InnerException for more details. + + + Timeout must be greater than or equal to TimeSpan.Zero. To disable timeout, specify TimeSpan.MaxValue. + + + Timeouts larger than Int32.MaxValue TotalMilliseconds (approximately 24 days) cannot be honored. To disable timeout, specify TimeSpan.MaxValue. + + + Cannot create a unique part name for {0}. + + + An unrecognized element was encountered in the XML during deserialization which was ignored. + + + TransactedBatchingBehavior validation failed. The service endpoint cannot be started. TransactedBatchingBehavior requires a binding that contains a binding element ITransactedBindingElement that returns true for ITransactedBindingElement.TransactedReceiveEnabled. If you are using NetMsmqBinding or MsmqIntegrationBinding make sure that ExactlyOnce is set to true. + + + TThe operation '{1}' on contract '{0}' is configured with TransactionAutoComplete set to false and with ConcurrencyMode not set to Single. TransactionAutoComplete set to false requires ConcurrencyMode.Single. + + + The '{0}' service is configured with ReleaseServiceInstanceOnTransactionComplete set to true, but the ConcurrencyMode is not set to Single. The ReleaseServiceInstanceOnTransactionComplete requires the use of ConcurrencyMode.Single. + + + The '{0}' service is configured with EnsureOrderedDispatch set to true, but the ConcurrencyMode is not set to Single. EnsureOrderedDispatch requires the use of ConcurrencyMode.Single. + + + The DispatchRuntime.EnsureOrderedDispatch property is set to true, but the DispatchRuntime.ConcurrencyMode is not set to Single. EnsureOrderedDispatch requires the use of ConcurrencyMode.Single. + + + The service does not support concurrent transactions. + + + The transaction under which this method call was executing was asynchronously aborted. + + + The SetTransactionComplete method was called in the operation '{0}' on contract '{1}' when TransactionAutoComplete was set to true. The SetTransactionComplete method can only be called when TransactionAutoComplete is set to false. This is an invalid scenario and the current transaction was aborted. + + + The SetTransactionComplete method was wrongly called more than once in the operation '{0}' on contract '{1}'. The SetTransactionComplete method can only be called once. This is an invalid scenario and the current transaction was aborted. + + + The binding for the endpoint at address '{0}' is configured with both the MsmqTransportBindingElement and the TransactionFlowBindingElement. These two elements cannot be used together. + + + The operation '{1}' on contract '{0}' is configured with TransactionAutoComplete set to false and the InstanceContextMode is not set to PerSession. TransactionAutoComplete set to false requires the use of InstanceContextMode.PerSession. + + + The operation '{0}' on callback contract '{1}' is configured with TransactionAutoComplete set to false. TransactionAutoComplete set to false cannot be used with operations on callback contracts. + + + The operation '{1}' on contract '{0}' is configured with TransactionAutoComplete set to false but SessionMode is not set to Required. TransactionAutoComplete set to false requires SessionMode.Required. + + + The service '{0}' is configured with TransactionAutoCompleteOnSessionClose set to true and with an InstanceContextMode not set to PerSession. TransactionAutoCompleteOnSessionClose set to true requires an instancing mode that uses sessions. + + + The service '{0}' is configured with a TransactionTimeout but no operations are configured with TransactionScopeRequired set to true. TransactionTimeout requires at least one operation with TransactionScopeRequired set to true. + + + The service '{0}' is configured with a TransactionIsolationLevel but no operations are configured with TransactionScopeRequired set to true. TransactionIsolationLevel requires at least one operation with TransactionScopeRequired set to true. + + + The service '{0}' is configured with ReleaseServiceInstanceOnTransactionComplete but no operations are configured with TransactionScopeRequired set to true. The ReleaseServiceInstanceOnTransactionComplete property requires at least one operation with TransactionScopeRequired set to true. Remove the ReleaseServiceInstanceOnTransactionComplete property from the service if this is the case. + + + The service '{0}' is configured with TransactionAutoCompleteOnSessionClose, but no operations are configured with TransactionScopeRequired set to true. The TransactionAutoCompleteOnSessionClose property requires at least one operation with TransactionScopeRequired set to true. Remove the TransactionAutoCompleteOnSessionClose property from the service if this is the case. + + + The service operation requires a transaction to be flowed. + + + The flowed transaction could not be unmarshaled. The following exception occurred: {0} + + + The incoming transaction cannot be deserialized. The transaction header in the message was either malformed or in an unrecognized format. The client and the service must be configured to use the same protocol and protocol version. The following exception occurred: {0} + + + The transaction header '{0}' within the namespace '{1}' was not understood by the service. The client and the service must be configured to use the same protocol and protocol version ('{2}'). + + + An attempt was made to add more than one transaction to a message. At most one transaction can be added. + + + Internal Error: The instance of the MessageContract cannot be null in {0}. + + + The operation '{0}' could not be loaded because it specifies "rpc-style" in "literal" mode, but uses message contract types or the CoreWCF.Channels.Message. This combination is disallowed -- specify a different value for style or use parameters other than message contract types or CoreWCF.Channels.Message. + + + The operation '{0}' could not be loaded because it has a parameter or return type of type CoreWCF.Channels.Message or a type that has MessageContractAttribute and other parameters of different types. When using CoreWCF.Channels.Message or types with MessageContractAttribute, the method must not use any other types of parameters. + + + When using the rpc-encoded style, message contract types or the CoreWCF.Channels.Message type cannot be used if the operation has no parameters or has a void return value. Add a blank message contract type as a parameter or return type to operation '{0}'. + + + This fault did not provide a matching translation: {0} + + + This fault did not provide a reason (MessageFault.Reason was null). + + + This fault did not provide a reason (MessageFault.Reason.Translations.Count was 0). + + + User operation '{0}.{1}' threw an exception that is unhandled in user code. This exception will be rethrown. If this is a recurring problem, it may indicate an error in the implementation of the '{0}.{1}' method. + + + Parameter '{0}' requires additional schema information that cannot be captured using the parameter mode. The specific attribute is '{1}'. + + + In order to use one of the ServiceHost constructors that takes a service instance, the InstanceContextMode of the service must be set to InstanceContextMode.Single. This can be configured via the ServiceBehaviorAttribute. Otherwise, please consider using the ServiceHost constructors that take a Type argument. + + + Cannot add outgoing headers to message as MessageVersion in OperationContext.Current '{0}' does not match with the header version of message being processed '{1}'. + + + When multiple endpoints on a service share the same ListenUri, those endpoints must all have the same Identity in their EndpointAddress. The endpoints at ListenUri '{0}' do not meet this criteria. + + + Wrapper element name cannot be empty. + + + Wrapper type for message {0} cannot be projected as a data contract type since it has multiple namespaces. Consider using the XmlSerializer + + + WSDL part {0} in message {1} from namespace {2} must have either an element or a type name + + + DataContractSerializer does not support collection specified on element '{0}' + + + Invalid OperationFormatUse specified in the OperationFormatStyle of operation {0}, DataContractSerializer supports only Literal. + + + XmlArrayAttribute cannot be used in repeating part {1}:{0}. + + + Could not find default endpoint element that references contract '{0}' in the ServiceModel client configuration section. This might be because no configuration file was found for your application, or because no endpoint element matching this contract could be found in the client element. + + + Could not find endpoint element with name '{0}' and contract '{1}' in the ServiceModel client configuration section. This might be because no configuration file was found for your application, or because no endpoint element matching this name could be found in the client element. + + + The Address property on ChannelFactory.Endpoint was null. The ChannelFactory's Endpoint must have a valid Address specified. + + + In order to generate configuration information using the GenerateServiceEndpoint method, the ServiceContractGenerator instance must have been initialized with a valid Configuration object. + + + The ServiceHost close operation timed out after {0}. This could be because a client failed to close a sessionful channel within the required time. The time allotted to this operation may have been a portion of a longer timeout. + + + Close process timed out waiting for service dispatch to complete. + + + The WSDL binding named {0} is not valid because no match for operation {1} was found in the corresponding portType definition. + + + The WSDL binding named {0} is not valid because an operation binding doesn't have a name specified. + + + The underlying channel factory could not be created because no binding information was found in the configuration file for endpoint with name '{0}'. Please check the endpoint configuration section with name '{0}' to ensure that binding information is present and correct. + + + The underlying channel factory could not be created because no Binding was passed to the ChannelFactory. Please supply a valid Binding instance via the ChannelFactory constructor. + + + The endpoint configuration section for contract '{0}' with name '{1}' could not be loaded because more than one endpoint configuration with the same name and contract were found. Please check your config and try again. + + + An endpoint configuration section for contract '{0}' could not be loaded because more than one endpoint configuration for that contract was found. Please indicate the preferred endpoint configuration section by name. + + + In operation '{0}', cannot pass null to methods that take Message as input parameter. + + + In operation '{0}', cannot return null from methods that return Message. + + + ServiceHost only supports class service types. + + + The contract name '{0}' could not be found in the list of contracts implemented by the service '{1}'. + + + In order to add an endpoint to the service '{0}', a non-empty contract name must be specified. + + + The contract name 'IMetadataExchange' could not be found in the list of contracts implemented by the service {0}. Add a ServiceMetadataBehavior to the configuration file or to the ServiceHost directly to enable support for this contract. + + + The contract type {0} is not attributed with ServiceContractAttribute. In order to define a valid contract, the specified type (either contract interface or service class) must be attributed with ServiceContractAttribute. + + + An endpoint for type '{0}' could not be added because the ServiceHost instance was not initialized properly. In order to add endpoints by Type, the CreateDescription method must be called. If you are using a class derived from ServiceHost, ensure that the class is properly calling base.CreateDescription. + + + Instance of MessagePartDescription Name='{0}' Namespace='{1}' cannot be used in this context: required 'Type' property was not set. + + + The wsdl operation input {0} in portType {1} does not reference a message. This is either because the message attribute is missing or empty. + + + The wsdl operation output {0} in portType {1} does not reference a message. This is either because the message attribute is missing or empty. + + + The wsdl operation {0} in portType {1} contains a fault that does not reference a message. This is either because the message attribute is missing or empty. + + + Cannot create a typed message from type '{0}'. The functionality only valid for types decorated with MessageContractAttribute. + + + A Channel/Service Endpoint is null. + + + A Channel/Service endpoint's Binding is null. + + + A Channel/Service endpoint's Contract is null. + + + A Channel/Service endpoint's Contract's name is null or empty. + + + A Channel/Service endpoint's Contract's namespace is null. + + + Service '{0}' has zero application (non-infrastructure) endpoints. This might be because no configuration file was found for your application, or because no service element matching the service name could be found in the configuration file, or because no endpoints were defined in the service element. + + + DeliveryRequirementsAttribute requires QueuedDelivery, but binding for the endpoint with contract '{0}' doesn't support it or isn't configured properly to support it. + + + DeliveryRequirementsAttribute disallows QueuedDelivery, but binding for the endpoint with contract '{0}' supports it. + + + The DeliveryRequirementsAttribute on contract '{0}' specifies that the binding must support ordered delivery (RequireOrderedDelivery). This condition could not be verified because the configured binding does not implement IBindingDeliveryCapabilities. The DeliveryRequirementsAttribute may only be used with bindings that implement the IBindingDeliveryCapabilities interface. + + + The DeliveryRequirementsAttribute on contract '{0}' specifies a QueuedDeliveryRequirements constraint. This condition could not be verified because the configured binding does not implement IBindingDeliveryCapabilities. The DeliveryRequirementsAttribute may only be used with bindings that implement the IBindingDeliveryCapabilities interface. + + + The DeliveryRequirementsAttribute on contract '{0}' specifies a QueuedDeliveryRequirements value of NotAllowed. However, the configured binding for this contract specifies that it does support queued delivery. A queued binding may not be used with this contract. + + + At least one operation on the '{0}' contract is configured with the TransactionFlowAttribute attribute set to Mandatory but the channel's binding '{1}' is not configured with a TransactionFlowBindingElement. The TransactionFlowAttribute attribute set to Mandatory cannot be used without a TransactionFlowBindingElement. + + + At least one operation on the '{0}' contract is configured with the TransactionFlowAttribute attribute set to Mandatory but the channel's binding '{1}' is not configured with a TransactionFlowBindingElement. The TransactionFlowAttribute attribute set to Mandatory cannot be used without a TransactionFlowBindingElement. + + + The message with Action '{0}' cannot be processed at the receiver, due to a ContractFilter mismatch at the EndpointDispatcher. This may be because of either a contract mismatch (mismatched Actions between sender and receiver) or a binding/security mismatch between the sender and the receiver. Check that sender and receiver have the same contract and the same binding (including security requirements, e.g. Message, Transport, None). + + + The message with To '{0}' cannot be processed at the receiver, due to an AddressFilter mismatch at the EndpointDispatcher. Check that the sender and receiver's EndpointAddresses agree. + + + The message with Action '{0}' cannot be processed at the receiver because this Action is reserved for the connection opening messages only and cannot be sent from client to server. To invoke this operation on the server, call the '{1}' method on the client proxy instead. + + + The operation '{0}' could not be invoked because the property '{1}' on the OperationContract is set to '{2}'. To invoke this operation on the server, call the '{3}' method on the client proxy instead. + + + When using the IAsyncResult design pattern, the End method cannot be decorated with OperationContractAttribute. Only the corresponding Begin method can be decorated with OperationContractAttribute; that attribute will apply to the Begin-End pair of methods. Method '{0}' in type '{1}' violates this. + + + The WS-AT messaging library failed to initialize. + + + A client-side channel to the WS-AT protocol service could not be created. + + + The DispatchOperation '{0}' requires Formatter, since DeserializeRequest and SerializeReply are not both false. + + + The ClientOperation '{0}' requires Formatter, since SerializeRequest and DeserializeReply are not both false. + + + DispatchOperation requires Invoker. + + + Channel requirements cannot be met by the ChannelFactory for Binding '{0}' since the contract requires support for one of these channel types '{1}' but the binding doesn't support any of them. + + + Channel type '{1}' was requested, but Binding '{0}' doesn't support it or isn't configured properly to support it. + + + ChannelDispatcher requirements cannot be met by the IChannelListener for Binding '{0}' since the contract requires support for one of these channel types '{1}' but the binding only supports these channel types '{2}'. + + + The listener at Uri '{0}' could not be initialized because it was created for an unrecognized channel type. + + + Contract requires Session, but Binding '{0}' doesn't support it or isn't configured properly to support it. + + + Contract does not allow Session, but Binding '{0}' does not support Datagram or is not configured properly to support it. + + + Contract requires OneWay, but Binding '{0}' doesn't support it or isn't configured properly to support it. + + + Contract requires TwoWay (either request-reply or duplex), but Binding '{0}' doesn't support it or isn't configured properly to support it. + + + Contract requires Request/Reply, but Binding '{0}' doesn't support it or isn't configured properly to support it. + + + Contract requires Duplex, but Binding '{0}' doesn't support it or isn't configured properly to support it. + + + Binding '{0}' doesn't support creating any channel types. This often indicates that the BindingElements in a CustomBinding have been stacked incorrectly or in the wrong order. A Transport is required at the bottom of the stack. The recommended order for BindingElements is: TransactionFlow, ReliableSession, Security, CompositeDuplex, OneWay, StreamSecurity, MessageEncoding, Transport. + + + The contract '{0}' is not self-consistent -- it has one or more IsTerminating or non-IsInitiating operations, but it does not have the SessionMode property set to SessionMode.Required. The IsInitiating and IsTerminating attributes can only be used in the context of a session. + + + The operation contract '{0}' is not self-consistent. When the '{1}' is set to '{2}', both '{3}' and '{4}' properties must be true, and the operation must not have any input parameters. + + + The ServiceHost must be configured with either a serviceType or a serviceInstance. Both of these values are currently null. + + + The ServiceMetadataExtension instance could not be added to the ServiceHost instance because it has already been added to another ServiceHost instance. + + + The ServiceMetadataExtension instance could not be removed from the ServiceHost instance because it has not been added to any ServiceHost instance. + + + The ServiceMetadataExtension instance could not be removed from the ServiceHost instance because it has already been added to a different ServiceHost instance. + + + A value of type '{0}' cannot be added to the generic collection, because the collection has been parameterized with a different type. + + + A null value cannot be added to the generic collection, because the collection has been parameterized with a value type. + + + Cannot add two items with the same key to SynchronizedKeyedCollection. + + + Item does not exist in SynchronizedKeyedCollection. + + + A reply message was received without a valid RelatesTo header. This may have been caused by a missing RelatesTo header or a RelatesTo header with an invalid WS-Addressing Relationship type. + + + Internal Error: The InnerChannel property is null. + + + The current channel does not support closing the output session as this channel does not implement ISessionChannel<IDuplexSession>. + + + The ServiceEndpoint with name '{0}' could not be exported to WSDL because the Binding property is null. To fix this, set the Binding property to a valid Binding instance. + + + A binding instance has already been associated to listen URI '{0}'. If two endpoints want to share the same ListenUri, they must also share the same binding object instance. The two conflicting endpoints were either specified in AddServiceEndpoint() calls, in a config file, or a combination of AddServiceEndpoint() and config. + + + The following Policy Assertions were not Imported:\r\n + + + XPath:{0}\r\n Assertions: + + + "XPath Unavailable" + + + A policy expression was ignored because another policy expression with that ID has already been read in this document.\r\nXPath:{0} + + + A policy document was ignored because a policy expression with that ID has already been imported.\r\nPolicy ID:{0} + + + A metadata section containing policy did not have an identifier so it cannot be referenced. + + + XPath:{0} + + + A policy reference was ignored because the policy with ID '{0}' could not be found. + + + A policy reference was ignored because the URI of the reference was empty. + + + A policy reference was ignored because the required {0} attribute was missing. + + + The policy expression was not fully imported because it exceeded the maximum allowable complexity. The import stopped at element '{0}' '{1}'. + + + The policy expression was not fully imported because its normalized form was too large. + + + Unrecognized policy element {0} in namespace {1}. + + + "{0}" is not a supported WS-Policy document root element. + + + The "{0}" namespace is not a recognized WS-Policy namespace. + + + Cannot find usable policy alternatives. + + + Unreachable policy detected.\r\nA WS-Policy element embedded in WSDL is missing a fragment identifier. This policy cannot be referenced by any WS-PolicyAttachment mechanisms.\r\nXPath:{0} + + + The processing of the WSDL parameter failed. Error: {0} + + + The optional WSDL extension element '{0}' from namespace '{1}' was not handled.\r\nXPath: {2} + + + The required WSDL extension element '{0}' from namespace '{1}' was not handled. + + + An unrecognized WSDL extension of Type '{0}' was not handled. + + + A previous call to this WsdlExporter left it in a faulted state. It is no longer usable. + + + A previous call to this WsdlImporter left it in a faulted state. It is no longer usable. + + + The ContractDescription argument to ImportEndpoints must be contained in the KnownContracts collection. + + + A previous attempt to import this {0} already failed. + + + The type {0} registered as a policy extension does not implement IPolicyImportExtension + + + The type {0} registered as a policy extension does not have a public default constructor. Policy extensions must have a public default constructor + + + An exception was thrown in a call to a policy import extension.\r\nExtension: {0}\r\nError: {1} + + + An exception was thrown in a call to a policy export extension.\r\nExtension: {0}\r\nError: {1} + + + Calling IWsdlExportExtension.ExportContract twice with the same ContractDescription is not supported. + + + Duplicate contract XmlQualifiedNames are not supported.\r\nAnother ContractDescription with the Name: {0} and Namespace: {1} has already been exported. + + + Similar ServiceEndpoints were exported. The WSDL export process was forced to suffix wsdl:binding names to avoid naming conflicts.\r\n Similar ServiceEndpoints means different binding instances having the Name: {0} and Namespace: {1} and either the same ContractDescription or at least the same contract Name: {2}. + + + An operation was skipped during export because it has a wildcard action. This is not supported in WSDL.\r\nContract Name:{0}\r\nContract Namespace:{1}\r\nOperation Name:{2} + + + An operation was skipped during export because the property '{0}' is set to '{1}'. This operation should be used for server only and should not be exposed from WSDL. \r\nContract Name:{2}\r\nContract Namespace:{3}\r\nOperation Name:{4} + + + The type {0} registered as a WSDL extension does not implement IWsdlImportExtension. + + + The type {0} registered as a WSDL extension does not have a public default constructor. WSDL extensions must have a public default constructor. + + + An exception was thrown in a call to a WSDL export extension: {0}\r\n contract: {1} + + + An exception was thrown in a call to a WSDL export extension: {0}\r\n Endpoint: {1} + + + A WSDL import extension threw an exception during the BeforeImport call: {0}\r\nError: {1} + + + An exception was thrown while running a WSDL import extension: {0}\r\nError: {1} + + + Cannot import {0}\r\nDetail: {2}\r\nXPath to Error Source: {1} + + + There was an error importing a {0} that the {1} is dependent on.\r\nXPath to {0}: {2} + + + The {0} binding element requires envelope version '{1}' It doesn't support '{2}'. + + + No value. + + + The '{0}' binding element does not support cloning. + + + WsdlImporter encountered unrecognized policy assertions in ServiceDescription '{0}': + + + The {0} declared on method '{1}' in type '{2}' is invalid. {0}s are only valid on methods that are declared in a type that has ServiceContractAttribute. Either add ServiceContractAttribute to type '{2}' or remove {0} from method '{1}'. + + + Too many attributes of type {0} on {1}. + + + Couldn't find required attribute of type {0} on {1}. + + + Attempted to get contract type for {0}, but that type is not a ServiceContract, nor does it inherit a ServiceContract. + + + OperationContract method '{0}' in type '{1}' does not properly implement the async pattern, as no corresponding method '{2}' could be found. Either provide a method called '{2}' or set the AsyncPattern property on method '{0}' to false. + + + OperationContract method '{0}' in type '{1}' does not properly implement the async pattern, as more than one corresponding method '{2}' was found. When using the async pattern, exactly one end method must be provided. Either remove or rename one or more of the '{2}' methods such that there is just one, or set the AsyncPattern property on method '{0}' to false. + + + Invalid async End method signature for method {0} in ServiceContract type {1}. Your end method must take an IAsyncResult as the last argument. + + + Invalid async Begin method signature for method {0} in ServiceContract type {1}. Your begin method must take an AsyncCallback and an object as the last two arguments and return an IAsyncResult. + + + Because base ServiceContract '{0}' has a CallbackContract '{1}', derived ServiceContract '{2}' must also specify either '{1}' or a derived type as its CallbackContract. + + + In a contract inheritance hierarchy, the ServiceContract's CallbackContract must be a subtype of the CallbackContracts of all of the CallbackContracts of the ServiceContracts inherited by the original ServiceContract, Types {0} and {1} violate this rule. + + + Cannot have two operations in the same contract with the same name, methods {0} and {1} in type {2} violate this rule. You can change the name of one of the operations by changing the method name or by using the Name property of OperationContractAttribute. + + + The {0}.{1} operation references a message element [{2}] that has already been exported from the {3}.{4} operation. You can change the name of one of the operations by changing the method name or using the Name property of OperationContractAttribute. Alternatively, you can control the element name in greater detail using the MessageContract programming model. + + + Cannot inherit two different operations with the same name, operation '{0}' from contracts '{1}' and '{2}' violate this rule. You can change the name of one of the operations by changing the method name or by using the Name property of OperationContractAttribute. + + + The synchronous OperationContract method '{0}' in type '{1}' was matched with the asynchronous OperationContract methods '{2}' and '{3}' because they have the same operation name '{4}'. When a synchronous OperationContract method is matched to a pair of asynchronous OperationContract methods, the two OperationContracts must define the same number and types of parameters. In this case, some of the arguments are different. To fix it, ensure that the OperationContracts define the same number and types of arguments, in the same order. Alternatively, changing the name of one of the methods will prevent matching. + + + The synchronous OperationContract method '{0}' in type '{1}' was matched with the task-based asynchronous OperationContract method '{2}' because they have the same operation name '{3}'. When a synchronous OperationContract method is matched to a task-based asynchronous OperationContract method, the two OperationContracts must define the same number and types of parameters. In this case, some of the arguments are different. To fix it, ensure that the OperationContracts define the same number and types of arguments, in the same order. Alternatively, changing the name of one of the methods will prevent matching. + + + The task-based asynchronous OperationContract method '{0}' in type '{1}' was matched with the asynchronous OperationContract methods '{2}' and '{3}' because they have the same operation name '{4}'. When a task-based asynchronous OperationContract method is matched to a pair of asynchronous OperationContract methods, the two OperationContracts must define the same number and types of parameters. In this case, some of the arguments are different. To fix it, ensure that the OperationContracts define the same number and types of arguments, in the same order. Alternatively, changing the name of one of the methods will prevent matching. + + + The synchronous OperationContract method '{0}' in type '{1}' was matched with the asynchronous OperationContract methods '{2}' and '{3}' because they have the same operation name '{4}'. When a synchronous OperationContract method is matched to a pair of asynchronous OperationContract methods, the two OperationContracts must define the same return type. In this case, the return types are different. To fix it, ensure that method '{0}' and method '{3}' have the same return type. Alternatively, changing the name of one of the methods will prevent matching. + + + The synchronous OperationContract method '{0}' in type '{1}' was matched with the task-based asynchronous OperationContract method '{2}' because they have the same operation name '{3}'. When a synchronous OperationContract method is matched to a task-based asynchronous OperationContract method, the two OperationContracts must define the same return type. In this case, the return types are different. To fix it, ensure that method '{0}' and method '{2}' have the same return type. Alternatively, changing the name of one of the methods will prevent matching. + + + The task-based asynchronous OperationContract method '{0}' in type '{1}' was matched with the asynchronous OperationContract methods '{2}' and '{3}' because they have the same operation name '{4}'. When a synchronous OperationContract method is matched to a pair of asynchronous OperationContract methods, the two OperationContracts must define the same return type. In this case, the return types are different. To fix it, ensure that method '{0}' and method '{3}' have the same return type. Alternatively, changing the name of one of the methods will prevent matching. + + + The synchronous OperationContract method '{0}' in type '{1}' was matched with the asynchronous OperationContract methods '{2}' and '{3}' because they have the same operation name '{4}'. When a synchronous OperationContract method is matched to a pair of asynchronous OperationContract methods, any additional attributes must be declared on the synchronous OperationContract method. In this case, the asynchronous OperationContract method '{2}' has one or more attributes of type '{5}'. To fix it, remove the '{5}' attribute or attributes from method '{2}'. Alternatively, changing the name of one of the methods will prevent matching. + + + The synchronous OperationContract method '{0}' in type '{1}' was matched with the task-based asynchronous OperationContract method '{2}' because they have the same operation name '{3}'. When a synchronous OperationContract method is matched to a task-based asynchronous OperationContract method, any additional attributes must be declared on the synchronous OperationContract method. In this case, the task-based asynchronous OperationContract method '{2}' has one or more attributes of type '{4}'. To fix it, remove the '{4}' attribute or attributes from method '{2}'. Alternatively, changing the name of one of the methods will prevent matching. + + + The task-based asynchronous OperationContract method '{0}' in type '{1}' was matched with the asynchronous OperationContract methods '{2}' and '{3}' because they have the same operation name '{4}'. When a task-based asynchronous OperationContract method is matched to a pair of asynchronous OperationContract methods, any additional attributes must be declared on the task-based asynchronous OperationContract method. In this case, the asynchronous OperationContract method '{2}' has one or more attributes of type '{5}'. To fix it, remove the '{5}' attribute or attributes from method '{2}'. Alternatively, changing the name of one of the methods will prevent matching. + + + The synchronous OperationContract method '{0}' in type '{1}' was matched with the asynchronous OperationContract methods '{2}' and '{3}' because they have the same operation name '{4}'. When a synchronous OperationContract method is matched to a pair of asynchronous OperationContract methods, the two OperationContracts must have the same value for the '{5}' property. In this case, the values are different. To fix it, change the '{5} property of one of the OperationContracts to match the other. Alternatively, changing the name of one of the methods will prevent matching. + + + The synchronous OperationContract method '{0}' in type '{1}' was matched with the task-based asynchronous OperationContract method '{2}' because they have the same operation name '{3}'. When a synchronous OperationContract method is matched to a task-based asynchronous OperationContract method, the two OperationContracts must have the same value for the '{4}' property. In this case, the values are different. To fix it, change the '{4} property of one of the OperationContracts to match the other. Alternatively, changing the name of one of the methods will prevent matching. + + + The task-based asynchronous OperationContract method '{0}' in type '{1}' was matched with the asynchronous OperationContract methods '{2}' and '{3}' because they have the same operation name '{4}'. When a task-based asynchronous OperationContract method is matched to a pair of asynchronous OperationContract methods, the two OperationContracts must have the same value for the '{5}' property. In this case, the values are different. To fix it, change the '{5} property of one of the OperationContracts to match the other. Alternatively, changing the name of one of the methods will prevent matching. + + + Operations marked with IsOneWay=true must not declare output parameters, by-reference parameters or return values. + + + One way operation {0} cannot not specify a reply action. + + + The method '{1}' in type '{0}' is marked IsOneWay=true and declares one or more FaultContractAttributes. One-way methods cannot declare FaultContractAttributes. To fix it, change IsOneWay to false or remove the FaultContractAttributes. + + + Only malformed Messages are supported. + + + Cannot locate operation {0} in Contract {1}. + + + Unsupported WSDL, only one message part is supported for fault messages. This fault message references zero or more than one message part. If you have edit access to the WSDL file, you can fix the problem by removing the extra message parts such that fault message references just one part. + + + Unsupported WSDL, the fault message part must reference an element. This fault message does not reference an element. If you have edit access to the WSDL document, you can fix the problem by referencing a schema element using the 'element' attribute. + + + Async End called on wrong channel. + + + Async End called with an IAsyncResult from a different Begin method. + + + The received transaction has an isolation level of '{0}' but the service is configured with a TransactionIsolationLevel of '{1}'. The isolation level for received transactions and the service must be the same. + + + The value of the addressHeaders argument is invalid because the collection contains null values. Null is not a valid value for the AddressHeaderCollection. + + + The array passed does not have enough space to hold all the properties contained by this collection. + + + The value could not be added to the collection, as the collection already contains an item of the same type: '{0}'. This collection only supports one instance of each type. + + + Cannot create channel for a contract that requires request/reply and a binding that requires manual addressing but only supports duplex communication. + + + Missing required '{0}' attribute. + + + Ignoring invalid SOAP header extension in wsdl:operation name='{0}' from targetNamespace='{1}'. Reason: {2} + + + Ignoring invalid SOAP fault extension in wsdl:operation name='{0}' from targetNamespace='{1}'. Reason: {2} + + + Ignoring invalid part in wsdl:message name='{0}' from targetNamespace='{1}'. Reason: {2} + + + PrivacyNotice element must have a Version attribute. + + + PrivacyNotice element Version attribute must have an integer value. + + + Binding validation failed. The client cannot send messages. A conflict in the binding properties caused this failure. The UseActiveDirectory is set to true and QueueTransferProtocol is set to Native. To resolve the conflict, correct one of the properties. + + + Binding validation failed because the binding's ReceiveErrorHandlig property is set to Move or Reject while the version of MSMQ installed on this system is not 4.0 or higher. The channel listener cannot be opened. Resolve the conflict by setting the ReceiveErrorHandling property to Drop or Fault, or by upgrading to MSMQ v4.0. + + + The Ambient transaction used to Complete the ReceiveContext Operation is not in an active state. + + + Binding validation failed because the binding's MsmqAuthenticationMode property is set to Certificate while the MsmqProtectionLevel property is not set to Sign or EncryptAndSign. The channel factory or service host cannot be opened. Resolve the conflict by correcting one of the properties. + + + Binding validation failed. The service or the client cannot be started. A conflict in the binding properties caused this failure. The MsmqAuthenticationMode is set to None and MsmqProtectionLevel is not set to None. To resolve to conflict, correct one of the properties. + + + Binding validation failed because the binding's MsmqAuthenticationMode property is set to WindowsDomain while the MsmqProtectionLevel property is not set to Sign or EncryptAndSign. The channel factory or service host cannot be opened. Resolve the conflict by correcting one of the properties. + + + Creation of a message security context failed because the attached sender certificate was invalid or cannot be validated. The message cannot be received. Ensure that a valid certificate is attached to the message and that the certificate is present in the receiver's certificate store. + + + The content type of an incoming message is unknown or not supported. The message cannot be received. Ensure that the sender was configured to use the same message encoder as the receiver. + + + An incoming MSMQ message contained invalid or unexpected .NET Message Framing information in its body. The message cannot be received. Ensure that the sender is using a compatible service contract with a matching SessionMode. + + + An XML error was encountered while reading a WCF message. The message cannot be received. Ensure the message was sent by a WCF client which used an identical message encoder. + + + TransactedBatchingBehavior validation failed because none of the service operations had the TransactionScopeRequired property set to true on their OperationBehavior attribute. The service host cannot be started. Ensure this requirement is met if you wish to use this behavior. + + + A mismatch was detected between the serialization format specified in the MsmqIntegrationMessageProperty and the body of the MSMQ message. The message cannot be sent. The serialization format ByteArray requires the body of the MSMQ message to be of type byte[]. + + + An error occurred while deserializing an MSMQ message's ActiveX body. The message cannot be received. The specified variant type for the body does not match the actual MSMQ message body. + + + An error occurred while deserializing an MSMQ message's XML body. The message cannot be received. Ensure that the service contract is decorated with appropriate [ServiceKnownType] attributes or the TargetSerializationTypes property is set on the MsmqIntegrationBindingElement. + + + The properties of the message are mismatched. The message cannot be sent. The BodyType message property cannot be specified if the ActiveX serialization format is used. + + + The sender's X.509 certificate was not found. The message cannot be sent. Ensure the certificate is available in the sender's certificate store. + + + Binding validation failed. The client cannot send the message. The DeadLetterQueue is set to Custom, but the CustomDeadLetterQueue is not specified. Specify the URI of the dead letter queue for each application in the CustomDeadLetterQueue property. + + + An error was encountered while deserializing the message. The message cannot be received. + + + Binding validation failed because the endpoint listen URI does not represent an MSMQ direct format name. The service host cannot be opened. Make sure you use a direct format name for the endpoint's listen URI. + + + The host in the CustomDeadLetterQueue URI is not "localhost" or the local machine name. A custom DLQ must reside on the sender's machine. + + + Binding validation failed. The client cannot send a message. The specified dead letter queue does not exist or cannot be written. Ensure the queue exists with the proper authorization to write to it. + + + Binding validation failed because the binding's MsmqProtectionLevel property is set to EncryptAndSign while the UseActiveDirectory is not set to true. The channel factory or the service host cannot be opened. Resolve the conflict by correcting one of the properties. + + + Binding validation failed. The service or the client cannot be started. The ExactlyOnce property is set to false and ReceiveContext is enabled. This is not supported. To resolve the conflict, either set ExactlyOnce to true or disable ReceiveContext. + + + The version check failed with the error: '{0}'. The version of MSMQ cannot be detected All operations that are on the queued channel will fail. Ensure that MSMQ is installed and is available. + + + The message ID '{0}' is not in the right format. + + + The specified addressing scheme is invalid for this binding. The NetMsmqBinding scheme must be net.msmq. The MsmqIntegrationBinding scheme must be msmq.formatname. + + + The MsmqIntegrationBinding validation failed. The service cannot be started. The {0} binding does not support the method signature for the service operation {1} in the {2} contract. Correct the service operation to use the MsmqIntegrationBinding. + + + The ActiveX serialization failed because the serialization format cannot be recognized. The message cannot be received. + + + The variant type is not recognized. The ActiveX serialization failed. The message cannot be sent. The specified variant type is not supported. + + + {0} ({1}, 0x{2}) + + + The message cannot be sent because it's missing an MsmqIntegrationMessageProperty. All messages sent over MSMQ integration channels must carry the MsmqIntegrationMessageProperty. + + + Binding validation failed. The service or the client cannot be started. The ExactlyOnce property is set to true and the Durable property is set to false. This is not supported. To resolve the conflict, correct one of these properties. + + + Argument must be a positive number or zero. + + + A mismatch between the binding and MSMQ queue configuration was detected. The service cannot be started. The ExactlyOnce property is set to false and the queue to read messages from is a transactional queue, Correct the error by setting the ExactlyOnce property to true or create a non-transactional binding. + + + Binding validation failed because the URI represents a subqueue and the ReceiveErrorHandling parameter is set to Move. The service host or channel listener cannot be opened. Resolve this conflict by setting the ReceiveErrorHandling to Fault, Drop or Reject. + + + Creation of a message security context failed because the sender's SID was not found in the message. The message cannot be received. The WindowsDomain MsmqAuthenticationMode requires the sender's SID. + + + An error occurred while opening the queue:{0}. The message cannot be sent or received from the queue. Ensure that MSMQ is installed and running. Also ensure that the queue is available to open with the required access mode and authorization. + + + An error occurred when converting the '{0}' queue path name to the format name: {1}. All operations on the queued channel failed. Ensure that the queue address is valid. MSMQ must be installed with Active Directory integration enabled and access to it is available. + + + Binding validation failed. The client cannot send messages. The CustomDeadLetterQueue property is set, but the DeadLetterQueue property is not set to Custom. Set the DeadLetterQueue property to Custom. + + + Binding validation failed. The client cannot send messages. A conflict in the binding properties is causing the failure. To use the custom dead letter queue, ExactlyOnce must be set to true to resolve to conflict. + + + A mismatch between the binding and MSMQ configuration was detected. The client cannot send messages. To use the custom dead letter queue, you must have MSMQ version 4.0 or higher. If you do not have MSMQ version 4.0 or higher set the DeadLetterQueue property to System or None. + + + The transport channel detected a poison message. This occurred because the message exceeded the maximum number of delivery attempts or because the channel detected a fundamental problem with the message. The inner exception may contain additional information. + + + There was an error opening the queue. Ensure that MSMQ is installed and running, the queue exists and has proper authorization to be read from. The inner exception may contain additional information. + + + The ReceiveContext delete operation failed because the message with Id '{0}' could not be received from the lock subqueue. + + + The ReceiveContext unlock operation failed because the message with Id '{0}' could not be moved from the lock subqueue to the main queue. + + + The queue could not be opened because the ReceiveContext feature is not supported on subqueues. Specify a different queue to receive from, or disable ReceiveContext. + + + An error occurred while receiving a message from the queue: {0}. Ensure that MSMQ is installed and running. Make sure the queue is available to receive from. + + + A transaction error occurred for this session. The session channel is faulted. Messages in the session cannot be sent or received. A queued session cannot be associated with more than one transaction. Ensure that all messages in the session are sent or received using a single transaction. + + + An error occurred while sending to the queue: {0}.Ensure that MSMQ is installed and running. If you are sending to a local queue, ensure the queue exists with the required access mode and authorization. + + + A serialization error occurred. The message cannot be sent or received. The MSMQ integration channel is able to serialize no more than {0} types. + + + The transaction associated with this session channel has been rolled back because Abort was called on the session channel before the transaction committed. + + + Session channels must not have pending messages when the transactions associated with these channels are committed. Pending messages are either messages that have not been received from the session channel or messages that have been received but Complete has not been called for them. The channel has faulted and the transaction was rolled back. + + + Session channels must be closed before the transaction is committed. The channel has faulted and the transaction was rolled back. + + + The total size of messages sent in this session exceeded the maximum value of Int32. The messages in this session cannot be sent. + + + An attempt made to close the session channel while there are still messages pending in the session. Current transaction will be rolled back and the session channel will be faulted. Messages in a session must be consumed all at once. + + + An attempt was made to close the session channel while there are still messages pending in the session. The sessiongram will be rolled back to the queue and the session channel will be faulted. + + + A serialization error occurred because of a mismatch between the value of the SerializationFormat property and the type of the body. The message cannot be sent. Ensure the type of the body is Stream or use a different SerializationFormat. + + + The message time to live (TTL) is too large. The message cannot be sent. The message TTL cannot exceed the Int32 maximum value. + + + A client X.509 certificate was not specified through the channel factory's Credentials property, but one is required when the binding's MsmqAuthenticationMode property is set to Certificate. The message cannot be sent. + + + The current transaction is not active. Messages in this session cannot be sent or received and the session channel will be faulted. All messages in a session must be sent or received using a single transaction. + + + Binding validation failed because the binding's ExactlyOnce property is set to true while the destination queue is non-transactional. The service host cannot be opened. Resolve this conflict by setting the ExactlyOnce property to false or creating a transactional queue for this binding. + + + A transaction was not found in Transaction.Current but one is required for this operation. The channel cannot be opened. Ensure this operation is being called within a transaction scope. + + + A transaction is required but is not available. Messages cannot be sent or received. Ensure that the transaction scope is specified to send or receive messages. + + + A mismatch occurred between the binding and the MSMQ configuration. Messages cannot be sent. The custom dead letter queue specified in the binding must be a transactional queue. Ensure that the custom dead letter queue address is correct and the queue is a transactional queue. + + + The net.msmq scheme does not support port numbers. To correct this, remove the port number from the URI. + + + Unrecognized error {0} (0x{1}) + + + The serialization failed because the serialization format '{0}' is not supported. The message cannot be sent or received. + + + Binding validation failed because the binding's MsmqAuthenticationMode property is set to WindowsDomain but MSMQ is installed with Active Directory integration disabled. The channel factory or service host cannot be opened. + + + The URL in invalid. The URL for the queue cannot contain the '$' character. Use the syntax in net.msmq://machine/private/queueName to address a private queue. + + + The URI is invalid because it is missing a host. + + + Failed to reacquire lock for message. + + + Cannot find '{0}' value in dictionary string. + + + WMI GetObject Query: {0} + + + WMI PutInstance Class: {0} + + + Cannot dequeue a '{0}' object while in the Created state. + + + The binding (Name={0}, Namespace={1}) cannot be used to create a ChannelFactory or a ChannelListener because it appears to be missing a TransportBindingElement. Every binding must have at least one binding element that derives from TransportBindingElement. + + + The TransportBindingElement of type '{0}' in this CustomBinding returned a null or empty string for the Scheme. TransportBindingElement's Scheme must be a non-empty string. + + + Binding '{0}' lacks a TransportBindingElement. Every binding must have a binding element that derives from TransportBindingElement. This binding element must appear last in the BindingElementCollection. + + + In Binding '{0}', TransportBindingElement '{1}' does not appear last in the BindingElementCollection. Please change the order of elements such that the TransportBindingElement is last. + + + None of the binding elements in binding '{0}' define a message version. At least one binding element must define a message version and return it from the GetProperty<MessageVersion> method. + + + Some of the binding elements in this binding were not used when building the ChannelFactory / ChannelListener. This may be have been caused by the binding elements being misordered. The recommended order for binding elements is: TransactionFlow, ReliableSession, Security, CompositeDuplex, OneWay, StreamSecurity, MessageEncoding, Transport. Note that the TransportBindingElement must be last. The following binding elements were not built: {0}. + + + More than one MessageEncodingBindingElement was found in the BindingParameters of the BindingContext. This usually is caused by having multiple MessageEncodingBindingElements in a CustomBinding. Remove all but one of these elements. + + + More than one IStreamUpgradeProviderElement was found in the BindingParameters of the BindingContext. This usually is caused by having multiple IStreamUpgradeProviderElements in a CustomBinding. Remove all but one of these elements. + + + More than one PeerResolverBindingElement was found in the BindingParameters of the BindingContext. This usually is caused by having multiple PeerResolverBindingElements in a CustomBinding. Remove all but one of these elements. + + + More than one PeerCustomResolverBindingElement was found in the BindingParameters of the BindingContext. This usually is caused by having multiple PeerCustomResolverBindingElement in a CustomBinding. Remove all but one of these elements. + + + The security capabilities of binding '{0}' do not match those of the generated runtime object. Most likely this means the binding contains a StreamSecurityBindingElement, but lacks a TransportBindingElement that supports Stream Security (such as TCP or Named Pipes). Either remove the unused StreamSecurityBindingElement or use a transport that supports this element. + + + Only an absolute Uri can be used as a base address. + + + This collection already contains an address with scheme {0}. There can be at most one address per scheme in this collection. If your service is being hosted in IIS you can fix the problem by setting 'system.serviceModel/serviceHostingEnvironment/multipleSiteBindingsEnabled' to true or specifying 'system.serviceModel/serviceHostingEnvironment/baseAddressPrefixFilters'. + + + A base address cannot contain a Uri user info section. + + + The binding does not contain a TransportBindingElement. + + + The binding does not contain a ChannelDemuxerBindingElement. + + + A base address cannot contain a Uri query string. + + + A base address cannot contain a Uri fragment. + + + The given URI must be absolute. + + + The binding for scheme '{0}' specified in the protocol mapping does not exist and must be created. + + + The binding on the service endpoint cannot be configured. + + + Configuration binding extension '{0}' could not be found. Verify that this binding extension is properly registered in system.serviceModel/extensions/bindingExtensions and that it is spelled correctly. + + + A binding reference cycle was detected in your configuration. The following reference cycle must be removed: {0}. + + + The binding specified cannot be null or an empty string. Please specify a valid binding. Valid binding values can be found in the system.serviceModel/extensions/bindingExtensions collection. + + + Cannot parse type '{0}' into a CoreWCF.Dispatcher.XPathMessageFilter. + + + Configuration endpoint extension '{0}' could not be found. Verify that this endpoint extension is properly registered in system.serviceModel/extensions/endpointExtensions and that it is spelled correctly. + + + An endpoint reference cycle was detected in your configuration. The following reference cycle must be removed: {0}. + + + The endpoint specified cannot be null or an empty string. Please specify a valid endpoint. Valid endpoint values can be found in the system.serviceModel/extensions/endpointExtensions collection. + + + Filter element body must not be empty. + + + An extension named {0} already appears in the {1}. Extension names must be unique. + + + An extension of name '{0}' already appears in extension collection. Extension names must be unique. + + + An extension of type '{0}' already appears in extension collection. Extension types must be unique. + + + A child element with the element name '{0}' already exists. Child elements can only be added once. + + + A child element named '{0}' with same key already exists at the same configuration scope. Collection elements must be unique within the same configuration scope (e.g. the same application.config file). Duplicate key value: '{1}'. + + + The '{0}' configuration element key cannot be null. + + + At least one of the configuration element keys '{0}' must not be null. + + + Extension element '{0}' cannot be added to this element. Verify that the extension is registered in the extension collection at system.serviceModel/extensions/{1}. + + + Extension collection '{0}' not found. + + + The extension of type '{0}' is not registered in the extension collection '{1}'. + + + Invalid value for serviceAuthenticationManagerType. The serviceAuthenticationManagerType '{0}' does not derive from '{1}'. + + + Invalid value in policyType. The policyType '{0}' does not implement from '{1}'. + + + The {1} binding does not have a configured binding named '{0}'. + + + The binding at {1} does not have a configured binding named '{0}'. This is an invalid value for {2}. + + + Cannot add the behavior extension '{0}' to the common endpoint behavior because it does not implement '{1}'. + + + Cannot add the behavior extension '{0}' to the common service behavior because it does not implement '{1}'. + + + Invalid value for the certificate validator type. The type '{0}' does not derive from the appropriate base class '{1}'. + + + Invalid value for the client credentials type. The type '{0}' does not derive from the appropriate base class '{1}'. + + + The value '{0}' is not a valid instance of type '{1}'. + + + The instance is not a valid configurable value of type '{0}'. + + + {0} is not a valid encoding string for System.Text.Encoding.GetEncoding(string). + + + There is no endpoint behavior named '{0}'. + + + Cannot add the '{0}' behavior extension to '{1}' endpoint behavior because the underlying behavior type does not implement the IEndpointBehavior interface. + + + The endpoint at {1} does not have a configured endpoint named '{0}'. This is an invalid value for {2}. + + + The attribute '{0}' cannot be specified on element '{1}' when attribute '{2}' is not specified. + + + The CreateServiceEndpoint method in type '{0}' returned null instead of an instance of type '{1}'. + + + Invalid element in configuration. The extension '{0}' does not derive from correct extension base type '{1}'. + + + Invalid element in configuration. The extension name '{0}' is not registered in the collection at system.serviceModel/extensions/{1}. + + + The '{0}' type must derive from {1} to be used in the {2} collection. + + + The element {0} requires a key of type '{1}'. Type of the key passed in: '{2}'. + + + '{0}' is not a valid reliable messaging version. Valid values are 'WSReliableMessagingFebruary2005' and 'WSReliableMessaging11'. + + + Invalid value for the saml serializer type. The type '{0}' does not derive from the appropriate base class: '{1}'. + + + Invalid binding path. There is no binding registered with the configuration path '{0}'. + + + Invalid value for the service credentials type. The type '{0}' does not derive from the appropriate base class '{1}'. + + + Invalid value for the security state encoder type. The type '{0}' does not derive from the appropriate base class '{1}'. + + + Invalid value for the username password validator type. The type '{0}' does not derive from the appropriate base class '{1}'. + + + Invalid value for serviceAuthorizationManagerType. The serviceAuthorizationManagerType '{0}' does not derive from '{1}'. + + + There is no service behavior named '{0}'. + + + Cannot add the behavior extension '{0}' to the service behavior named '{1}' because the underlying behavior type does not implement the IServiceBehavior interface. + + + Start must be between 0 and {0}. Value passed in is {1}. + + + '{0}' is not a valid transaction protocol. Valid values are 'OleTransactions', 'WSAtomicTransactionOctober2004', and 'WSAtomicTransaction11'. + + + The type '{0}' registered for extension '{1}' could not be loaded. + + + Invalid binding type for binding extension configuration object. This binding extension manages configuration of binding type '{0}' and cannot act upon type '{1}'. + + + Invalid binding element type for binding element extension configuration object. This binding element extension manages configuration of binding element type '{0}' and cannot act upon type '{1}'. + + + Invalid endpoint type for endpoint extension configuration object. This endpoint extension manages configuration of endpoint type '{0}' and cannot act upon type '{1}'. + + + No elements matching the key '{0}' were found in the configuration element collection. + + + The key does not match the indexer key. When setting the value of a specific index, the key of the desired value must match the index at which it is being set. Key on element (expected value): {0}. Key provided to indexer: {1}. + + + Cannot add the message encoding element '{0}'. Another message encoding element already exists in the binding '{1}'. There can only be one message encoding element for each binding. + + + Cannot find the extension collection associated with extension of type '{0}'. + + + Federated issuer address cannot be null when specifying an issuer binding. + + + The configuration is read only. + + + The '{0}' configuration section cannot be created. The machine.config file is missing information. Verify that this configuration section is properly registered and that you have correctly spelled the section name. For Windows Communication Foundation sections, run ServiceModelReg.exe -i to fix this error. + + + Cannot add stream upgrade element '{0}'. Another stream upgrade element already exists in the binding '{1}'. There can only be one stream update element per binding. + + + Cannot add the transport element '{0}'. Another transport element already exists in the binding '{1}'. There can only be one transport element for each binding. + + + The XmlElement must contain XML content. + + + The XPathFilter for an XPathFilterElement cannot be null. + + + Namespace prefix '{0}' referenced in XPath expression was not found. + + + (Default) + + + MTAWorkerThread exception + + + An unexpected error has occurred. + + + The CLSID specified in the service file is not configured in the specified application. (The CLSID is {0}, the AppID is {1}.) + + + The CLSID specified in the service file does not have a service element in a configuration file. (The CLSID is {0}.) + + + An endpoint configured for the COM+ CLSID {0} is not a configured interface on the class. (The contract type is {1}.) + + + The COM+ string in the .svc file was formatted incorrectly. (The string is "{0}".) + + + The contract type name in the configuration file was not in the form of an interface identifier. (The string is "{0}".) + + + The configured application was not found. (The Application ID was {0}.) + + + A transaction vote request was completed, but there was no outstanding vote request. + + + Failed to convert type library to assembly + + + Incorrect Interface version in registry + + + Failed to load type library + + + An attempt to load the native type library '{0}' was made. Native type libraries cannot be loaded. + + + Could not find interface in the Assembly + + + The '{0}' user-defined type could not be found. Ensure that the correct type and type library are registered and specified. + + + Could not find keyword {0}. + + + Invalid serializer specified. The only valid values are 'xml' and 'datacontract'. + + + The keyword '{0}' has no equal sign following it. Ensure that each keyword is followed by an equal sign and a value. + + + No value found for a keyword. + + + Badly terminated value {0}. + + + Missing Quote in value {0}. + + + Repeated moniker keyword. + + + Interface {0} not found in configuration. + + + Interface {0} has a null namespace or name. + + + Method {0} given in config was not found on interface {1}. + + + Only one type of server identity can be specified. + + + Address not specified. + + + Mex binding section name attribute not specified. + + + Mex address not specified. + + + Contract not specified. + + + Binding not specified. + + + Binding namespace not specified. + + + Failed to do mex retrieval:{0}. + + + None of the contract in metadata matched the contract specified. + + + The contract does not have an endpoint supporting the binding specified. + + + Moniker Missing Colon + + + Multiple server identity keywords were specified. Ensure that at most one identity keyword is specified. + + + The object does not support the interface '{0}'. + + + Could not duplicate the token (error=0x{0:X}). + + + Could not perform an AccessCheck (error=0x{0:X}). + + + Could not impersonate the anonymous user (error=0x{0:X}). + + + The provided SafeArray parameter was passed by value. SafeArray parameters must be passed by reference. + + + Multi-dimensional SafeArray parameters cannot be used. + + + The elements of the SafeArray must be of the type VARIANT. + + + The lower bound of the SafeArray was not zero. SafeArrays with a lower bound other than zero cannot be used. + + + Could not open the thread token (error=0x{0:X}). + + + Could not open the process token (error=0x{0:X}). + + + The isolation level for component {0} is invalid. (The value was {1}.) + + + The conversion between the client parameter type '{0}' to the required server parameter type '{1}' cannot be performed. + + + The required outer proxy could not be created. Ensure that the service moniker is correctly installed and registered. + + + Cannot load library {0}. Ensure that WCF is properly installed. + + + Interface Not Registered + + + Bad Interface Registration + + + The argument passed to SetObject is not a COM object. + + + No type library available for interface + + + Cannot find CLSID {0} in COM+ application {1}. + + + Cannot create an instance of the specified service: access is denied. + + + An internal error occurred attempting to create an instance of the specified service. + + + No services are configured for the application. + + + Access is denied. The message was not authenticated with a valid windows identity. + + + The session requirements of the contracts are inconsistent. All COM contracts in a service must have the same session requirement. + + + Access is denied. + + + Parameter at index {0} is null. + + + Unable to retrieve IUnknown for object. + + + QueryInterface succeeded but the persistable type wrapper was null. + + + Unexpected threading model. WCF/COM+ integration only supports STA and MTA threading models. + + + None of the methods were found for interface {0}. + + + The {0} operation on the service {1} could not be found in the catalog. + + + The interface with IID {0} cannot be exposed as a web service + + + The parameter named {0} of type {1} on method {2} of interface {3} cannot be serialized. + + + The return value of type {0} on method {1} of interface {2} cannot be serialized. + + + The COM+ Integration service '{0}' specified in configuration is not in a supported format and could not be started. Ensure that the configuration is correctly specified. + + + The method '{0}' could not be found. Ensure that the correct method name is specified. + + + The Dispatch ID '{0}' could not be found or is invalid. + + + At least one operation is asynchronous. Asynchronous operations are not allowed. + + + There are duplicate operations, which is invalid. Remove the duplicates. + + + The number of parameters in the request did not match the number supported by the method. Ensure that the correct number of parameters are specified. + + + Binding type {0} instance {1} not found in config. + + + The required address keyword was not specified. + + + The required binding keyword was not specified or is not valid. + + + A VARIANT parameter was passed by value. VARIANT parameters must be passed by reference. + + + The type for the '{0}' parameter in '{1}' within the namespace '{2}' cannot not be resolved. + + + The operation cannot be performed after the communications channel has been created. + + + The interface with IID {0} has no methods configured in the COM+ catalog and cannot be exposed as a web service. + + + The interface with IID {0} is not configured in the COM+ catalog and cannot be exposed as a web service. + + + The channeloption intrinsic object cannot be created because the channel builder is not initialized. + + + There is no transaction in the context of the operation. + + + The service does not accept issued tokens. + + + There was an error verifying some XML Schemas generated during export:\r\n{0} + + + There was a validation error on a schema generated during export:\r\n Source: {0}\r\n Line: {1} Column: {2}\r\n Validation Error: {3} + + + The Address, Binding and Contract keywords are required. + + + Type load for contract interface ID {0} failed with Error:{1}. + + + Fail to load binding {0} from config. Error:{1}. + + + Application {0} is marked Pooled. Pooled applications are not supported under COM+ hosting. + + + Application {0} has recycling enabled. Recycling of applications is not supported under COM+ hosting. + + + The client token at least needs to have the SecurityImpersonationLevel of at least Impersonation for Out of process Webhost activations. + + + This InstanceContext requires a valid Message to obtain the instance. + + + From: {0}\nAppId: {1}\nClsId: {2}\nIncoming TransactionId: {3}\nRequesting Identity: {4} + + + From: {0}\nAppId: {1}\nClsId: {2}\nIid: {3}\nAction: {4}\nInstance Id: {5}\nManaged Thread Id: {6}\nUnmanaged Thread Id: {7}\nRequesting Identity: {8} + + + AppId: {0}\nClsId: {1}\n + + + AppId: {0} + + + Iid: {0}\nType Library ID: {1} + + + A Windows hotfix or later service pack is required on Windows XP and Windows Server 2003 to use WS-AtomicTransaction and COM+ Integration Web service transaction functionality. See the Microsoft .NET Framework release notes for instructions on installing the required hotfix. + + + Generating manifest file {0} failed with {1}. + + + Directory {0} not found. + + + Cannot access directory {0}. + + + The object with CLSID '{0}' does not support the required IPersistStream interface. + + + CLSID of type {0} does not match the CLSID on PersistStreamTypeWrapper which is {1}. + + + Target object does not support IPersistStream. + + + Target type is an interface but corresponding type is not PersistStreamTypeWrapper. + + + CLSID {0} is not allowed. + + + Transferring to ComPlus logical thread {0}. + + + The cNamedArgs parameter is not supported and must be 0. + + + Binding '{0}' was not found in config. The config file must be present and contain a binding matching the one specified in the moniker. + + + The claimType cannot be an empty string. + + + X509Chain does not have any valid certificates. + + + X509CertificateValidationMode.Custom requires a CustomCertificateValidator. Specify the CustomCertificateValidator property. + + + UserNamePasswordValidationMode.MembershipProvider requires a MembershipProvider. Specify the MembershipProvider property. + + + UserNamePasswordValidationMode.Custom requires a CustomUserNamePasswordValidator. Specify the CustomUserNamePasswordValidator property. + + + The Security Support Provider Interface does not support Impersonation level 'None'. Specify Identification, Impersonation or Delegation level. + + + The public key is not an RSA key. + + + The '{0}' dynamic link library (dll) failed to load. + + + Writing audit messages to the Security log is not supported by the current platform. You must write audit messages to the Application log. + + + No custom principal is specified in the authorization context. + + + Access is denied. + + + SecurityAuditBehavior is not supported on the channel factory. + + + The Infocard token created during channel intialization has expired. Please create a new channel to reacquire token. + + + No Infocard token was found in the ChannelParameters. Infocard requires that the security token be created during channel intialization. + + + Message with action {0} received from a neighbor is missing a via Header. + + + The LinkUtility message received from a neighbor has invalid values for usefull '{0}' and total '{1}'. + + + Internal Error: Peer Neighbor state change from {0} to {1} is invalid. + + + The MaxReceivedMessageSize of the associated listener ({0}) is greater than the MaxReceivedMessageSize of the PeerNode ({1}) with the meshid ({2}), ensure that all ChannelFactories and Endpoints for this mesh have the same configuration for MaxRecievedMessageSize. + + + Binding settings conflict with an existing instance that is using the same mesh name. Check the value of the property {0}. + + + value must be >= {0} and <= {1}. + + + Invalid message: the peer channel via ({0}) has a size of ({1}) it exceeds the maximum via size of ({2}). + + + The PeerNode cannot be opened because it has been Aborted. + + + PNRP is not available. Please refer to the documentation with your system for details on how to install and enable PNRP. + + + The PNRP service is not installed on this machine. Please refer to the documentation with your system for details on how to install and enable PNRP. + + + A PeerResolverBindingElement is required in the {0} binding. The default resolver (PNRP) is not available. + + + Resolver must be specified. The default resolver (PNRP) is not available. Please refer to the documentation with your system for details on how to install and enable PNRP. + + + The specified ResolverType: {0} cannot be loaded. Please ensure that the type name specified refers to a type that can be loaded. + + + Specified resolver settings are not enough to create a valid resolver. Please ensure that a ResolverType and an Address is specified for the custom resolver. + + + The ListenIPAddress {0} is invalid. + + + Internal Error. PeerFlooder instance is already disposed. It cannot be used to send messages. + + + Internal Error. Address of the Service cannot be registered with PNRP. + + + The registrationId {0} is invalid. + + + Application message contains a header that conflicts with a PeerChannel specific header. Name = {0} and Namespace = {1}. + + + PNRP could not find any clouds that match the current operation. + + + "Specified addresses can not be registered with PNRP because either PNRP is not enabled or the specified addresses do not have corresponding clouds. Please refer to the documentation with your system for details on how to install and enable PNRP." + + + The binding's PeerTransportSecuritySettings can not be supported under the current system security configuration. + + + Credentials specified are not sufficient to carry requested operation. Please specify a valid value for {0}. + + + Connection was not accepted because the SecurityContext contained tokens that do not match the current security settings. + + + Addresses specified in the registration exceed PNRP's per registration address limit. + + + Provided information is Insufficient to create a valid connection to the resolver service. + + + Specified PeerResolverMode value {0} is invalid. Please specify either PeerResolveMode.Auto, Default, or Pnrp. + + + concrete PeerResolver implementation must override Initialize to accept metadata about resolver service. + + + The operation: {0} is not valid while the object is in open state. + + + The operation: {0} is not valid while the object is in closed state. + + + Registration info can not be null. Please ensure that the Register operation is invoked with a valid RegistrationInfo object. + + + Resolve info can not be null. Please ensure that the Resolve operation is invoked with a valid ResolveInfo object. + + + Refresh info can not be null. Please ensure that the Refresh operation is invoked with a valid RefreshInfo object. + + + MessageBody does not contain a valid {0} message. Please ensure that the message is well formed. + + + A peer registration with the service address {0} already exists. + + + MeshId: {0}, Node Id: {1}, Online: {2}, Open: {3}, Port: {4} + + + The MessagePropagationFilter threw an exception. Please refer to InnerException. + + + An event notification threw an exception. Please refer to InnerException. + + + The Peer resolver threw an exception. Please refer to InnerException. + + + One of the addresses specified doesn't match any PNRP cloud for registration.{0} + + + Specified cloud {0} could not be used for the specified operation because it is disabled. + + + Specified cloud {0} is configured for Resolve operations only. + + + Requested PNRP operation {0} could not be performed because the port is blocked possibly by a firewall. + + + Specified mesh name {0} cannot be used because a name can only be registered once per process. + + + Invalid RefreshInterval value of {0}; it must be greater than zero + + + Invalid CleanupInterval value of {0}; it must be greater than zero + + + Multiple link-local only interfaces detected. Please specifiy the interface you require by using the ListenIpAddress attribute in the PeerTransportBindingElement + + + Registration with zero addresses detected. Please call Register with more than zero addresses. + + + Certificate generation has failed. Please see the inner exception for more information. + + + Throttle on the mesh {0} waiting. + + + Attempting to prune the slow neighbor for the mesh {0}. + + + Maintainer is starting for the mesh {0}. + + + Maintainer is attempting a connection to Peer {0} for the mesh {1}. + + + Maintainer encountered exception when attempting a connection to Peer {0} for the mesh {1}. Exception is {2}. + + + Mantainer's InitialConnect is running for the mesh {0}. + + + Mantainer is attempting to prune connections for the mesh {0}. + + + Mantainer is attempting to establish additional connections for the mesh {0}. + + + BasicHttpContextBinding {0}:{1} requires that AllowCookies property is set to true. + + + The message contains a callback context header with an endpoint reference for AddressingVersion '{0}'. Callback context can only be transmitted when the AddressingVersion is configured with 'WSAddressing10'. + + + The callback address already has a context header in it. + + + The callback address contains multiple context headers. There can be at most one context header in a callback address. + + + The incoming message with action '{0}' contains a callback context header with name '{1}' and namespace '{2}'. Callback context headers are not expected in incoming messages at the client. + + + The message contains a callback context message property. Callback context can be transmitted only when the ContextBindingElement is configured with ContextExchangeMechanism of ContextSoapHeader. + + + ContextBindingElement cannot provide channel factory for the requested channel shape {0}. + + + ContextBindingElement cannot provide channel listener for the requested channel shape {0}. + + + Value '{0}' specified for 'name' attribute of ContextMessageProperty is either null or has invalid character(s). Please ensure value of 'name' is within the allowed value space. + + + Context protocol was unable to parse the context header. Nodes disallowed by the context header schema were found inside the context header. + + + The outgoing message with action '{0}' contains a callback context message property. Callback context cannot be transmitted in outgoing messages at the server. + + + Channel context management cannot be enabled or disabled after the channel is opened. + + + Context cached at the channel cannot be set or retrieved when the context management is disabled at the channel layer. Ensure context channel property 'IContextManager.Enabled' is set to true. + + + Context cached at the channel layer cannot be changed after the channel is opened. + + + Cannot specify 'ContextMessageProperty' in message when using context channel with context management enabled. Ensure the message does not have 'ContextMessageProperty' or disable context management by setting channel property 'IContextManager.Enabled' to false. + + + Context channel received a message with context which does not match the current context cached at the channel. Ensure service does not change context after it was originally set or disable context management by setting channel property 'IContextManager.Enabled' to false. + + + Service behavior {0} requires that the binding associated with endpoint {1} listening on {2} supports the context protocol, because the contract associated with this endpoint may require a session. Currently configured binding for this endpoint does not support the context protocol. Please modify the binding to add support for the context protocol or modify the SessionMode on the contract to NotAllowed. + + + Binding {1}:{2} is configured with ContextExchangeMechanism.HttpCookie which is not compatible with the transport type {0}. Please modify the ContextExchangeMechanism or use HTTP or HTTPS transport. + + + ContextBindingElement of binding {0}:{1} is configured with ContextExchangeMode.HttpCookie but the configuration of this binding's HttpTransportBindingElement prevents upper channel layers from managing cookies. Please set the HttpTransportBindingElement.AllowCookies property to false or change the ContextExchangeMechanism of ContextBindingElement to SoapHeader. + + + ContextBindingElementImporter cannot import policy because PolicyImportContext.BindingElements collection is null. + + + EndpointAddress: {0}, Via:{1} + + + Context protocol was unable to parse the context header. + + + Context protocol was unable to parse the callback context header. + + + The OLE Transactions header was invalid or corrupt. + + + The WS-AtomicTransaction header was invalid or corrupt. + + + The issued token accompanying the WS-AtomicTransaction coordination context was invalid or corrupt. + + + The OLE Transactions propagation token received in the message could not be used to unmarshal a transaction. It may be invalid or corrupt. + + + The WS-AtomicTransaction extended information included in the OLE Transactions propagation token was invalid or corrupt. + + + An error occurred communicating with the distributed transaction manager. + + + The WS-AtomicTransaction protocol service could not unmarshal the flowed transaction. The following exception occured: {0} + + + The transaction identifier element in the registration header is invalid + + + The context identifier element in the registration header is invalid. + + + The token identifier element in the registration header is invalid. + + + The transaction identifier element in the coordination context is invalid. + + + The WS-AtomicTransaction transaction formatter could not read the registry value '{0}'. + + + The MSDTC transaction manager's WS-AtomicTransaction protocol service '{0}' is disabled and cannot unmarshal incoming transactions. + + + The MSDTC transaction manager has disabled incoming transactions. + + + The incoming transaction cannot be unmarshaled because the source MSDTC transaction manager has either disabled outbound transactions or disabled its WS-AtomicTransaction protocol service. + + + A registration service address could not be created from MSDTC whereabouts information. + + + The MSDTC whereabouts information could not be deserialized. + + + The standard whereabouts signature was missing from the MSDTC whereabouts information. + + + The MSDTC whereabouts information's protocol count was invalid. + + + The MSDTC whereabouts information's host name byte count was invalid. + + + The MSDTC whereabouts information's host name was invalid. + + + The MSDTC whereabouts information did not contain a host name. + + + The specified WSAT protocol version is invalid. + + + The parameter cannot be empty. + + + The requested resouce has not changed and should be taken from cache. + + + The requested resource has moved to one of the following locations:\n{0} + + + The requested resource must be accessed through one of the following intermediary service locations:\n{0} + + + The requested resource has been moved. + + + At least one RedirectionLocation must be provided for this RedirectionType. + + + RedirectionType 'Cache' does not allow any RedirectionLocation objects be passed into the constructor. + + + {0} ({1}) + + + {0} + + + The requested resource is available. + + + Executing user callback. + + + Close '{0}'. + + + Construct ChannelFactory. Contract type: '{0}'. + + + Construct ServiceHost '{0}'. + + + Execute '{0}.{1}'. + + + Execute Async: Begin: '{0}.{1}'; End: '{2}.{3}'. + + + Close ChannelFactory. Contract type: '{0}'. + + + Close ClientBase. Contract type: '{0}'. + + + Close ServiceHost '{0}'. + + + Listen at '{0}'. + + + Open '{0}'. + + + Open ServiceHost '{0}'. + + + Open ChannelFactory. Contract type: '{0}'. + + + Open ClientBase. Contract type: '{0}'. + + + Process action '{0}'. + + + Processing message {0}. + + + Receive bytes on connection '{0}'. + + + Set up Secure Session. + + + Renew Secure Session. + + + Close Security Session. + + + Shared listener connection: '{0}'. + + + Socket connection: '{0}'. + + + Reading data from connection on '{0}'. + + + Receiving data at via '{0}'. + + + Begin method execution. + + + Created: {0} + + + Disposed: {0} + + + Sent a message over a channel + + + Prepared message for sending over a channel + + + ComPlus:channel created. + + + ComPlus:Dispatch method details. + + + ComPlus:DllHost initializer:Adding host. + + + ComPlus:Started DllHost initializer. + + + ComPlus:Starting DllHost initializer. + + + ComPlus:Stopped DllHost initializer. + + + ComPlus:Stopping DllHost initializer. + + + ComPlus:Entering COM+ activity. + + + ComPlus:Executing COM call. + + + ComPlus:Received instance creation request. + + + ComPlus:Created instance. + + + ComPlus:Released instance. + + + ComPlus:Invoked method. + + + ComPlus:Invoking method. + + + Complus:Invoking method with transaction in COM+ context. + + + Complus:Invoking method with new incoming transaction. + + + ComPlus:Left COM+ activity. + + + Complus:Mex channel loader loaded. + + + Complus:Metadata exchange completed successfully. + + + ComPlus:Created service contract. + + + ComPlus:Created service endpoint. + + + ComPlus:Started service. + + + ComPlus:Started service:details. + + + ComPlus:Starting service. + + + ComPlus:Stopped service. + + + ComPlus:Stopping service. + + + ComPlus:Service moniker parsed. + + + ComPlus:Type library converter event. + + + ComPlus:Finished type library import. + + + ComPlus:Type library import: using assembly. + + + ComPlus:Type library import: using type library. + + + ComPlus:Starting type library import. + + + ComPlus:Transaction aborted by COM+ context. + + + ComPlus:Transaction aborted by Transaction Manager. + + + ComPlus:Transaction committed. + + + ComPlus:Typed channel builder loaded. + + + ComPlus:WSDL channel builder loaded. + + + Aborted '{0}'. + + + Failed to abort {0} + + + Failed to close {0} + + + Closed {0} + + + Created {0} + + + Closing {0} + + + Disposing {0} + + + CommunicationObject faulted due to exception. + + + Faulted {0} + + + Failed to open {0} + + + Opened {0} + + + Opening {0} + + + The configuration is read-only. + + + Extension type is not configured. + + + The connection has been abandoned. + + + Connection information. + + + An exception occurred while closing the connections in this connection pool. + + + A connection has exceeded the idle timeout of this connection pool ({0}) and been closed. + + + A connection has exceeded the connection lease timeout of this connection pool ({0}) and been closed. + + + MaxOutboundConnectionsPerEndpoint quota ({0}) has been reached, so connection was closed and not stored in this connection pool. + + + MaxOutboundConnectionsPerEndpoint quota ({0}) has been reached, so the connection was closed and not reused by the listener. + + + No matching <service> tag was found. Default endpoints added. + + + Failed to trace a message + + + Did not understand message header. + + + A response message was received, but there are no outstanding requests waiting for this message. The message is being dropped. + + + The given schema cannot be imported in this format. + + + The type of the element does not match the configuration type. + + + End method execution. + + + Endpoint listener closed. + + + Endpoint listener opened. + + + Error invoking user code + + + Configuration evaluation context not found. + + + Starting Security ExportChannelBinding + + + Finished Security ExportChannelBinding + + + The extension collection does not exist. + + + The extension collection is empty. + + + Extension element not associated with an extension collection. + + + The extension element already exists in the collection. + + + Extension type not found. + + + Failed to set an activity id header on an outgoing message + + + Failed to read an activity id header on a message + + + Evaluating message logging filter against the message exceeded the node quota set on the filter. + + + Get BehaviorElement. + + + Get ChannelEndpointElement. + + + Get machine.config common behaviors. + + + Get configuration section. + + + Get configured binding. + + + Get default configured binding. + + + Get configured endpoint. + + + Get default configured endpoint. + + + Get ServiceElement. + + + Authentication failed for HTTP(S) connection + + + The HTTP SOAPAction header and the wsa:Action SOAP header did not match. + + + Failed to lookup a channel to receive an incoming message. Either the endpoint or the SOAP action was not found. + + + Failed to send request message over HTTP + + + Failed to send response message over HTTP + + + Received bad HTTP response + + + HTTP response was received + + + The HTTP concurrent receive quota was reached. + + + Client certificate is invalid. + + + Client certificate is invalid with native error code {0} (see http://go.microsoft.com/fwlink/?LinkId=187517 for details). + + + Client certificate is required. No certificate was found in the request. This might be because the client certificate could not be successfully validated by the operating system or IIS. For information on how to bypass those validations and use a custom X509CertificateValidator in WCF please see http://go.microsoft.com/fwlink/?LinkId=208540. + + + Starting Security ImportChannelBinding + + + Finished Security ImportChannelBinding + + + An existing incompatible transport manager was found for the specified URI. + + + Initiating Named Pipe connection. + + + Initiating TCP connection. + + + The IssuanceTokenProvider has started a new security negotiation. + + + The IssuanceTokenProvider has completed the security negotiation. + + + The IssuanceTokenProvider applied a redirection header. + + + The IssuanceTokenProvider removed the expired service token. + + + IssuanceTokenProvider pruned service token cache. + + + The IssuanceTokenProvider used the cached service token. + + + Listener created + + + Listener disposed + + + Maximum number of pending connections has been reached. + + + Maximum number of inbound session channel has been reached. + + + A message was closed + + + A message was closed again + + + A message was copied + + + Reached the limit of messages to log. Message logging is stopping. + + + Message not logged because its size exceeds configured quota + + + A message was read + + + Sent a message over a channel. + + + Received a message over a channel. + + + A message was written + + + Switched threads while processing a message. + + + MsmqActivation service cannot peek on the queue. + + + MsmqActivation service cannot discover queues. + + + MSMQ datagram message received. + + + MSMQ datagram message sent. + + + MSMQ detected successfully. + + + Entered batching mode. + + + Expected exception caught. + + + Hosting environment found the base address for the service. + + + Left batching mode. + + + MsmqActivation service found application matching queue. + + + Cannot move or delete message because it is still locked under the transaction. + + + Message was dropped. + + + Message was rejected. + + + Cannot move or delete message. + + + Poison message moved to the poison subqueue. + + + Poison message moved to the retry subqueue. + + + Poison message rejected. + + + Pool of the native MSMQ messages is full. This may affect performance. + + + Transaction which received this message was aborted at least once. + + + MSMQ queue closed. + + + MSMQ queue opened. + + + Cannot detect if the queue is transactional. + + + MsmqActivation service started scan for queues. + + + MSMQ transport session received. + + + MSMQ transport session sent. + + + MSMQ Activation service started application. + + + Hosting environment started service. + + + Unexpected acknowledgment value. + + + Failed to receive a message over a named pipe channel. + + + Received a message over a named pipe channel. + + + NegotiationTokenAuthenticator was attached. + + + NegotiationTokenProvider was attached. + + + No existing transport manager was found for the specified URI. + + + Transport is listening at base URI. + + + The configuration system has detected a duplicate key in a different configuration scope and is overriding with the more recent value. + + + A message was received by a peer channel. + + + A message was sent on a peer channel. + + + A PeerNode received a message that did not match any local channels. + + + A PeerNode received a flooded message that was not propagated further. + + + A PeerNode received a flooded message. + + + Received message could not be forwarded to other neighbors since it exceeded the quota set for the peer node. + + + A Peer Neighbor close has failed. + + + A Peer Neighbor closing has failed. + + + A Peer Neighbor Manager is offline. + + + A Peer Neighbor Manager is online. + + + A message was received by a Peer Neighbor. + + + A Peer Neighbor was not accepted. + + + A Peer Neighbor was not found. + + + A Peer Neighbor open has failed. + + + A Peer Neighbor state change has failed. + + + A Peer Neighbor state has changed. + + + A PeerNode address has changed. + + + A neighbor connection could not be established due to insufficient or wrong credentials. + + + A neighbor security handshake as timed out. + + + A PeerNode was closed. + + + A PeerNode is closing. + + + Peer node open failed. + + + A PeerNode was opened. + + + A PeerNode is opening. + + + Message source could not be authenticated. + + + PeerService Opened and listening at '{0}'. + + + A performance counter failed to load. Some performance counters will not be available. + + + Failed to load the performance counter '{0}'. Some performance counters will not be available + + + There was an error while updating the performance counter '{0}'. This performance counter will be disabled. + + + Loading performance counters for the service failed. Performance counters will not be available for this service. + + + Unloading the performance counters failed. + + + Registered addresses in PNRP. + + + Resolved addresses in PNRP. + + + Unexpected Exception during PNRP resolve operation. + + + Unregistered addresses in PNRP. + + + A null Message (signalling end of channel) was received from a datagram channel, but the channel is still in the Opened state. This indicates a bug in the datagram channel, and the demuxer receive loop has been prematurely stalled. + + + PeerMaintainer Activity. + + + A reliable channel has been opened. + + + Behavior type already exists in the collection + + + Received reply over request channel + + + A failure occured while performing a security related operation. + + + An active security session was removed by the server. + + + A failure occurred while writing to the security audit log. + + + The security audit log is written successfully. + + + The security protocol verified the incoming message. + + + The security protocol secured the outgoing message. + + + The security protocol cannot secure the outgoing message. + + + The security protocol cannot verify the incoming message. + + + The client security session renewed the session key. + + + A Close message was sent by the client security session. + + + Close response message was sent by client security session. + + + Close message was received by client security session.TraceCodeSecurityClientSessionKeyRenewed=Client security session renewed session key. + + + The client security session discarded the previous session key. + + + The SecurityContextSecurityToken cache is full. + + + Identity cannot be determined for an EndpointReference. + + + Identity was determined for an EndpointReference. + + + The HostName portion of an endpoint address cannot be normalized. + + + Identity verification failed. + + + Identity verification succeeded. + + + Security impersonation failed at the server. + + + Security Impersonation succeeded at the server. + + + An inactive security session was faulted by the server. + + + Service security negotiation processing failure. + + + A new security session key was issued by the server. + + + A pending security session was added to the server. + + + The pending security session was closed by the server. + + + A pending security session was activated by the server. + + + The server security session received a close message from the client. + + + Server security session received Close response message from client. + + + Server security session sent session aborted fault to client. + + + The security session key was updated by the server. + + + The server security session sent a key renewal fault to the client. + + + The server security session sent a close response to the client. + + + Server security session sent Close to client. + + + Client security session received session aborted fault from server. + + + Failure sending security session aborted fault to client. + + + The client security session received a closed reponse from the server. + + + A failure occurred when sending a security session Close response to the client. + + + Failure sending security session Close to client. + + + The client security session received a key renewal fault from the server. + + + The client security session was redirected. + + + A failure occurred when sending a renewal fault on the security session key to the client. + + + The client security session operation failed. + + + The security session operation completed successfully at the client. + + + A security session operation was started at the client. + + + The security session operation failed at the server. + + + The ServicePrincipalName could not be mapped to a SecurityIdentifier. + + + Security Token Authenticator was closed. + + + Security Token Authenticator was opened. + + + Security Token Provider was closed. + + + Security Token Provider was opened. + + + ServiceChannel information. + + + ServiceHost base addresses. + + + ServiceHost close operation timedout. + + + ServiceHost faulted. + + + ServiceHost error on calling ReleasePerformanceCounters. + + + The system hit the limit set for throttle '{0}'. Limit for this throttle was set to {1}. Throttle value can be changed by modifying attribute '{2}' in serviceThrottle element or by modifying '{0}' property on behavior ServiceThrottlingBehavior. + + + The system hit an internal throttle limit. Limit for this throttle was set to {0}. This throttle cannot be configured. + + + The system hit the limit set for the '{0}' throttle. Throttle value can be changed by modifying {0} property on {1}. + + + Switched threads while processing a message for Contract '{0}' at Address '{1}'. ConcurrencyMode for service is set to Single/Reentrant and the service is currently processing another message. + + + Switched threads while processing a message for Contract '{0}' at Address '{1}'. Cannot process more than one transaction at a time and the transaction associated with the previous message is not yet complete. Ensure that the caller has committed the transaction. + + + Switched threads while processing a message for Contract '{0}' at Address '{1}'. Waiting for the completion of ReceiveContext acknowledgement. If your service seems to be not processing the message ensure that the channel implementation of receive context completes the operation. + + + Switched threads while processing a message for Contract '{0}' at Address '{1}'. UseSynchronizationContext property on ServiceBehaviorAttribute is set to true, and SynchronizationContext.Current was non-null when opening ServiceHost. If your service seems to be not processing messages, consider setting UseSynchronizationContext to false. + + + Replying to an operation threw a exception. + + + The Request/Reply operation {0} has no Reply Message. + + + The Request/Reply operation {0} has no IRequestContext to use for the reply. + + + Service security negotiation completed. + + + The incoming message is not part of an existing security session. + + + Create ServiceHost. + + + The TransportManager was successfully closed. + + + A named pipe was successfully duplicated. + + + A socket was successfully duplicated. + + + The PROCESS_DUP_HANDLE access right has been granted to the {0} service's account with SID '{1}'. + + + The TransportManager is now successfully listening. + + + Behavior type is not of expected type + + + An attempt to reuse a pooled connection failed. Another attempt will be made with {0} remaining in the overall timeout. + + + An attempt to connect to the named pipe endpoint at '{1}' failed. Another attempt will be made with {0} remaining in the overall timeout. + + + The operating system's timer resolution was detected as {0} ticks, which is about {1} milliseconds. + + + RequestContext aborted + + + PipeConnection aborted + + + The shared memory for the endpoint of the service '{0}' does not exist. The service may not be started. + + + SocketConnection aborted + + + SocketConnection aborted under Close + + + SocketConnection close + + + SocketConnection create + + + SpnegoTokenProvider completed SSPI negotiation. + + + SpnegoTokenAuthenticator completed SSPI negotiation. + + + Client's outgoing SSPI negotiation. + + + Service's outgoing SSPI negotiation. + + + The remote SSL client failed to provide a required certificate. + + + The stream security upgrade was accepted successfully. + + + Failed to receive a message over TCP channel + + + Received a message over TCP channel + + + Understood message header. + + + No service available to handle this action + + + Unhandled exception in user operation '{0}.{1}'. + + + Webhost could not activate service + + + Webhost couldn't compile service + + + Setting a value via WMI. + + + A non-critical error or warning occurred during WSDL Export + + + A non-critical error or warning occurred in the MetadataExchangeClient during WSDL Import This could result in some endpoints not being imported. + + + An incoming channel was disposed because there was an error while attempting to open it. + + + Listen at '{0}'. + + + An invalid create sequence message was received. + + + An invalid WS-RM message was received. + + + An incoming create sequence request was rejected because the maximum pending channel count was reached. + + + A message in a WS-RM sequence has been dropped because it could not be buffered. + + + The reliable session infrastructure detected a system clock change. This will temporarily result in a less optimal message retry strategy. + + + WS-RM SequenceAcknowledgement received. + + + WS-RM Last Sequence message received. + + + WS-RM Sequence message received. + + + WS-RM SequenceAcknowledgement sent. + + + WS-RM Last Sequence message sent. + + + WS-RM Sequence message sent. + + + A WS-RM sequence has faulted. + + + Channel connection was dropped + + + An async callback threw an exception! + + + The MetadataExchangeClient is sending a request for metadata. + + + The MetadataExchangeClient received a reply. + + + The ServiceDebugBehavior Help Page is enabled at a relative address and cannot be created because there is no base address. + + + The TCP connect operation failed. + + + The transaction '{0}' was received for operation '{1}' from a transacted transport, such as MSMQ. + + + The transaction '{0}' was flowed to operation '{1}'. + + + The transaction '{0}' was received for operation '{1}' from an InstanceContext transaction. + + + Existing transaction '{0}' being used for operation '{1}'. + + + The transaction '{0}' for operation '{1}' was completed due to the TransactionAutoComplete OperationBehaviorAttribute member being set to true. + + + The transaction '{0}' for operation '{1}' was completed due to an unhandled execution exception. + + + The transaction '{0}' for operation '{1}' was completed due to a call to SetTransactionComplete. + + + The transaction '{0}' was completed when the session was closed due to the TransactionAutoCompleteOnSessionClose ServiceBehaviorAttribute member. + + + The transaction '{0}' for operation '{1}' was completed due to asynchronous abort. + + + The transaction '{0}' for operation '{1}' remains attached to the InstanceContext. + + + The transaction '{0}' was aborted because it was uncompleted when the session was closed and the TransactionAutoCompleteOnSessionClose OperationBehaviorAttribute was set to false. + + + The service instance was released on the completion of the transaction '{0}' because the ReleaseServiceInstanceOnTransactionComplete ServiceBehaviorAttribute was set to true. + + + The transaction '{0}' was asynchronously aborted. + + + The OleTransactions protocol negotiation failed for coordination context '{0}'. + + + The transaction '{0}' for operation '{1}' was newly created. + + + Activating message received. + + + InstanceContext cached for InstanceId {0}. + + + InstanceContext for InstanceId {0} removed from cache. + + + DurableInstance's InstanceContext refcount incremented. + + + DurableInstance's InstanceContext refcount decremented. + + + ContextChannel created. + + + A new ContextChannel was accepted. + + + Context added to Message. + + + Context retrieved from Message. + + + WorkflowServiceHost created. + + + ServiceDurableInstance '{0}' deleted from persistence store. + + + ServiceDurableInstance '{0}' disposed. + + + ServiceDurableInstance loaded from persistence store. + + + ServiceDurableInstance saved to persistence store. + + + WorkflowDurableInstance '{0}' loaded. + + + WorkflowDurableInstance '{0}' activated. + + + WorkflowDurableInstance aborted. + + + Work item enqueued. + + + Reply sent for InstanceId {0}. + + + Fault Sent for InstanceId {0}. + + + Sql execution started. + + + Sql execution complete. + + + SqlPersistenceProvider.Open() parameters. + + + SynchronizationContextWorkflowSchedulerService - Timer {0} cancelled. + + + SynchronizationContextWorkflowSchedulerService - Timer {0} created for InstanceId {1}. + + + Reading of a syndication feed started. + + + Reading of a syndication feed completed. + + + Reading of a syndication item started. + + + Reading of a syndication item completed. + + + Writing of a syndication feed started. + + + Writing of a syndication feed completed. + + + Writing of a syndication item started. + + + Writing of a syndication item completed. + + + Syndication element with name '{0}' and namespace '{1}' was not written. + + + Syndication element with name '{0}' and namespace '{1}' is invalid. + + + HTTP query string parameter with name '{0}' was ignored. + + + Incoming HTTP request with URI '{0}' matched operation '{1}'. + + + Incoming HTTP request with URI '{0}' does not match any operation. + + + Parameter 'baseAddress' must an absolute uri. + + + BaseAddress must an absolute uri. + + + Cannot change BaseAddress after calling MakeReadOnly. + + + There were multiple UriTemplateMatch results, but MatchSingle was called. + + + BaseAddress has not been set. Set the BaseAddress property before calling MakeReadOnly, Match, or MatchSingle. + + + KeyValuePairs must have at least one element. + + + UriTemplate '{0}' contains {1} path variables and {2} query variables but {3} values were passed to the BindByPosition method. The number of values passed to BindByPosition should be greater than or equal to the number of path variables in the template and cannot be greater than the total number of variables in the template. + + + baseAddress must an absolute Uri. + + + The UriTemplate '{0}' is not valid; each portion of the query string must be of the form 'name' or of the form 'name=value', where each name is unique. Note that the names are case-insensitive. See the documentation for UriTemplate for more details. + + + The UriTemplate '{0}' is not valid; the query string cannot end with '&amp;'. See the documentation for UriTemplate for more details. + + + The UriTemplate '{0}' is not valid; each portion of the query string must be of the form 'name' or of the form 'name=value'. See the documentation for UriTemplate for more details. + + + The UriTemplate '{0}' is not valid; the UriTemplate variable named '{1}' appears multiple times in the template. Note that UriTemplate variable names are case-insensitive. See the documentation for UriTemplate for more details. + + + UriTemplateTable does not support '{0}' and '{1}' since they are not equivalent, but cannot be disambiguated because they have equivalent paths and the same common literal values for the query string. See the documentation for UriTemplateTable for more detail. + + + UriTemplateTable does not support multiple templates that have equivalent path as template '{0}' but have different query strings, where the query strings cannot all be disambiguated via literal values. See the documentation for UriTemplateTable for more detail. + + + UriTemplateTable (with allowDuplicateEquivalentUriTemplates = false) does not support both '{0}' and '{1}', since they are equivalent. Call MakeReadOnly with allowDuplicateEquivalentUriTemplates = true to use both of these UriTemplates in the same table. See the documentation for UriTemplateTable for more detail. + + + UriTemplate does not support '{0}' as a valid format for a segment or a query part. + + + The path variable '{0}' in the UriTemplate must be bound to a non-empty string value. + + + UriTemplate '{0}' contains no variables; yet the BindByPosition method was called with {1} values. + + + UTCSR - Lookup was called before match + + + The UriTemplate '{0}' is not valid; UriTemplate does not support two adjacent variables with no literal in compound segments, such as in the segment '{1}'. + + + The UriTemplate '{0}' is not valid; each portion of the query string must be of the form 'name=value', when value cannot be a compound segment. See the documentation for UriTemplate for more details. + + + The UriTemplate '{0}' is not valid; each portion of the query string must be of the form 'name' or of the form 'name=value', where name is a simple literal. See the documentation for UriTemplate for more details. + + + Changing an inline default value with information from the additional default values is not supported; the default value to the variable '{0}' was already provided as part of the UriTemplate '{1}'. See the documentation for UriTemplate for more details. + + + The default values of UriTemplate are immutable; they cannot be modified after the construction of the UriTemplate instance. See the documentation of UriTemplate for more details. + + + The UriTemplate '{0}' is not valid; the UriTemplate compound path segment '{1}' provides a default value to variable '{2}'. Note that UriTemplate doesn't support default values to variables in compound segments. See the documentation for UriTemplate for more details. + + + The UriTemplate '{0}' is not valid; the UriTemplate variable declaration '{1}' provides a default value to query variable '{2}'. Note that UriTemplate doesn't support default values to query variables. See the documentation for UriTemplate for more details. + + + The UriTemplate '{0}' is not valid; the UriTemplate variable declaration '{1}' provides an empty default value to path variable '{2}'. Note that UriTemplate path variables cannot be bound to a null or empty value. See the documentation for UriTemplate for more details. + + + The UriTemplate '{0}' is not valid; the UriTemplate variable declaration '{1}' isn't a valid variable construct. Note that UriTemplate variable definitions are either a simple, non-empty, variable name or a 'name=value' format, where the name must not be empty and the value provides a default value to the variable. See the documentation for UriTemplate for more details. + + + The UriTemplate '{0}' is not valid; the wildcard ('{1}') cannot appear in a variable name or literal, unless as a construct for a wildcard segment. Note that a wildcard segment, either a literal or a variable, is valid only as the last path segment in the template; the wildcard can appear only once. See the documentation for UriTemplate for more details. + + + The UriTemplate '{0}' is not valid; the UriTemplate last path segment '{1}' provides a default value to final star variable '{2}'. Note that UriTemplate doesn't support default values to final star variable. See the documentation for UriTemplate for more details. + + + The UriTemplate '{0}' is not valid; the path variable '{1}', defined as part of a compound path segment has been provided with a default value as part of the additional defaults. Note that UriTemplate doesn't support default values to variables in compound segments. See the documentation for UriTemplate for more details. + + + The UriTemplate '{0}' is not valid; the query variable '{1}' has been provided a default value as part of the additional defaults. Note that UriTemplate doesn't support default values to query variables. See the documentation for UriTemplate for more details. + + + The UriTemplate '{0}' is not valid; the additional default value '{1}' has a null value as default value. Note that null default values must be only provided to concrete path variables. See the documentation for UriTemplate for more details. + + + The UriTemplate '{0}' is not valid; the UriTemplate path variable '{1}' has a null default value while following path variable '{2}' has no defaults or provides a non-null default value. Note that UriTemplate path variable with null default value must be followed only with other path variables with null defaulted values. See the documentation for UriTemplate for more details. + + + The UriTemplate '{0}' is not valid; the UriTemplate path variable '{1}' has a null default value while the following path segment '{2}' is not a variable segment with a null default value. Note that UriTemplate path variable with null default values must be followed only with other path variables with null defaulted value. See the documentation for UriTemplate for more details. + + + The UriTemplate '{0}' is not valid; the UriTemplate path variable '{1}' has a null default value while the template is finished with a wildcard. Note that UriTemplate path variable with null default values must be followed only with other path variables with null defaulted value. See the documentation for UriTemplate for more details. + + + The UriTemplate '{0}' is not valid; the UriTemplate final star variable '{1}' has been provides a default value as part of the additional defaults information. Note that UriTemplate doesn't support default values to final star variable. See the documentation for UriTemplate for more details. + + + An invalid template '{0}' was passed as the key in a pair of template and its associated object. UriTemplateTable Key-Value pairs must always contain a valid UriTemplate object as key; note that UriTemplateTable doesn't support templates that are ignoring the trailing slash in respect to matching. See the documentation for UriTemplateTable for more details. + + + A null UriTemplate was passed as the key in a pair of template and its associated object. UriTemplateTable Key-Value pairs must always contain a valid UriTemplate object as key. See the documentation for UriTemplateTable for more details. + + + The BindByName method of UriTemplate was called with an empty name in the collection of arguments for the bind. Note that the NameValueCollection or the Dictionary passed to BindByName cannot contain an empty (or null) name as a key. See the documentation of UriTemplate for more details. + + + The UriTemplate contains a literal value for query key '{0}', but that key also is present in the NameValueCollection. Either remove that key from the NameValueCollection, or else change the UriTemplate to not have a query literal for that key. + + + The name of the extension element must be specified. + + + The Rss20Serializer does not support RSS version '{0}'. + + + The Atom10 specification requires '{0}' to have one of these values: \"text\", \"html\", \"xhtml\", however this value is '{1}' in the document being deserialized. + + + Error in line {0} position {1}. + + + An error was encountered when parsing the feed's XML. Refer to the inner exception for more details. + + + An error was encountered when parsing the document's XML. Refer to the inner exception for more details. + + + An error was encountered when parsing the item's XML. Refer to the inner exception for more details. + + + An error was encountered when parsing a DateTime value in the XML. + + + The outer element name must be specified. + + + The element with name '{0}' and namespace '{1}' is not an allowed feed format. + + + The element with name '{0}' and namespace '{1}' is not an allowed document format. + + + The element with name '{0}' and namespace '{1}' is not an allowed item format. + + + The syndication feed formatter must be configured with a syndication feed. + + + The document formatter must be configured with a document. + + + The syndication item formatter must be configured with a syndication item. + + + A feed containing items that are not buffered (i.e. the items are not stored in an IList) cannot clone its items. Buffer the items in the feed before calling Clone on it or pass false to the Clone method. + + + The feed being deserialized has non-contiguous sets of items in it. This is not supported by '{0}'. + + + The feed created a null category. + + + The item created a null category. + + + The feed created a null person. + + + The item created a null person. + + + =The feed created a null item. + + + Reading of a syndication feed started. + + + Reading of a syndication feed completed. + + + Reading of a syndication item started. + + + Reading of a syndication item completed. + + + Writing of a syndication feed started. + + + Writing of a syndication feed completed. + + + Writing of a syndication item started. + + + Writing of a syndication item completed. + + + Syndication XML node of type '{0}' with name '{1}' and namespace '{2}' ignored on read. + + + Reading of a service document started. + + + Reading of a service document completed. + + + Writing of a service document started. + + + Writing of a service document completed. + + + Reading of a categories document started. + + + Reading of a categories document completed. + + + Writing of a categories document started. + + + Writing of a categories document completed. + + + The feed's authors were not serialized as part of serializing the feed in RSS 2.0 format. + + + The feed's contributors were not serialized as part of serializing the feed in RSS 2.0 format. + + + The feed's id was not serialized as part of serializing the feed in RSS 2.0 format. + + + The feed's links were not serialized as part of serializing the feed in RSS 2.0 format. + + + The item's authors were not serialized as part of serializing the feed in RSS 2.0 format. + + + The item's contributors were not serialized as part of serializing the feed in RSS 2.0 format. + + + The item's links were not serialized as part of serializing the feed in RSS 2.0 format. + + + The item's copyrights were not serialized as part of serializing the feed in RSS 2.0 format. + + + The item's content was not serialized as part of serializing the feed in RSS 2.0 format. + + + The item's last updated time was not serialized as part of serializing the feed in RSS 2.0 format. + + + The outer name of the element extension cannot be empty. + + + The Type of object passed as parameter '{0}' is not derived from {1}. Ensure that the type of object passed is either of type {1} or derived from {1}. + + + Failed to impersonate client identity during serialization of the response message. + + + Line {0}, position {1}. + + + end of file + + + element '{0}' from namespace '{1}' + + + end element '{0}' from namespace '{1}' + + + text '{0}' + + + cdata '{0}' + + + comment '{0}' + + + node {0} + + + Start element expected. Found {0}. + + + A single WSDL document could not be generated for this service. Multiple service contract namespaces were found ({0}). Ensure that all your service contracts have the same namespace. + + + You can also access the service description as a single file: + + + The use of '{0}' on the task-based asynchronous method is not supported. + + + Client side task-based asynchronous method must not have any out or ref parameters. Any data that would have been returned through an out or ref parameter should instead be returned as part of the TResult in the resulting task. + + + Generating message contract since the operation has multiple return values. + + + ID0020: The collection is empty. + + + ID2004: IAsyncResult must be the AsyncResult instance returned from the Begin call. The runtime is expecting '{0}', and the actual type is '{1}'. + + + ID3002: WSTrustServiceContract could not create a SecurityTokenService instance from WSTrustServiceContract.SecurityTokenServiceConfiguration. + + + ID3004: Cannot obtain the schema for namespace: '{0}'. + + + ID3022: The WSTrustServiceContract only supports receiving RequestSecurityToken messages. If you need to support more message types, override the WSTrustServiceContract.DispatchRequest method. + + + ID3023: The WSTrustServiceContract only supports receiving RequestSecurityToken messages asynchronously. If you need to support more message types, override the WSTrustServiceContract.BeginDispatchRequest and EndDispatchRequest. + + + ID3097: ServiceHost does not contain any valid Endpoints. Add at least one valid endpoint in the SecurityTokenServiceConfiguration.TrustEndpoints collection. + + + ID3112: Unrecognized RequestType '{0}' specified in the incoming request. + + + ID3113: The WSTrustServiceContract does not support receiving '{0}' messages with the '{1}' SOAP action. If you need to support this, override the ValidateDispatchContext method. + + + ID3114: The WSTrustServiceContract cannot deserialize the WS-Trust request. + + + ID3137: The TrustVersion '{0}', is not supported, only 'TrustVersion.WSTrust13' and 'TrustVersion.WSTrustFeb2005' is supported. + + + ID3138: The RequestSecurityTokenResponse that was received did not contain a SecurityToken. + + + ID3139: The WSTrustChannel cannot compute a proof key. The KeyType '{0}' is not supported. Valid proof key types supported by the WSTrustChannel are WSTrust13 and WSTrustFeb2005. + + + ID3140: Specify one or more BaseAddresses to enable metadata or set DisableWsdl to true in the SecurityTokenServiceConfiguration. + + + ID3141: The RequestType '{0}', is not supported. If you need to support this RequestType, override the corresponding virtual method in your SecurityTokenService derived class. + + + ID3144: The PortType '{0}' Operation '{1}' has Message '{2}' is expected to have only one part but contains '{3}'. + + + ID3146: WsdlEndpointConversionContext.WsdlPort cannot be null. + + + ID3147: WsdlEndpointConversionContext.WsdlPort.Service cannot be null. + + + ID3148: WsdlEndpointConversionContext.WsdlPort.Service.ServiceDescription cannot be null. + + + ID3149: Cannot find an input message type for PortType '({0}, {1})' for operation '{2}' in the given ServiceDescription. + + + ID3150: Cannot find an output message type for PortType '({0}, {1})' for operation '{2}' in the given ServiceDescription. + + + ID3190: The WSTrustChannel cannot compute a proof key without a valid SecurityToken set as the RequestSecurityToken.UseKey when the RequestSecurityToken.KeyType is '{0}'. + + + ID3191: The WSTrustChannel received a RequestedSecurityTokenResponse message containing an Entropy without a ComputedKeyAlgorithm. + + + ID3192: The WSTrustChannel cannot compute a proof key. The received RequestedSecurityTokenResponse does not contain a RequestedProofToken and the ComputedKeyAlgorithm specified in the response is not supported: '{0}'. + + + ID3193: The WSTrustChannel cannot compute a proof key. The received RequestedSecurityTokenResponse indicates that the proof key is computed using combined entropy. However, the response does not include an entropy. + + + ID3194: The WSTrustChannel cannot compute a proof key. The received RequestedSecurityTokenResponse indicates that the proof key is computed using combined entropy. However, the request does not include an entropy. + + + ID3269: Cannot determine the TrustVersion. It must either be specified explicitly, or a SecurityBindingElement must be present in the binding. + + + ID3270: The WSTrustChannel does not support multi-leg issuance protocols. The RSTR received from the STS must be enclosed in a RequestSecurityTokenResponseCollection element. + + + ID3285: The WS-Trust operation '{0}' is not valid or unsupported. + + + ID3286: The 'inner' parameter must implement the 'CoreWCF.Channels.IChannel' interface. + + + ID3287: WSTrustChannelFactory does not support changing the value of this property after a channel is created. + + + ID4008: '{0}' does not provide an implementation for '{1}'. + + + ID4039: A custom ServiceAuthorizationManager has been configured. Any custom ServiceAuthorizationManager must be derived from IdentityModelServiceAuthorizationManager. + + + ID4041: Cannot configure the ServiceHost '{0}'. The ServiceHost is in a bad state and cannot be configured. + + + ID4053: The token has WS-SecureConversation version '{0}'. Version '{1}' was expected. + + + ID4072: The SecurityTokenHandler '{0}' registered for TokenType '{1}' must derive from '{2}'. + + + ID4101: The token cannot be validated because it is not a SamlSecurityToken or a Saml2SecurityToken. Token type: '{0}' + + + ID4192: The reader is not positioned on a KeyInfo element that can be read. + + + ID4240: The tokenRequirement must derived from 'RecipientServiceModelSecurityTokenRequirement' for SecureConversationSecurityTokens. The tokenRequirement is of type '{0}'. + + + ID4244: Internal error: sessionAuthenticator must support IIssuanceSecurityTokenAuthenticator. + + + ID4245: Internal error: sessionAuthenticator must support ICommunicationObject. + + + ID4268: MergeClaims must have at least one identity that is not null. + + + ID4271: No IAuthorizationPolicy was found for the Transport security token '{0}'. + + + ID4274: The Configuration property of this SecurityTokenHandler is set to null. Tokens cannot be read or validated in this state. Set this property or add this SecurityTokenHandler to a SecurityTokenHandlerCollection with a valid Configuration property. + + + ID4285: Cannot replace SecurityToken with Id '{0}' in cache with new one. Token must exist in cache to be replaced. + + + ID4287: The SecurityTokenRequirement '{0}' doesn't contain a ListenUri. + + + ID5004: Unrecognized namespace: '{0}'. + + + Authorize + + + OnAuthorizeRequest Failed. + + + OnAuthorizeRequest Succeeded. + + + Authentication failed. + + + The IssuedSecurityTokenProvider cannot support the FederatedClientCredentialsParameters. The FederatedClientCredentialsParameters has already provided the '{0}' parameter. + + + The TrustVersion '{0}', is not supported, only 'TrustVersion.WSTrust13' and 'TrustVersion.WSTrustFeb2005' is supported. + + + The input {0} must be a '{1}' object. + + + The input handler list cannot be empty. + + + The '{0}' list is invalid because the property '{1}' of '{2}' is not null. + + + The '{0}' list created by the Func '{1}' is invalid because it contains one or more null items. + + + The config element '{0}' is invalid because the attribute '{1}' and the sub element '{2}' were both specified. These are mutually exclusive items and cannot be used simultaneouly. + + + This '{0}' object cannot be used to generate configuration because it was created with the constructor that takes a '{1}' as the paramter. This functionality is not supported through configuration files. Please use a different constructor if you wish to generate a configuration file. + + + Invalid type: '{0}'. It must inherit from base type '{1}', cannot be abstract, and must expose a public default constructor. + + + '{0}' cannot return a null '{1}' instance. Please ensure that '{0}' returns a valid '{1}' instance. + + + HTTP pipeline operation cancelled. + + + The message property '{0}' is missing in the HttpRequestMessage. Please make sure this property not removed or changed from the properties of the HttpRequestMessage. If you are creating a new HttpRequestMessage, please copy this property from the old message to the new one. + + + The message property '{0}' inside the HttpRequestMessage is not with expected type '{1}'. Please make sure this property not removed or changed from the properties of the HttpRequestMessage. If you are creating a new HttpRequestMessage, please copy this property from the old message to the new one. + + + The value '{0}' is not a valid content type. + + + The property '{0}' is not supported when building a ChannelFactory. The property value must be null when calling BuildChannelFactory. + + + Cound not load type '{0}' from the assemblies in current AppDomain. + + + The HTTP response message should not be null. Please ensure your '{0}' instance returns a non-null '{1}' object. + + + The subprotocol '{0}' was not requested by the client - no '{1}' header was included in the request. + + + The subprotocol '{0}' was not requested by the client. The client requested the following subprotocol(s): '{1}'. + + + The subprotocol '{0}' is invalid because it contains the invalid character '{1}'. + + + The value specified ('{0}') contains more than one subprotocol which is not supported. + + + Empty string is not a valid subprotocol value. Please use "null" to specify no value. + + + This method is not supported for this HTTP content. + + + Invalid value for the {0} type. The type '{1}' does not derive from the appropriate base class '{2}' or is abstract. + + + This service only supports WebSocket connections. + + + This service does not support WebSocket connections. + + + WebSocket upgrade request failed. Received response status code '{0} ({1})', expected: '{2} ({3})'. + + + WebSocket upgrade request failed. The header '{0}' is missing in the response. + + + WebSocket upgrade request failed. The value of header '{0}' is '{1}'. The expected value is '{2}'. + + + Unexpected response - the server accepted the upgrade request but specified the subprotocol '{0}' when no subprotocol was requested. + + + WebSocket object cannot be accessed directly. + + + A WebSocket error occurred. + + + Unexpected WebSocket close message received when receiving a message. + + + Cannot write to the stream because the end of the stream marker was already written. + + + HttpChannelFactory cannot create the channel with shape '{0}' when the {1} of {2} was set as '{3}'. + + + Maximum number of pending WebSocket connections ({0}) has been reached. Consider increasing the '{1}' quota on the '{2}' property of the transport. + + + The opening handshake properties associated with the current WebSocket connection are not available. The most likely cause is that the property '{0}' on the '{1}' object returned from the custom '{2}' is not set. + + + The operation to establish the WebSocket connection timed out. To increase this time limit, use the OpenTimeout property on the service endpoint's binding. + + + The task was cancelled. + + + An error occured when getting the WebSocketVersion from the WebSocket factory of type '{0}'. See the inner exception for details. + + + The WebSocketVersion returned by the WebSocket factory of type '{0}' is either null, empty or invalid. + + + An error occurred when creating the WebSocket with the factory of type '{0}'. See the inner exception for details. + + + WebSocket creation failed. The '{0}' returned a WebSocket that is either null or not opened. + + + The WebSocket returned by the factory of type '{0}' has the SubProtocol '{1}' that doesn't match the requested SubProtocol value '{2}'. + + + The '{0}' contains multiple '{1}' objects, which is invalid. At most one '{1}' should be specified. + + + The Send operation timed out after '{0}'. Increase the SendTimeout value on the Binding. The time allotted to this operation may have been a portion of a longer timeout. + + + The Receive operation timed out after '{0}'. For duplex sessionful channels, the receive timeout is also the idle timeout for the channel, so consider setting a suitably large value for the ReceiveTimeout value on the Binding. The time allotted to this operation may have been a portion of a longer timeout. + + + The '{0}' operation timed out after '{1}'. The time allotted to this operation may have been a portion of a longer timeout. + + + This platform does not support server side WebSockets. + + + This platform does not support client side WebSockets natively. Support for client side WebSockets can be enabled on this platform by providing an implementation of {0}. + + + WebSockets are not supported in the classic pipeline mode. Consider using the integrated pipeline mode for the application pool. + + + The WebSocketModule is not loaded. Check if the WebSocket feature is installed and the WebSocketModule is enabled in the list of IIS modules (see http://go.microsoft.com/fwlink/?LinkId=231398 for details). + + + The name of the policy being imported for contract '{0}:{1}' is invalid:'{2}'. It should be either '{3}', '{4}' or '{5}'. + + + The server didn't accept the connection request. It is possible that the WebSocket protocol version on your client doesn't match the one on the server('{0}'). + + + The server didn't accept the connection request. It is possible that the WebSocket subprotocol sent by your client is not supported by the server. Protocol(s) supported by the server are '{0}'. + + + The server didn't accept the connection request. It is possible that the client side message encoding format doesn't match the setting on the server side. Please check your binding settings. + + + The server didn't accept the connection request. It is possible that the client side message encoding format or message transfer mode doesn't match the setting on the server side. Please check your binding settings. + + + This collection holds request headers and cannot contain the specified response header '{0}'. + + + This collection holds response headers and cannot contain the specified request header '{0}'. + + + Support for {0} and {1} can not be enabled with {2} when the {3} of the {4} is '{5}'. Ensure the {4} used with the binding has a {3} of '{6}'. + + + Enumeration has either not started or has already finished. + + + The parameter '{0}' cannot be an empty string. + + + Specified value has invalid Control characters. + + + Specified value has invalid CRLF characters. + + + Specified value has invalid HTTP Header characters. + + + Specified value has invalid non-ASCII characters. + + + Specified argument was out of the range of valid values. + + + Failed to copy the HTTP header '{0}' with value '{1}' to '{2}'. + + + The size quota for this stream ({0}) has been exceeded. + + + The IAsyncResult implementation '{0}' tried to complete a single operation multiple times. This could be caused by an incorrect application IAsyncResult implementation or other extensibility code, such as an IAsyncResult that returns incorrect CompletedSynchronously values or invokes the AsyncCallback multiple times. + + + Async Callback threw an exception. + + + A null value was returned from an async 'Begin' method or passed to an AsyncCallback. Async 'Begin' implementations must return a non-null IAsyncResult and pass the same IAsyncResult object as the parameter to the AsyncCallback. + + + An incorrect implementation of the IAsyncResult interface may be returning incorrect values from the CompletedSynchronously property or calling the AsyncCallback more than once. The type {0} could be the incorrect implementation. + + + An incorrect implementation of the IAsyncResult interface may be returning incorrect values from the CompletedSynchronously property or calling the AsyncCallback more than once. + + + An incorrect IAsyncResult was provided to an 'End' method. The IAsyncResult object passed to 'End' must be the one returned from the matching 'Begin' or passed to the callback provided to 'Begin'. + + + End cannot be called twice on an AsyncResult. + + + This buffer cannot be returned to the buffer manager because it is the wrong size. + + + The argument must be a non-empty string. + + + The ActionItem was already scheduled for execution that hasn't been completed yet. + + + A Dequeue operation timed out after {0}. The time allotted to this operation may have been a portion of a longer timeout. + + + The task timed out after {0}. The time allotted to this operation may have been a portion of a longer timeout. + + + '{0}' is an invalid XmlNodeType. + + + Object synchronization method was called from an unsynchronized block of code. + + + Argument {0} must be a non-negative timeout value. Provided value was {1}. + + + The specified method handle is incorrect for the proxy of type '{0}' + + + Failed to create a typed proxy for type '{0}' + + + This SecurityKeyIdentifierClause does not support key creation. + + + This SecurityKeyIdentifier does not have any clause that can create a key. + + + No clause of type '{0}' was found in the SecurityKeyIdentifier. + + + The security token authenticator '{0}' cannot validate a token of type '{1}'. + + + Stream returned by OperationStreamProvider cannot be null. + + + The value '{0}' cannot be parsed as the type '{1}'. + + + Argument {0} must be a positive timeout value. Provided value was {1}. + + + Cannot claim lock within the allotted timeout of {0}. The time allotted to this operation may have been a portion of a longer timeout. + + + The token requirement does not contain a property '{0}'. + + + The token requirement has an unexpected type '{1}' for property '{0}'. The expected property type is '{2}'. + + + The token provider '{0}' does not support token cancellation. + + + The token provider '{0}' was unable to provide a security token. + + + The token provider '{0}' was unable to renew the security token. + + + The token provider '{0}' does not support token renewal. + + + The X.509 certificate ({0}) usage time is invalid. The usage time '{1}' does not fall between NotBefore time '{2}' and NotAfter time '{3}'. + + + The {0} X.509 certificate is in an untrusted certificate store. + + + The X.509 certificate {0} is not in the trusted people store. + + + The X.509 certificate {0} chain building failed. The certificate that was used has a trust chain that cannot be verified. Replace the certificate or change the certificateValidationMode. {1} + + + The username cannot be empty. + + \ No newline at end of file diff --git a/src/CoreWCF.Primitives/tests/CoreWCF.Primitives.Tests.csproj b/src/CoreWCF.Primitives/tests/CoreWCF.Primitives.Tests.csproj new file mode 100644 index 000000000..529f5561b --- /dev/null +++ b/src/CoreWCF.Primitives/tests/CoreWCF.Primitives.Tests.csproj @@ -0,0 +1,22 @@ + + + netcoreapp2.2 + CoreWCF.Primitives.Tests + true + false + false + false + + + + + + all + runtime; build; native; contentfiles; analyzers + + + + + + + \ No newline at end of file diff --git a/src/CoreWCF.Primitives/tests/DispatchBuilderTests.cs b/src/CoreWCF.Primitives/tests/DispatchBuilderTests.cs new file mode 100644 index 000000000..5a1f63c3a --- /dev/null +++ b/src/CoreWCF.Primitives/tests/DispatchBuilderTests.cs @@ -0,0 +1,101 @@ +//using Microsoft.Extensions.DependencyInjection; +using Helpers; +using Microsoft.AspNetCore.Hosting.Server; +using Microsoft.AspNetCore.Hosting.Server.Features; +using Microsoft.Extensions.DependencyInjection; +using CoreWCF.Channels; +using CoreWCF.Configuration; +using CoreWCF.Primitives.Tests; +using System.Threading; +using Xunit; + +public static class DispatchBuilderTests +{ + [Fact] + public static void BuildDispatcherWithConfiguration() + { + string serviceAddress = "http://localhost/dummy"; + var services = new ServiceCollection(); + services.AddServiceModelServices(); + var serverAddressesFeature = new ServerAddressesFeature(); + serverAddressesFeature.Addresses.Add(serviceAddress); + IServer server = new MockServer(); + server.Features.Set(serverAddressesFeature); + services.AddSingleton(server); + var serviceProvider = services.BuildServiceProvider(); + var serviceBuilder = serviceProvider.GetRequiredService(); + serviceBuilder.AddService(); + var binding = new CustomBinding("BindingName", "BindingNS"); + binding.Elements.Add(new MockTransportBindingElement()); + serviceBuilder.AddServiceEndpoint(binding, serviceAddress); + var dispatcherBuilder = serviceProvider.GetRequiredService(); + var dispatchers = dispatcherBuilder.BuildDispatchers(typeof(SimpleService)); + Assert.Single(dispatchers); + var dispatcher = dispatchers[0]; + Assert.Equal("foo", dispatcher.Binding.Scheme); + Assert.Equal(serviceAddress, dispatcher.BaseAddress.ToString()); + var requestContext = TestRequestContext.Create(serviceAddress); + IChannel mockChannel = new MockReplyChannel(serviceProvider); + dispatcher.DispatchAsync(requestContext, mockChannel, CancellationToken.None).Wait(); + requestContext.ValidateReply(); + } + + [Fact] + public static void BuildDispatcherWithConfiguration_Singleton_Not_WellKnown() + { + string serviceAddress = "http://localhost/dummy"; + var services = new ServiceCollection(); + services.AddServiceModelServices(); + var serverAddressesFeature = new ServerAddressesFeature(); + serverAddressesFeature.Addresses.Add(serviceAddress); + IServer server = new MockServer(); + server.Features.Set(serverAddressesFeature); + services.AddSingleton(server); + var serviceProvider = services.BuildServiceProvider(); + var serviceBuilder = serviceProvider.GetRequiredService(); + serviceBuilder.AddService(); + var binding = new CustomBinding("BindingName", "BindingNS"); + binding.Elements.Add(new MockTransportBindingElement()); + serviceBuilder.AddServiceEndpoint(binding, serviceAddress); + var dispatcherBuilder = serviceProvider.GetRequiredService(); + var dispatchers = dispatcherBuilder.BuildDispatchers(typeof(SimpleSingletonService)); + Assert.Single(dispatchers); + var dispatcher = dispatchers[0]; + Assert.Equal("foo", dispatcher.Binding.Scheme); + Assert.Equal(serviceAddress, dispatcher.BaseAddress.ToString()); + var requestContext = TestRequestContext.Create(serviceAddress); + IChannel mockChannel = new MockReplyChannel(serviceProvider); + dispatcher.DispatchAsync(requestContext, mockChannel, CancellationToken.None).Wait(); + requestContext.ValidateReply(); + } + + [Fact] + public static void BuildDispatcherWithConfiguration_Singleton_WellKnown() + { + string serviceAddress = "http://localhost/dummy"; + var services = new ServiceCollection(); + services.AddServiceModelServices(); + var serverAddressesFeature = new ServerAddressesFeature(); + serverAddressesFeature.Addresses.Add(serviceAddress); + IServer server = new MockServer(); + server.Features.Set(serverAddressesFeature); + services.AddSingleton(server); + services.AddSingleton(new SimpleSingletonService()); + var serviceProvider = services.BuildServiceProvider(); + var serviceBuilder = serviceProvider.GetRequiredService(); + serviceBuilder.AddService(); + var binding = new CustomBinding("BindingName", "BindingNS"); + binding.Elements.Add(new MockTransportBindingElement()); + serviceBuilder.AddServiceEndpoint(binding, serviceAddress); + var dispatcherBuilder = serviceProvider.GetRequiredService(); + var dispatchers = dispatcherBuilder.BuildDispatchers(typeof(SimpleSingletonService)); + Assert.Single(dispatchers); + var dispatcher = dispatchers[0]; + Assert.Equal("foo", dispatcher.Binding.Scheme); + Assert.Equal(serviceAddress, dispatcher.BaseAddress.ToString()); + var requestContext = TestRequestContext.Create(serviceAddress); + IChannel mockChannel = new MockReplyChannel(serviceProvider); + dispatcher.DispatchAsync(requestContext, mockChannel, CancellationToken.None).Wait(); + requestContext.ValidateReply(); + } +} diff --git a/src/CoreWCF.Primitives/tests/Helpers/MockReplyChannel.cs b/src/CoreWCF.Primitives/tests/Helpers/MockReplyChannel.cs new file mode 100644 index 000000000..f5ea1e46d --- /dev/null +++ b/src/CoreWCF.Primitives/tests/Helpers/MockReplyChannel.cs @@ -0,0 +1,80 @@ +using Microsoft.Extensions.DependencyInjection; +using CoreWCF; +using CoreWCF.Channels; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Helpers +{ + internal class MockReplyChannel : IReplyChannel + { + private IServiceScope _serviceScope; + + public MockReplyChannel(IServiceProvider serviceProvider) + { + var servicesScopeFactory = serviceProvider.GetRequiredService(); + _serviceScope = servicesScopeFactory.CreateScope(); + } + + public EndpointAddress LocalAddress => throw new NotImplementedException(); + + public CommunicationState State => CommunicationState.Opened; + + public event EventHandler Closed; + public event EventHandler Closing; + public event EventHandler Faulted; + public event EventHandler Opened; + public event EventHandler Opening; + + public void Abort() + { + throw new NotImplementedException(); + } + + public Task CloseAsync() + { + throw new NotImplementedException(); + } + + public Task CloseAsync(CancellationToken token) + { + throw new NotImplementedException(); + } + + public T GetProperty() where T : class + { + return _serviceScope.ServiceProvider.GetService(); + } + + public Task OpenAsync() + { + throw new NotImplementedException(); + } + + public Task OpenAsync(CancellationToken token) + { + throw new NotImplementedException(); + } + + public Task ReceiveRequestAsync() + { + throw new NotImplementedException(); + } + + public Task ReceiveRequestAsync(CancellationToken token) + { + throw new NotImplementedException(); + } + + public Task> TryReceiveRequestAsync(CancellationToken token) + { + throw new NotImplementedException(); + } + + public Task WaitForRequestAsync(CancellationToken token) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/CoreWCF.Primitives/tests/Helpers/MockServer.cs b/src/CoreWCF.Primitives/tests/Helpers/MockServer.cs new file mode 100644 index 000000000..576670037 --- /dev/null +++ b/src/CoreWCF.Primitives/tests/Helpers/MockServer.cs @@ -0,0 +1,29 @@ +using Microsoft.AspNetCore.Hosting.Server; +using Microsoft.AspNetCore.Http.Features; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Helpers +{ + + internal class MockServer : IServer + { + public IFeatureCollection Features { get; } = new FeatureCollection(); + + public void Dispose() + { + throw new NotImplementedException(); + } + + public Task StartAsync(IHttpApplication application, CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } + + public Task StopAsync(CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/CoreWCF.Primitives/tests/Helpers/MockTransportBindingElement.cs b/src/CoreWCF.Primitives/tests/Helpers/MockTransportBindingElement.cs new file mode 100644 index 000000000..571e2503c --- /dev/null +++ b/src/CoreWCF.Primitives/tests/Helpers/MockTransportBindingElement.cs @@ -0,0 +1,14 @@ +using CoreWCF.Channels; + +namespace Helpers +{ + internal class MockTransportBindingElement : TransportBindingElement + { + public override string Scheme => "foo"; + + public override BindingElement Clone() + { + return this; + } + } +} diff --git a/src/CoreWCF.Primitives/tests/Helpers/TestRequestContext.cs b/src/CoreWCF.Primitives/tests/Helpers/TestRequestContext.cs new file mode 100644 index 000000000..d9e4312a0 --- /dev/null +++ b/src/CoreWCF.Primitives/tests/Helpers/TestRequestContext.cs @@ -0,0 +1,91 @@ +using CoreWCF.Channels; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Xunit; + +namespace Helpers +{ + class TestRequestContext : RequestContext + { + private Message _requestMessage; + private string _replyMessageString; + + public TestRequestContext(Message requestMessage) + { + _requestMessage = requestMessage; + } + + public override Message RequestMessage => _requestMessage; + + public Message ReplyMessage { get; private set; } + + public override void Abort() + { + } + + public override Task CloseAsync() + { + return Task.CompletedTask; + } + + public override Task CloseAsync(CancellationToken token) + { + return Task.CompletedTask; + } + + public override Task ReplyAsync(Message message) + { + return ReplyAsync(message, CancellationToken.None); + } + + public override Task ReplyAsync(Message message, CancellationToken token) + { + ReplyMessage = message; + SerializeReply(); + return Task.CompletedTask; + } + + internal void SerializeReply() + { + MessageEncodingBindingElement mebe = new TextMessageEncodingBindingElement(MessageVersion.Soap11, Encoding.UTF8); + var mef = mebe.CreateMessageEncoderFactory(); + var me = mef.Encoder; + MemoryStream ms = new MemoryStream(); + me.WriteMessage(ReplyMessage, ms); + var messageBytes = ms.ToArray(); + _replyMessageString = Encoding.UTF8.GetString(messageBytes); + } + + internal void ValidateReply() + { + Assert.Equal(s_replyMessage, _replyMessageString); + } + + internal static TestRequestContext Create(string toAddress) + { + MessageEncodingBindingElement mebe = new TextMessageEncodingBindingElement(MessageVersion.Soap11, Encoding.UTF8); + var mef = mebe.CreateMessageEncoderFactory(); + var me = mef.Encoder; + var requestMessageBytes = Encoding.UTF8.GetBytes(s_requestMessage); + var requestMessage = me.ReadMessage(new ArraySegment(requestMessageBytes), BufferManager.CreateBufferManager(1, 1)); + requestMessage.Headers.To = new Uri(toAddress); + requestMessage.Headers.Action = "http://tempuri.org/ISimpleService/Echo"; + return new TestRequestContext(requestMessage); + } + + private static string s_requestMessage = @" + + + + aaaaa + + +"; + + private static string s_replyMessage = @"http://tempuri.org/ISimpleService/EchoResponseaaaaa"; + } +} diff --git a/src/CoreWCF.Primitives/tests/ISimpleService.cs b/src/CoreWCF.Primitives/tests/ISimpleService.cs new file mode 100644 index 000000000..fa1526b62 --- /dev/null +++ b/src/CoreWCF.Primitives/tests/ISimpleService.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoreWCF.Primitives.Tests +{ + [ServiceContract] + interface ISimpleService + { + [OperationContract] + string Echo(string echo); + } +} diff --git a/src/CoreWCF.Primitives/tests/Properties/launchSettings.json b/src/CoreWCF.Primitives/tests/Properties/launchSettings.json new file mode 100644 index 000000000..39fa82829 --- /dev/null +++ b/src/CoreWCF.Primitives/tests/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:32585/", + "sslPort": 0 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "Microsoft.ServiceModel.Primitives.Tests": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "http://localhost:32586/" + } + } +} \ No newline at end of file diff --git a/src/CoreWCF.Primitives/tests/SimpleService.cs b/src/CoreWCF.Primitives/tests/SimpleService.cs new file mode 100644 index 000000000..8ff95eb3a --- /dev/null +++ b/src/CoreWCF.Primitives/tests/SimpleService.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace CoreWCF.Primitives.Tests +{ + class SimpleService : ISimpleService + { + public string Echo(string echo) + { + return echo; + } + } + + [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)] + class SimpleSingletonService : ISimpleService + { + public string Echo(string echo) + { + return echo; + } + } +} diff --git a/src/NuGet.config b/src/NuGet.config new file mode 100644 index 000000000..eb4ae61d5 --- /dev/null +++ b/src/NuGet.config @@ -0,0 +1,13 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Samples/DesktopClient/App.config b/src/Samples/DesktopClient/App.config new file mode 100644 index 000000000..56efbc7b5 --- /dev/null +++ b/src/Samples/DesktopClient/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/Samples/DesktopClient/DesktopClient.csproj b/src/Samples/DesktopClient/DesktopClient.csproj new file mode 100644 index 000000000..951bf41d0 --- /dev/null +++ b/src/Samples/DesktopClient/DesktopClient.csproj @@ -0,0 +1,55 @@ + + + + + Debug + AnyCPU + {AC474D52-EC86-43CC-AE48-579D9465DE2D} + Exe + DesktopClient + DesktopClient + v4.7.2 + 512 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Samples/DesktopClient/IEchoService.cs b/src/Samples/DesktopClient/IEchoService.cs new file mode 100644 index 000000000..c713a91b6 --- /dev/null +++ b/src/Samples/DesktopClient/IEchoService.cs @@ -0,0 +1,11 @@ +using System.ServiceModel; + +namespace Contract +{ + [ServiceContract] + public interface IEchoService + { + [OperationContract] + string Echo(string text); + } +} diff --git a/src/Samples/DesktopClient/Program.cs b/src/Samples/DesktopClient/Program.cs new file mode 100644 index 000000000..d4e255a91 --- /dev/null +++ b/src/Samples/DesktopClient/Program.cs @@ -0,0 +1,30 @@ +using System; +using System.ServiceModel; + +namespace DesktopClient +{ + class Program + { + static void Main(string[] args) + { + var factory = new ChannelFactory(new NetTcpBinding(), new EndpointAddress("net.tcp://localhost:8808/nettcp")); + factory.Open(); + var channel = factory.CreateChannel(); + ((IClientChannel)channel).Open(); + Console.WriteLine("net.tcp Echo(\"Hello\") => " + channel.Echo("Hello")); + ((IClientChannel)channel).Close(); + factory.Close(); + + factory = new ChannelFactory(new BasicHttpBinding(), new EndpointAddress("http://localhost:8080/basichttp")); + factory.Open(); + channel = factory.CreateChannel(); + ((IClientChannel)channel).Open(); + Console.WriteLine("http Echo(\"Hello\") => " + channel.Echo("Hello")); + ((IClientChannel)channel).Close(); + factory.Close(); + + Console.WriteLine("Hit enter to exit"); + Console.ReadLine(); + } + } +} diff --git a/src/Samples/DesktopClient/Properties/AssemblyInfo.cs b/src/Samples/DesktopClient/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..a968d74fd --- /dev/null +++ b/src/Samples/DesktopClient/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("DesktopClient")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("DesktopClient")] +[assembly: AssemblyCopyright("Copyright © 2019")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("ac474d52-ec86-43cc-ae48-579d9465de2d")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/Samples/DesktopServer/App.config b/src/Samples/DesktopServer/App.config new file mode 100644 index 000000000..56efbc7b5 --- /dev/null +++ b/src/Samples/DesktopServer/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/Samples/DesktopServer/DesktopServer.csproj b/src/Samples/DesktopServer/DesktopServer.csproj new file mode 100644 index 000000000..ce71d02ee --- /dev/null +++ b/src/Samples/DesktopServer/DesktopServer.csproj @@ -0,0 +1,56 @@ + + + + + Debug + AnyCPU + {53D7DDAB-757E-42DF-A064-62E97C7AD813} + Exe + DesktopServer + DesktopServer + v4.7.2 + 512 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Samples/DesktopServer/EchoService.cs b/src/Samples/DesktopServer/EchoService.cs new file mode 100644 index 000000000..f98969c75 --- /dev/null +++ b/src/Samples/DesktopServer/EchoService.cs @@ -0,0 +1,11 @@ +namespace DesktopServer +{ + public class EchoService : Contract.IEchoService + { + public string Echo(string text) + { + System.Console.WriteLine($"Received {text} from client!"); + return text; + } + } +} diff --git a/src/Samples/DesktopServer/IEchoService.cs b/src/Samples/DesktopServer/IEchoService.cs new file mode 100644 index 000000000..c713a91b6 --- /dev/null +++ b/src/Samples/DesktopServer/IEchoService.cs @@ -0,0 +1,11 @@ +using System.ServiceModel; + +namespace Contract +{ + [ServiceContract] + public interface IEchoService + { + [OperationContract] + string Echo(string text); + } +} diff --git a/src/Samples/DesktopServer/Program.cs b/src/Samples/DesktopServer/Program.cs new file mode 100644 index 000000000..a378321b9 --- /dev/null +++ b/src/Samples/DesktopServer/Program.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.ServiceModel; +using System.Text; +using System.Threading.Tasks; + +namespace DesktopServer +{ + class Program + { + static void Main(string[] args) + { + var host = new ServiceHost(typeof(EchoService), + new Uri("net.tcp://localhost:8808/"), + new Uri("http://localhost:8080/")); + host.AddServiceEndpoint(typeof(Contract.IEchoService), new NetTcpBinding(), "/nettcp"); + host.AddServiceEndpoint(typeof(Contract.IEchoService), new BasicHttpBinding(), "/basichttp"); + host.Open(); + foreach(var endpoint in host.Description.Endpoints) + { + Console.WriteLine("Listening on " + endpoint.ListenUri.ToString()); + } + Console.WriteLine("Hit enter to close"); + Console.ReadLine(); + host.Close(); + } + } +} diff --git a/src/Samples/DesktopServer/Properties/AssemblyInfo.cs b/src/Samples/DesktopServer/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..6d9ab1db7 --- /dev/null +++ b/src/Samples/DesktopServer/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("DesktopServer")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("DesktopServer")] +[assembly: AssemblyCopyright("Copyright © 2019")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("53d7ddab-757e-42df-a064-62e97c7ad813")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/Samples/NetCoreClient/IEchoService.cs b/src/Samples/NetCoreClient/IEchoService.cs new file mode 100644 index 000000000..c713a91b6 --- /dev/null +++ b/src/Samples/NetCoreClient/IEchoService.cs @@ -0,0 +1,11 @@ +using System.ServiceModel; + +namespace Contract +{ + [ServiceContract] + public interface IEchoService + { + [OperationContract] + string Echo(string text); + } +} diff --git a/src/Samples/NetCoreClient/NetCoreClient.csproj b/src/Samples/NetCoreClient/NetCoreClient.csproj new file mode 100644 index 000000000..4cfbcb879 --- /dev/null +++ b/src/Samples/NetCoreClient/NetCoreClient.csproj @@ -0,0 +1,14 @@ + + + + Exe + netcoreapp2.2 + + + + + + + + + diff --git a/src/Samples/NetCoreClient/Program.cs b/src/Samples/NetCoreClient/Program.cs new file mode 100644 index 000000000..48cd84b25 --- /dev/null +++ b/src/Samples/NetCoreClient/Program.cs @@ -0,0 +1,30 @@ +using System; +using System.ServiceModel; + +namespace NetCoreClient +{ + class Program + { + static void Main(string[] args) + { + var factory = new ChannelFactory(new NetTcpBinding(), new EndpointAddress("net.tcp://localhost:8808/nettcp")); + factory.Open(); + var channel = factory.CreateChannel(); + ((IClientChannel)channel).Open(); + Console.WriteLine("net.tcp Echo(\"Hello\") => " + channel.Echo("Hello")); + ((IClientChannel)channel).Close(); + factory.Close(); + + factory = new ChannelFactory(new BasicHttpBinding(), new EndpointAddress("http://localhost:8080/basichttp")); + factory.Open(); + channel = factory.CreateChannel(); + ((IClientChannel)channel).Open(); + Console.WriteLine("http Echo(\"Hello\") => " + channel.Echo("Hello")); + ((IClientChannel)channel).Close(); + factory.Close(); + + Console.WriteLine("Hit enter to exit"); + Console.ReadLine(); + } + } +} diff --git a/src/Samples/NetCoreServer/EchoService.cs b/src/Samples/NetCoreServer/EchoService.cs new file mode 100644 index 000000000..2d5a0d018 --- /dev/null +++ b/src/Samples/NetCoreServer/EchoService.cs @@ -0,0 +1,11 @@ +namespace NetCoreServer +{ + public class EchoService : Contract.IEchoService + { + public string Echo(string text) + { + System.Console.WriteLine($"Received {text} from client!"); + return text; + } + } +} diff --git a/src/Samples/NetCoreServer/IEchoService.cs b/src/Samples/NetCoreServer/IEchoService.cs new file mode 100644 index 000000000..7ee329b51 --- /dev/null +++ b/src/Samples/NetCoreServer/IEchoService.cs @@ -0,0 +1,11 @@ +using CoreWCF; + +namespace Contract +{ + [ServiceContract] + public interface IEchoService + { + [OperationContract] + string Echo(string text); + } +} diff --git a/src/Samples/NetCoreServer/NetCoreServer.csproj b/src/Samples/NetCoreServer/NetCoreServer.csproj new file mode 100644 index 000000000..9343ee4ba --- /dev/null +++ b/src/Samples/NetCoreServer/NetCoreServer.csproj @@ -0,0 +1,18 @@ + + + + Exe + netcoreapp2.2 + + + + + + + + + + + + + diff --git a/src/Samples/NetCoreServer/Program.cs b/src/Samples/NetCoreServer/Program.cs new file mode 100644 index 000000000..bb66494a8 --- /dev/null +++ b/src/Samples/NetCoreServer/Program.cs @@ -0,0 +1,23 @@ +using CoreWCF.Configuration; +using Microsoft.AspNetCore; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; + +namespace NetCoreServer +{ + class Program + { + static void Main(string[] args) + { + var host = CreateWebHostBuilder(args).Build(); + host.Run(); + } + + public static IWebHostBuilder CreateWebHostBuilder(string[] args) => + WebHost.CreateDefaultBuilder(args) + .UseKestrel(options => { options.ListenLocalhost(8080); }) + .UseUrls("http://localhost:8080") + .UseNetTcp(8808) + .UseStartup(); + } +} diff --git a/src/Samples/NetCoreServer/Properties/launchSettings.json b/src/Samples/NetCoreServer/Properties/launchSettings.json new file mode 100644 index 000000000..2b471d7be --- /dev/null +++ b/src/Samples/NetCoreServer/Properties/launchSettings.json @@ -0,0 +1,24 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:1438/", + "sslPort": 0 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "NetCoreServer": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "http://localhost:1439/" + } + } +} \ No newline at end of file diff --git a/src/Samples/NetCoreServer/Startup.cs b/src/Samples/NetCoreServer/Startup.cs new file mode 100644 index 000000000..45953989d --- /dev/null +++ b/src/Samples/NetCoreServer/Startup.cs @@ -0,0 +1,26 @@ +using CoreWCF; +using CoreWCF.Configuration; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; + +namespace NetCoreServer +{ + public class Startup + { + public void ConfigureServices(IServiceCollection services) + { + services.AddServiceModelServices(); + } + + public void Configure(IApplicationBuilder app, IHostingEnvironment env) + { + app.UseServiceModel(builder => + { + builder.AddService(); + builder.AddServiceEndpoint(new BasicHttpBinding(), "/basichttp"); + builder.AddServiceEndpoint(new NetTcpBinding(), "/nettcp"); + }); + } + } +} diff --git a/src/WcfCore.sln b/src/WcfCore.sln new file mode 100644 index 000000000..8d15b33db --- /dev/null +++ b/src/WcfCore.sln @@ -0,0 +1,180 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.28803.452 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{0B5D4C83-181F-44AD-8F24-95599BFD66B8}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{73CF9B6D-0B40-422E-8F55-9D0C134E9E37}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CoreWCF.Primitives", "CoreWCF.Primitives\src\CoreWCF.Primitives.csproj", "{A2BBD98A-0862-414F-93A4-BAE0AE9AD654}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CoreWCF.Http", "CoreWCF.Http\src\CoreWCF.Http.csproj", "{5261B584-C338-4573-B4A0-F76C56D89F6C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CoreWCF.NetTcp", "CoreWCF.NetTcp\src\CoreWCF.NetTcp.csproj", "{58568DAD-EF5D-4CA9-9812-2FAE30B849DB}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CoreWCF.Http.Tests", "CoreWCF.Http\tests\CoreWCF.Http.Tests.csproj", "{7C01D118-BD40-4B0D-8728-6D61ED623081}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CoreWCF.NetTcp.Tests", "CoreWCF.NetTcp\tests\CoreWCF.NetTcp.Tests.csproj", "{5C693BE3-CD4A-40C9-8105-93681BF5B5A6}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CoreWCF.Primitives.Tests", "CoreWCF.Primitives\tests\CoreWCF.Primitives.Tests.csproj", "{0BAAF301-23F3-49AC-B167-B8A88565F95B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{083F2ED2-5D26-4EEB-A410-29ADCC0C4B42}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetCoreServer", "Samples\NetCoreServer\NetCoreServer.csproj", "{3FB095F3-767E-470D-918D-A8BE546C8217}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetCoreClient", "Samples\NetCoreClient\NetCoreClient.csproj", "{F270C4F1-6F1D-44B1-B29F-97C8D810C209}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DesktopClient", "Samples\DesktopClient\DesktopClient.csproj", "{AC474D52-EC86-43CC-AE48-579D9465DE2D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DesktopServer", "Samples\DesktopServer\DesktopServer.csproj", "{53D7DDAB-757E-42DF-A064-62E97C7AD813}" +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 + {A2BBD98A-0862-414F-93A4-BAE0AE9AD654}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A2BBD98A-0862-414F-93A4-BAE0AE9AD654}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A2BBD98A-0862-414F-93A4-BAE0AE9AD654}.Debug|x64.ActiveCfg = Debug|Any CPU + {A2BBD98A-0862-414F-93A4-BAE0AE9AD654}.Debug|x64.Build.0 = Debug|Any CPU + {A2BBD98A-0862-414F-93A4-BAE0AE9AD654}.Debug|x86.ActiveCfg = Debug|Any CPU + {A2BBD98A-0862-414F-93A4-BAE0AE9AD654}.Debug|x86.Build.0 = Debug|Any CPU + {A2BBD98A-0862-414F-93A4-BAE0AE9AD654}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A2BBD98A-0862-414F-93A4-BAE0AE9AD654}.Release|Any CPU.Build.0 = Release|Any CPU + {A2BBD98A-0862-414F-93A4-BAE0AE9AD654}.Release|x64.ActiveCfg = Release|Any CPU + {A2BBD98A-0862-414F-93A4-BAE0AE9AD654}.Release|x64.Build.0 = Release|Any CPU + {A2BBD98A-0862-414F-93A4-BAE0AE9AD654}.Release|x86.ActiveCfg = Release|Any CPU + {A2BBD98A-0862-414F-93A4-BAE0AE9AD654}.Release|x86.Build.0 = Release|Any CPU + {5261B584-C338-4573-B4A0-F76C56D89F6C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5261B584-C338-4573-B4A0-F76C56D89F6C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5261B584-C338-4573-B4A0-F76C56D89F6C}.Debug|x64.ActiveCfg = Debug|Any CPU + {5261B584-C338-4573-B4A0-F76C56D89F6C}.Debug|x64.Build.0 = Debug|Any CPU + {5261B584-C338-4573-B4A0-F76C56D89F6C}.Debug|x86.ActiveCfg = Debug|Any CPU + {5261B584-C338-4573-B4A0-F76C56D89F6C}.Debug|x86.Build.0 = Debug|Any CPU + {5261B584-C338-4573-B4A0-F76C56D89F6C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5261B584-C338-4573-B4A0-F76C56D89F6C}.Release|Any CPU.Build.0 = Release|Any CPU + {5261B584-C338-4573-B4A0-F76C56D89F6C}.Release|x64.ActiveCfg = Release|Any CPU + {5261B584-C338-4573-B4A0-F76C56D89F6C}.Release|x64.Build.0 = Release|Any CPU + {5261B584-C338-4573-B4A0-F76C56D89F6C}.Release|x86.ActiveCfg = Release|Any CPU + {5261B584-C338-4573-B4A0-F76C56D89F6C}.Release|x86.Build.0 = Release|Any CPU + {58568DAD-EF5D-4CA9-9812-2FAE30B849DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {58568DAD-EF5D-4CA9-9812-2FAE30B849DB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {58568DAD-EF5D-4CA9-9812-2FAE30B849DB}.Debug|x64.ActiveCfg = Debug|Any CPU + {58568DAD-EF5D-4CA9-9812-2FAE30B849DB}.Debug|x64.Build.0 = Debug|Any CPU + {58568DAD-EF5D-4CA9-9812-2FAE30B849DB}.Debug|x86.ActiveCfg = Debug|Any CPU + {58568DAD-EF5D-4CA9-9812-2FAE30B849DB}.Debug|x86.Build.0 = Debug|Any CPU + {58568DAD-EF5D-4CA9-9812-2FAE30B849DB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {58568DAD-EF5D-4CA9-9812-2FAE30B849DB}.Release|Any CPU.Build.0 = Release|Any CPU + {58568DAD-EF5D-4CA9-9812-2FAE30B849DB}.Release|x64.ActiveCfg = Release|Any CPU + {58568DAD-EF5D-4CA9-9812-2FAE30B849DB}.Release|x64.Build.0 = Release|Any CPU + {58568DAD-EF5D-4CA9-9812-2FAE30B849DB}.Release|x86.ActiveCfg = Release|Any CPU + {58568DAD-EF5D-4CA9-9812-2FAE30B849DB}.Release|x86.Build.0 = Release|Any CPU + {7C01D118-BD40-4B0D-8728-6D61ED623081}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7C01D118-BD40-4B0D-8728-6D61ED623081}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7C01D118-BD40-4B0D-8728-6D61ED623081}.Debug|x64.ActiveCfg = Debug|Any CPU + {7C01D118-BD40-4B0D-8728-6D61ED623081}.Debug|x64.Build.0 = Debug|Any CPU + {7C01D118-BD40-4B0D-8728-6D61ED623081}.Debug|x86.ActiveCfg = Debug|Any CPU + {7C01D118-BD40-4B0D-8728-6D61ED623081}.Debug|x86.Build.0 = Debug|Any CPU + {7C01D118-BD40-4B0D-8728-6D61ED623081}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7C01D118-BD40-4B0D-8728-6D61ED623081}.Release|Any CPU.Build.0 = Release|Any CPU + {7C01D118-BD40-4B0D-8728-6D61ED623081}.Release|x64.ActiveCfg = Release|Any CPU + {7C01D118-BD40-4B0D-8728-6D61ED623081}.Release|x64.Build.0 = Release|Any CPU + {7C01D118-BD40-4B0D-8728-6D61ED623081}.Release|x86.ActiveCfg = Release|Any CPU + {7C01D118-BD40-4B0D-8728-6D61ED623081}.Release|x86.Build.0 = Release|Any CPU + {5C693BE3-CD4A-40C9-8105-93681BF5B5A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5C693BE3-CD4A-40C9-8105-93681BF5B5A6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5C693BE3-CD4A-40C9-8105-93681BF5B5A6}.Debug|x64.ActiveCfg = Debug|Any CPU + {5C693BE3-CD4A-40C9-8105-93681BF5B5A6}.Debug|x64.Build.0 = Debug|Any CPU + {5C693BE3-CD4A-40C9-8105-93681BF5B5A6}.Debug|x86.ActiveCfg = Debug|Any CPU + {5C693BE3-CD4A-40C9-8105-93681BF5B5A6}.Debug|x86.Build.0 = Debug|Any CPU + {5C693BE3-CD4A-40C9-8105-93681BF5B5A6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5C693BE3-CD4A-40C9-8105-93681BF5B5A6}.Release|Any CPU.Build.0 = Release|Any CPU + {5C693BE3-CD4A-40C9-8105-93681BF5B5A6}.Release|x64.ActiveCfg = Release|Any CPU + {5C693BE3-CD4A-40C9-8105-93681BF5B5A6}.Release|x64.Build.0 = Release|Any CPU + {5C693BE3-CD4A-40C9-8105-93681BF5B5A6}.Release|x86.ActiveCfg = Release|Any CPU + {5C693BE3-CD4A-40C9-8105-93681BF5B5A6}.Release|x86.Build.0 = Release|Any CPU + {0BAAF301-23F3-49AC-B167-B8A88565F95B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0BAAF301-23F3-49AC-B167-B8A88565F95B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0BAAF301-23F3-49AC-B167-B8A88565F95B}.Debug|x64.ActiveCfg = Debug|Any CPU + {0BAAF301-23F3-49AC-B167-B8A88565F95B}.Debug|x64.Build.0 = Debug|Any CPU + {0BAAF301-23F3-49AC-B167-B8A88565F95B}.Debug|x86.ActiveCfg = Debug|Any CPU + {0BAAF301-23F3-49AC-B167-B8A88565F95B}.Debug|x86.Build.0 = Debug|Any CPU + {0BAAF301-23F3-49AC-B167-B8A88565F95B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0BAAF301-23F3-49AC-B167-B8A88565F95B}.Release|Any CPU.Build.0 = Release|Any CPU + {0BAAF301-23F3-49AC-B167-B8A88565F95B}.Release|x64.ActiveCfg = Release|Any CPU + {0BAAF301-23F3-49AC-B167-B8A88565F95B}.Release|x64.Build.0 = Release|Any CPU + {0BAAF301-23F3-49AC-B167-B8A88565F95B}.Release|x86.ActiveCfg = Release|Any CPU + {0BAAF301-23F3-49AC-B167-B8A88565F95B}.Release|x86.Build.0 = Release|Any CPU + {3FB095F3-767E-470D-918D-A8BE546C8217}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3FB095F3-767E-470D-918D-A8BE546C8217}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3FB095F3-767E-470D-918D-A8BE546C8217}.Debug|x64.ActiveCfg = Debug|Any CPU + {3FB095F3-767E-470D-918D-A8BE546C8217}.Debug|x64.Build.0 = Debug|Any CPU + {3FB095F3-767E-470D-918D-A8BE546C8217}.Debug|x86.ActiveCfg = Debug|Any CPU + {3FB095F3-767E-470D-918D-A8BE546C8217}.Debug|x86.Build.0 = Debug|Any CPU + {3FB095F3-767E-470D-918D-A8BE546C8217}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3FB095F3-767E-470D-918D-A8BE546C8217}.Release|Any CPU.Build.0 = Release|Any CPU + {3FB095F3-767E-470D-918D-A8BE546C8217}.Release|x64.ActiveCfg = Release|Any CPU + {3FB095F3-767E-470D-918D-A8BE546C8217}.Release|x64.Build.0 = Release|Any CPU + {3FB095F3-767E-470D-918D-A8BE546C8217}.Release|x86.ActiveCfg = Release|Any CPU + {3FB095F3-767E-470D-918D-A8BE546C8217}.Release|x86.Build.0 = Release|Any CPU + {F270C4F1-6F1D-44B1-B29F-97C8D810C209}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F270C4F1-6F1D-44B1-B29F-97C8D810C209}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F270C4F1-6F1D-44B1-B29F-97C8D810C209}.Debug|x64.ActiveCfg = Debug|Any CPU + {F270C4F1-6F1D-44B1-B29F-97C8D810C209}.Debug|x64.Build.0 = Debug|Any CPU + {F270C4F1-6F1D-44B1-B29F-97C8D810C209}.Debug|x86.ActiveCfg = Debug|Any CPU + {F270C4F1-6F1D-44B1-B29F-97C8D810C209}.Debug|x86.Build.0 = Debug|Any CPU + {F270C4F1-6F1D-44B1-B29F-97C8D810C209}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F270C4F1-6F1D-44B1-B29F-97C8D810C209}.Release|Any CPU.Build.0 = Release|Any CPU + {F270C4F1-6F1D-44B1-B29F-97C8D810C209}.Release|x64.ActiveCfg = Release|Any CPU + {F270C4F1-6F1D-44B1-B29F-97C8D810C209}.Release|x64.Build.0 = Release|Any CPU + {F270C4F1-6F1D-44B1-B29F-97C8D810C209}.Release|x86.ActiveCfg = Release|Any CPU + {F270C4F1-6F1D-44B1-B29F-97C8D810C209}.Release|x86.Build.0 = Release|Any CPU + {AC474D52-EC86-43CC-AE48-579D9465DE2D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AC474D52-EC86-43CC-AE48-579D9465DE2D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AC474D52-EC86-43CC-AE48-579D9465DE2D}.Debug|x64.ActiveCfg = Debug|Any CPU + {AC474D52-EC86-43CC-AE48-579D9465DE2D}.Debug|x64.Build.0 = Debug|Any CPU + {AC474D52-EC86-43CC-AE48-579D9465DE2D}.Debug|x86.ActiveCfg = Debug|Any CPU + {AC474D52-EC86-43CC-AE48-579D9465DE2D}.Debug|x86.Build.0 = Debug|Any CPU + {AC474D52-EC86-43CC-AE48-579D9465DE2D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AC474D52-EC86-43CC-AE48-579D9465DE2D}.Release|Any CPU.Build.0 = Release|Any CPU + {AC474D52-EC86-43CC-AE48-579D9465DE2D}.Release|x64.ActiveCfg = Release|Any CPU + {AC474D52-EC86-43CC-AE48-579D9465DE2D}.Release|x64.Build.0 = Release|Any CPU + {AC474D52-EC86-43CC-AE48-579D9465DE2D}.Release|x86.ActiveCfg = Release|Any CPU + {AC474D52-EC86-43CC-AE48-579D9465DE2D}.Release|x86.Build.0 = Release|Any CPU + {53D7DDAB-757E-42DF-A064-62E97C7AD813}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {53D7DDAB-757E-42DF-A064-62E97C7AD813}.Debug|Any CPU.Build.0 = Debug|Any CPU + {53D7DDAB-757E-42DF-A064-62E97C7AD813}.Debug|x64.ActiveCfg = Debug|Any CPU + {53D7DDAB-757E-42DF-A064-62E97C7AD813}.Debug|x64.Build.0 = Debug|Any CPU + {53D7DDAB-757E-42DF-A064-62E97C7AD813}.Debug|x86.ActiveCfg = Debug|Any CPU + {53D7DDAB-757E-42DF-A064-62E97C7AD813}.Debug|x86.Build.0 = Debug|Any CPU + {53D7DDAB-757E-42DF-A064-62E97C7AD813}.Release|Any CPU.ActiveCfg = Release|Any CPU + {53D7DDAB-757E-42DF-A064-62E97C7AD813}.Release|Any CPU.Build.0 = Release|Any CPU + {53D7DDAB-757E-42DF-A064-62E97C7AD813}.Release|x64.ActiveCfg = Release|Any CPU + {53D7DDAB-757E-42DF-A064-62E97C7AD813}.Release|x64.Build.0 = Release|Any CPU + {53D7DDAB-757E-42DF-A064-62E97C7AD813}.Release|x86.ActiveCfg = Release|Any CPU + {53D7DDAB-757E-42DF-A064-62E97C7AD813}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {A2BBD98A-0862-414F-93A4-BAE0AE9AD654} = {0B5D4C83-181F-44AD-8F24-95599BFD66B8} + {5261B584-C338-4573-B4A0-F76C56D89F6C} = {0B5D4C83-181F-44AD-8F24-95599BFD66B8} + {58568DAD-EF5D-4CA9-9812-2FAE30B849DB} = {0B5D4C83-181F-44AD-8F24-95599BFD66B8} + {7C01D118-BD40-4B0D-8728-6D61ED623081} = {73CF9B6D-0B40-422E-8F55-9D0C134E9E37} + {5C693BE3-CD4A-40C9-8105-93681BF5B5A6} = {73CF9B6D-0B40-422E-8F55-9D0C134E9E37} + {0BAAF301-23F3-49AC-B167-B8A88565F95B} = {73CF9B6D-0B40-422E-8F55-9D0C134E9E37} + {3FB095F3-767E-470D-918D-A8BE546C8217} = {083F2ED2-5D26-4EEB-A410-29ADCC0C4B42} + {F270C4F1-6F1D-44B1-B29F-97C8D810C209} = {083F2ED2-5D26-4EEB-A410-29ADCC0C4B42} + {AC474D52-EC86-43CC-AE48-579D9465DE2D} = {083F2ED2-5D26-4EEB-A410-29ADCC0C4B42} + {53D7DDAB-757E-42DF-A064-62E97C7AD813} = {083F2ED2-5D26-4EEB-A410-29ADCC0C4B42} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {2C86AC5B-B946-48AD-A3A5-0E1A29EDA4CF} + EndGlobalSection +EndGlobal