1 //------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 //------------------------------------------------------------
4 namespace System.ServiceModel.Channels
6 using System.Collections.Generic;
7 using System.Collections.Specialized;
8 using System.Diagnostics;
9 using System.Globalization;
12 using System.Net.Http;
13 using System.Net.Http.Headers;
14 using System.Net.Mime;
15 using System.Net.Security;
16 using System.Net.Sockets;
17 using System.Reflection;
19 using System.Runtime.CompilerServices;
20 using System.Runtime.Diagnostics;
21 using System.Security.Authentication.ExtendedProtection;
22 using System.Security.Principal;
23 using System.ServiceModel;
24 using System.ServiceModel.Activation;
25 using System.ServiceModel.Diagnostics;
26 using System.ServiceModel.Diagnostics.Application;
27 using System.ServiceModel.Security;
28 using System.ServiceModel.Security.Tokens;
30 using System.Threading;
31 using System.Threading.Tasks;
34 // abstract out the common functionality of an "HttpInput"
35 abstract class HttpInput
37 const string multipartRelatedMediaType = "multipart/related";
38 const string startInfoHeaderParam = "start-info";
39 const string defaultContentType = "application/octet-stream";
41 BufferManager bufferManager;
43 MessageEncoder messageEncoder;
44 IHttpTransportFactorySettings settings;
46 WebException webException;
48 bool enableChannelBinding;
49 bool errorGettingInputStream;
51 protected HttpInput(IHttpTransportFactorySettings settings, bool isRequest, bool enableChannelBinding)
53 this.settings = settings;
54 this.bufferManager = settings.BufferManager;
55 this.messageEncoder = settings.MessageEncoderFactory.Encoder;
56 this.webException = null;
57 this.isRequest = isRequest;
58 this.inputStream = null;
59 this.enableChannelBinding = enableChannelBinding;
63 this.streamed = TransferModeHelper.IsRequestStreamed(settings.TransferMode);
67 this.streamed = TransferModeHelper.IsResponseStreamed(settings.TransferMode);
71 internal static HttpInput CreateHttpInput(HttpWebResponse httpWebResponse, IHttpTransportFactorySettings settings, ChannelBinding channelBinding)
73 return new WebResponseHttpInput(httpWebResponse, settings, channelBinding);
76 internal WebException WebException
78 get { return webException; }
79 set { webException = value; }
82 // Note: This method will return null in the case where throwOnError is false, and a non-fatal error occurs.
83 // Please exercice caution when passing in throwOnError = false. This should basically only be done in error
84 // code paths, or code paths where there is very good reason that you would not want this method to throw.
85 // When passing in throwOnError = false, please handle the case where this method returns null.
86 public Stream GetInputStream(bool throwOnError)
88 if (inputStream == null && (throwOnError || !this.errorGettingInputStream))
92 inputStream = GetInputStream();
93 this.errorGettingInputStream = false;
97 this.errorGettingInputStream = true;
98 if (throwOnError || Fx.IsFatal(e))
103 DiagnosticUtility.TraceHandledException(e, TraceEventType.Warning);
111 public abstract long ContentLength { get; }
112 protected abstract string ContentTypeCore { get; }
113 protected abstract bool HasContent { get; }
114 protected abstract string SoapActionHeader { get; }
115 protected abstract Stream GetInputStream();
116 protected virtual ChannelBinding ChannelBinding { get { return null; } }
118 protected string ContentType
122 string contentType = ContentTypeCore;
124 if (string.IsNullOrEmpty(contentType))
126 return defaultContentType;
133 void ThrowMaxReceivedMessageSizeExceeded()
135 if (TD.MaxReceivedMessageSizeExceededIsEnabled())
137 TD.MaxReceivedMessageSizeExceeded(SR.GetString(SR.MaxReceivedMessageSizeExceeded, settings.MaxReceivedMessageSize));
142 ThrowHttpProtocolException(SR.GetString(SR.MaxReceivedMessageSizeExceeded, settings.MaxReceivedMessageSize), HttpStatusCode.RequestEntityTooLarge);
146 string message = SR.GetString(SR.MaxReceivedMessageSizeExceeded, settings.MaxReceivedMessageSize);
147 Exception inner = new QuotaExceededException(message);
148 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(message, inner));
152 Message DecodeBufferedMessage(ArraySegment<byte> buffer, Stream inputStream)
156 // if we're chunked, make sure we've consumed the whole body
157 if (ContentLength == -1 && buffer.Count == settings.MaxReceivedMessageSize)
159 byte[] extraBuffer = new byte[1];
160 int extraReceived = inputStream.Read(extraBuffer, 0, 1);
161 if (extraReceived > 0)
163 ThrowMaxReceivedMessageSizeExceeded();
169 return messageEncoder.ReadMessage(buffer, bufferManager, ContentType);
171 catch (XmlException xmlException)
173 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
174 new ProtocolException(SR.GetString(SR.MessageXmlProtocolError), xmlException));
183 Message ReadBufferedMessage(Stream inputStream)
185 ArraySegment<byte> messageBuffer = GetMessageBuffer();
186 byte[] buffer = messageBuffer.Array;
188 int count = messageBuffer.Count;
192 int bytesRead = inputStream.Read(buffer, offset, count);
193 if (bytesRead == 0) // EOF
195 if (ContentLength != -1)
197 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
198 new ProtocolException(SR.GetString(SR.HttpContentLengthIncorrect)));
207 return DecodeBufferedMessage(new ArraySegment<byte>(buffer, 0, offset), inputStream);
210 Message ReadChunkedBufferedMessage(Stream inputStream)
214 return messageEncoder.ReadMessage(inputStream, bufferManager, settings.MaxBufferSize, ContentType);
216 catch (XmlException xmlException)
218 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
219 new ProtocolException(SR.GetString(SR.MessageXmlProtocolError), xmlException));
223 Message ReadStreamedMessage(Stream inputStream)
225 MaxMessageSizeStream maxMessageSizeStream = new MaxMessageSizeStream(inputStream, settings.MaxReceivedMessageSize);
229 return messageEncoder.ReadMessage(maxMessageSizeStream, settings.MaxBufferSize, ContentType);
231 catch (XmlException xmlException)
233 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
234 new ProtocolException(SR.GetString(SR.MessageXmlProtocolError), xmlException));
238 protected abstract void AddProperties(Message message);
240 void ApplyChannelBinding(Message message)
242 if (this.enableChannelBinding)
244 ChannelBindingUtility.TryAddToMessage(this.ChannelBinding, message, true);
248 // makes sure that appropriate HTTP level headers are included in the received Message
249 Exception ProcessHttpAddressing(Message message)
251 Exception result = null;
252 AddProperties(message);
254 // check if user is receiving WS-1 messages
255 if (message.Version.Addressing == AddressingVersion.None)
257 bool actionAbsent = false;
260 actionAbsent = (message.Headers.Action == null);
262 catch (XmlException e)
264 DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
266 catch (CommunicationException e)
268 DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
273 result = new ProtocolException(SR.GetString(SR.HttpAddressingNoneHeaderOnWire,
274 XD.AddressingDictionary.Action.Value));
277 bool toAbsent = false;
280 toAbsent = (message.Headers.To == null);
282 catch (XmlException e)
284 DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
286 catch (CommunicationException e)
288 DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
293 result = new ProtocolException(SR.GetString(SR.HttpAddressingNoneHeaderOnWire,
294 XD.AddressingDictionary.To.Value));
296 message.Headers.To = message.Properties.Via;
301 string action = null;
303 if (message.Version.Envelope == EnvelopeVersion.Soap11)
305 action = SoapActionHeader;
307 else if (message.Version.Envelope == EnvelopeVersion.Soap12 && !String.IsNullOrEmpty(ContentType))
309 ContentType parsedContentType = new ContentType(ContentType);
311 if (parsedContentType.MediaType == multipartRelatedMediaType && parsedContentType.Parameters.ContainsKey(startInfoHeaderParam))
313 // fix to grab action from start-info as stated in RFC2387
314 action = new ContentType(parsedContentType.Parameters[startInfoHeaderParam]).Parameters["action"];
318 // only if we can't find an action inside start-info
319 action = parsedContentType.Parameters["action"];
325 action = UrlUtility.UrlDecode(action, Encoding.UTF8);
327 if (action.Length >= 2 && action[0] == '"' && action[action.Length - 1] == '"')
329 action = action.Substring(1, action.Length - 2);
332 if (message.Version.Addressing == AddressingVersion.None)
334 message.Headers.Action = action;
340 if (action.Length > 0 && string.Compare(message.Headers.Action, action, StringComparison.Ordinal) != 0)
342 result = new ActionMismatchAddressingException(SR.GetString(SR.HttpSoapActionMismatchFault,
343 message.Headers.Action, action), message.Headers.Action, action);
347 catch (XmlException e)
349 DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
351 catch (CommunicationException e)
353 DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
358 ApplyChannelBinding(message);
360 if (DiagnosticUtility.ShouldUseActivity)
362 TraceUtility.TransferFromTransport(message);
364 if (DiagnosticUtility.ShouldTraceInformation)
366 TraceUtility.TraceEvent(TraceEventType.Information, TraceCode.MessageReceived, SR.GetString(SR.TraceCodeMessageReceived),
367 MessageTransmitTraceRecord.CreateReceiveTraceRecord(message), this, null, message);
370 // MessageLogger doesn't log AddressingVersion.None in the encoder since we want to make sure we log
371 // as much of the message as possible. Here we log after stamping the addressing information
372 if (MessageLogger.LoggingEnabled && message.Version.Addressing == AddressingVersion.None)
374 MessageLogger.LogMessage(ref message, MessageLoggingSource.TransportReceive | MessageLoggingSource.LastChance);
380 void ValidateContentType()
385 if (string.IsNullOrEmpty(ContentType))
387 if (MessageLogger.ShouldLogMalformed)
389 // We pass in throwOnError = false below so that the exception which is eventually thrown is the ProtocolException below, with Http status code 415 "UnsupportedMediaType"
390 Stream stream = this.GetInputStream(false);
393 MessageLogger.LogMessage(stream, MessageLoggingSource.Malformed);
396 ThrowHttpProtocolException(SR.GetString(SR.HttpContentTypeHeaderRequired), HttpStatusCode.UnsupportedMediaType, HttpChannelUtilities.StatusDescriptionStrings.HttpContentTypeMissing);
398 if (!messageEncoder.IsContentTypeSupported(ContentType))
400 if (MessageLogger.ShouldLogMalformed)
402 // We pass in throwOnError = false below so that the exception which is eventually thrown is the ProtocolException below, with Http status code 415 "UnsupportedMediaType"
403 Stream stream = this.GetInputStream(false);
406 MessageLogger.LogMessage(stream, MessageLoggingSource.Malformed);
409 string statusDescription = string.Format(CultureInfo.InvariantCulture, HttpChannelUtilities.StatusDescriptionStrings.HttpContentTypeMismatch, ContentType, messageEncoder.ContentType);
410 ThrowHttpProtocolException(SR.GetString(SR.ContentTypeMismatch, ContentType, messageEncoder.ContentType), HttpStatusCode.UnsupportedMediaType, statusDescription);
414 public IAsyncResult BeginParseIncomingMessage(AsyncCallback callback, object state)
416 return this.BeginParseIncomingMessage(null, callback, state);
419 public IAsyncResult BeginParseIncomingMessage(HttpRequestMessage httpRequestMessage, AsyncCallback callback, object state)
421 bool throwing = true;
424 IAsyncResult result = new ParseMessageAsyncResult(httpRequestMessage, this, callback, state);
437 public Message EndParseIncomingMessage(IAsyncResult result, out Exception requestException)
439 bool throwing = true;
442 Message message = ParseMessageAsyncResult.End(result, out requestException);
455 public HttpRequestMessageHttpInput CreateHttpRequestMessageInput()
457 HttpRequestMessage message = new HttpRequestMessage();
461 message.Content = new StreamContent(new MaxMessageSizeStream(this.GetInputStream(true), this.settings.MaxReceivedMessageSize));
464 HttpChannelUtilities.EnsureHttpRequestMessageContentNotNull(message);
466 this.ConfigureHttpRequestMessage(message);
467 ChannelBinding channelBinding = this.enableChannelBinding ? this.ChannelBinding : null;
468 return new HttpRequestMessageHttpInput(message, this.settings, this.enableChannelBinding, channelBinding);
471 public abstract void ConfigureHttpRequestMessage(HttpRequestMessage message);
473 public Message ParseIncomingMessage(out Exception requestException)
475 return this.ParseIncomingMessage(null, out requestException);
478 public Message ParseIncomingMessage(HttpRequestMessage httpRequestMessage, out Exception requestException)
480 Message message = null;
481 requestException = null;
482 bool throwing = true;
485 ValidateContentType();
487 ServiceModelActivity activity = null;
488 if (DiagnosticUtility.ShouldUseActivity &&
489 ((ServiceModelActivity.Current == null) ||
490 (ServiceModelActivity.Current.ActivityType != ActivityType.ProcessAction)))
492 activity = ServiceModelActivity.CreateBoundedActivity(true);
496 if (DiagnosticUtility.ShouldUseActivity && activity != null)
498 // Only update the Start identifier if the activity is not null.
499 ServiceModelActivity.Start(activity, SR.GetString(SR.ActivityProcessingMessage, TraceUtility.RetrieveMessageNumber()), ActivityType.ProcessMessage);
502 if (!this.HasContent)
504 if (this.messageEncoder.MessageVersion == MessageVersion.None)
506 message = new NullMessage();
515 Stream stream = this.GetInputStream(true);
518 message = ReadStreamedMessage(stream);
520 else if (this.ContentLength == -1)
522 message = ReadChunkedBufferedMessage(stream);
526 if (httpRequestMessage == null)
528 message = ReadBufferedMessage(stream);
532 message = ReadBufferedMessage(httpRequestMessage);
537 requestException = ProcessHttpAddressing(message);
552 Message ReadBufferedMessage(HttpRequestMessage httpRequestMessage)
554 Fx.Assert(httpRequestMessage != null, "httpRequestMessage cannot be null.");
557 using (HttpContent currentContent = httpRequestMessage.Content)
559 int length = (int)this.ContentLength;
560 byte[] buffer = this.bufferManager.TakeBuffer(length);
561 bool success = false;
564 MemoryStream ms = new MemoryStream(buffer);
565 currentContent.CopyToAsync(ms).Wait<CommunicationException>();
566 httpRequestMessage.Content = new ByteArrayContent(buffer, 0, length);
568 foreach (var header in currentContent.Headers)
570 httpRequestMessage.Content.Headers.Add(header.Key, header.Value);
575 message = this.messageEncoder.ReadMessage(new ArraySegment<byte>(buffer, 0, length), this.bufferManager, this.ContentType);
582 // We don't have to return it in success case since the buffer will be returned to bufferManager when the message is disposed.
583 this.bufferManager.ReturnBuffer(buffer);
590 void ThrowHttpProtocolException(string message, HttpStatusCode statusCode)
592 ThrowHttpProtocolException(message, statusCode, null);
595 void ThrowHttpProtocolException(string message, HttpStatusCode statusCode, string statusDescription)
597 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateHttpProtocolException(message, statusCode, statusDescription, webException));
600 internal static ProtocolException CreateHttpProtocolException(string message, HttpStatusCode statusCode, string statusDescription, Exception innerException)
602 ProtocolException exception = new ProtocolException(message, innerException);
603 exception.Data.Add(HttpChannelUtilities.HttpStatusCodeExceptionKey, statusCode);
604 if (statusDescription != null && statusDescription.Length > 0)
606 exception.Data.Add(HttpChannelUtilities.HttpStatusDescriptionExceptionKey, statusDescription);
612 protected virtual void Close()
616 ArraySegment<byte> GetMessageBuffer()
618 long count = ContentLength;
621 if (count > settings.MaxReceivedMessageSize)
623 ThrowMaxReceivedMessageSizeExceeded();
626 bufferSize = (int)count;
628 return new ArraySegment<byte>(bufferManager.TakeBuffer(bufferSize), 0, bufferSize);
631 class ParseMessageAsyncResult : TraceAsyncResult
633 ArraySegment<byte> buffer;
639 Exception requestException = null;
640 HttpRequestMessage httpRequestMessage;
641 static AsyncCallback onRead = Fx.ThunkCallback(new AsyncCallback(OnRead));
643 public ParseMessageAsyncResult(
644 HttpRequestMessage httpRequestMessage,
646 AsyncCallback callback,
648 : base(callback, state)
650 this.httpInput = httpInput;
651 this.httpRequestMessage = httpRequestMessage;
657 httpInput.ValidateContentType();
658 this.inputStream = httpInput.GetInputStream(true);
660 if (!httpInput.HasContent)
662 if (httpInput.messageEncoder.MessageVersion == MessageVersion.None)
664 this.message = new NullMessage();
672 else if (httpInput.streamed || httpInput.ContentLength == -1)
674 if (httpInput.streamed)
676 this.message = httpInput.ReadStreamedMessage(inputStream);
680 this.message = httpInput.ReadChunkedBufferedMessage(inputStream);
684 if (this.message != null)
686 this.requestException = httpInput.ProcessHttpAddressing(this.message);
691 AsyncCompletionResult result;
692 if (httpRequestMessage == null)
694 result = this.DecodeBufferedMessageAsync();
698 result = this.DecodeBufferedHttpRequestMessageAsync();
701 if (result == AsyncCompletionResult.Completed)
707 AsyncCompletionResult DecodeBufferedMessageAsync()
709 this.buffer = this.httpInput.GetMessageBuffer();
710 this.count = this.buffer.Count;
713 IAsyncResult result = inputStream.BeginRead(buffer.Array, offset, count, onRead, this);
714 if (result.CompletedSynchronously)
716 if (ContinueReading(inputStream.EndRead(result)))
718 return AsyncCompletionResult.Completed;
722 return AsyncCompletionResult.Queued;
725 bool ContinueReading(int bytesRead)
729 if (bytesRead == 0) // EOF
743 IAsyncResult result = inputStream.BeginRead(buffer.Array, offset, count, onRead, this);
744 if (!result.CompletedSynchronously)
749 bytesRead = inputStream.EndRead(result);
754 using (DiagnosticUtility.ShouldUseActivity ? ServiceModelActivity.BoundOperation(this.CallbackActivity) : null)
756 using (ServiceModelActivity activity = DiagnosticUtility.ShouldUseActivity ? ServiceModelActivity.CreateBoundedActivity(true) : null)
758 if (DiagnosticUtility.ShouldUseActivity)
760 ServiceModelActivity.Start(activity, SR.GetString(SR.ActivityProcessingMessage, TraceUtility.RetrieveMessageNumber()), ActivityType.ProcessMessage);
763 this.message = this.httpInput.DecodeBufferedMessage(new ArraySegment<byte>(buffer.Array, 0, offset), inputStream);
764 this.requestException = this.httpInput.ProcessHttpAddressing(this.message);
770 static void OnRead(IAsyncResult result)
772 if (result.CompletedSynchronously)
775 ParseMessageAsyncResult thisPtr = (ParseMessageAsyncResult)result.AsyncState;
777 Exception completionException = null;
781 completeSelf = thisPtr.ContinueReading(thisPtr.inputStream.EndRead(result));
783 #pragma warning suppress 56500 // Microsoft, transferring exception to another thread
792 completionException = e;
797 thisPtr.Complete(false, completionException);
801 public static Message End(IAsyncResult result, out Exception requestException)
803 ParseMessageAsyncResult thisPtr = AsyncResult.End<ParseMessageAsyncResult>(result);
804 requestException = thisPtr.requestException;
805 return thisPtr.message;
808 AsyncCompletionResult DecodeBufferedHttpRequestMessageAsync()
810 // Need to consider moving this to async implemenation for HttpContent reading.(CSDMAIN: 229108)
811 this.message = this.httpInput.ReadBufferedMessage(this.httpRequestMessage);
812 this.requestException = this.httpInput.ProcessHttpAddressing(this.message);
813 return AsyncCompletionResult.Completed;
817 class WebResponseHttpInput : HttpInput
819 HttpWebResponse httpWebResponse;
820 byte[] preReadBuffer;
821 ChannelBinding channelBinding;
824 public WebResponseHttpInput(HttpWebResponse httpWebResponse, IHttpTransportFactorySettings settings, ChannelBinding channelBinding)
825 : base(settings, false, channelBinding != null)
827 this.channelBinding = channelBinding;
828 this.httpWebResponse = httpWebResponse;
829 if (this.httpWebResponse.ContentLength == -1)
831 this.preReadBuffer = new byte[1];
833 if (this.httpWebResponse.GetResponseStream().Read(preReadBuffer, 0, 1) == 0)
835 this.preReadBuffer = null;
839 this.hasContent = (this.preReadBuffer != null || this.httpWebResponse.ContentLength > 0);
840 if (!this.hasContent)
842 // Close the response stream to avoid leaking the connection.
843 this.httpWebResponse.GetResponseStream().Close();
847 protected override ChannelBinding ChannelBinding
851 return this.channelBinding;
855 public override long ContentLength
859 return httpWebResponse.ContentLength;
863 protected override string ContentTypeCore
867 return httpWebResponse.ContentType;
871 protected override bool HasContent
873 get { return this.hasContent; }
876 protected override string SoapActionHeader
880 return httpWebResponse.Headers["SOAPAction"];
884 protected override void AddProperties(Message message)
886 HttpResponseMessageProperty responseProperty = new HttpResponseMessageProperty(httpWebResponse.Headers);
887 responseProperty.StatusCode = httpWebResponse.StatusCode;
888 responseProperty.StatusDescription = httpWebResponse.StatusDescription;
889 message.Properties.Add(HttpResponseMessageProperty.Name, responseProperty);
890 message.Properties.Via = message.Version.Addressing.AnonymousUri;
893 public override void ConfigureHttpRequestMessage(HttpRequestMessage message)
895 // HTTP pipeline for client side is not implemented yet
896 // DCR CSDMain 216853 is tracking this
897 // This API is never going to be called in current stack
898 Fx.Assert(false, "HTTP pipeline for client is not implemented yet. This method should not be called.");
899 throw FxTrace.Exception.AsError(new NotSupportedException());
902 protected override void Close()
906 httpWebResponse.Close();
908 catch (Exception exception)
910 if (Fx.IsFatal(exception))
913 DiagnosticUtility.TraceHandledException(exception, TraceEventType.Error);
917 protected override Stream GetInputStream()
919 Fx.Assert(this.HasContent, "this.HasContent must be true.");
920 if (this.preReadBuffer != null)
922 return new WebResponseInputStream(httpWebResponse, preReadBuffer);
926 return new WebResponseInputStream(httpWebResponse);
930 class WebResponseInputStream : DetectEofStream
932 // in order to avoid ----ing kernel buffers, we throttle our reads. http.sys
933 // deals with this fine, but System.Net doesn't do any such throttling.
934 const int maxSocketRead = 64 * 1024;
935 HttpWebResponse webResponse;
938 public WebResponseInputStream(HttpWebResponse httpWebResponse)
939 : base(httpWebResponse.GetResponseStream())
941 this.webResponse = httpWebResponse;
944 public WebResponseInputStream(HttpWebResponse httpWebResponse, byte[] prereadBuffer)
945 : base(new PreReadStream(httpWebResponse.GetResponseStream(), prereadBuffer))
947 this.webResponse = httpWebResponse;
951 public override void Close()
957 protected override void OnReceivedEof()
959 base.OnReceivedEof();
970 responseClosed = true;
971 this.webResponse.Close();
974 public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
978 return BaseStream.BeginRead(buffer, offset, Math.Min(count, maxSocketRead), callback, state);
980 catch (IOException ioException)
982 throw this.CreateResponseIOException(ioException);
984 catch (ObjectDisposedException objectDisposedException)
986 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(objectDisposedException.Message, objectDisposedException));
988 catch (WebException webException)
990 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateResponseWebException(webException, this.webResponse));
994 public override int EndRead(IAsyncResult result)
998 return BaseStream.EndRead(result);
1000 catch (IOException ioException)
1002 throw this.CreateResponseIOException(ioException);
1004 catch (ObjectDisposedException objectDisposedException)
1006 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(objectDisposedException.Message, objectDisposedException));
1008 catch (WebException webException)
1010 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateResponseWebException(webException, this.webResponse));
1014 public override int Read(byte[] buffer, int offset, int count)
1018 return BaseStream.Read(buffer, offset, Math.Min(count, maxSocketRead));
1020 catch (ObjectDisposedException objectDisposedException)
1022 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(objectDisposedException.Message, objectDisposedException));
1024 catch (IOException ioException)
1026 throw this.CreateResponseIOException(ioException);
1028 catch (WebException webException)
1030 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateResponseWebException(webException, this.webResponse));
1035 public override int ReadByte()
1039 return BaseStream.ReadByte();
1041 catch (ObjectDisposedException objectDisposedException)
1043 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(objectDisposedException.Message, objectDisposedException));
1045 catch (IOException ioException)
1047 throw this.CreateResponseIOException(ioException);
1049 catch (WebException webException)
1051 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateResponseWebException(webException, this.webResponse));
1055 private Exception CreateResponseIOException(IOException ioException)
1057 TimeSpan timeSpan = this.CanTimeout ? TimeoutHelper.FromMilliseconds(this.ReadTimeout) : TimeSpan.MaxValue;
1059 return DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateResponseIOException(ioException, timeSpan));
1066 // abstract out the common functionality of an "HttpOutput"
1067 abstract class HttpOutput
1069 const string DefaultMimeVersion = "1.0";
1071 HttpAbortReason abortReason;
1075 IHttpTransportFactorySettings settings;
1076 byte[] bufferToRecycle;
1077 BufferManager bufferManager;
1078 MessageEncoder messageEncoder;
1080 static Action<object> onStreamSendTimeout;
1081 string mtomBoundary;
1082 Stream outputStream;
1083 bool supportsConcurrentIO;
1084 EventTraceActivity eventTraceActivity;
1085 bool canSendCompressedResponses;
1087 protected HttpOutput(IHttpTransportFactorySettings settings, Message message, bool isRequest, bool supportsConcurrentIO)
1089 this.settings = settings;
1090 this.message = message;
1091 this.isRequest = isRequest;
1092 this.bufferManager = settings.BufferManager;
1093 this.messageEncoder = settings.MessageEncoderFactory.Encoder;
1094 ICompressedMessageEncoder compressedMessageEncoder = this.messageEncoder as ICompressedMessageEncoder;
1095 this.canSendCompressedResponses = compressedMessageEncoder != null && compressedMessageEncoder.CompressionEnabled;
1098 this.streamed = TransferModeHelper.IsRequestStreamed(settings.TransferMode);
1102 this.streamed = TransferModeHelper.IsResponseStreamed(settings.TransferMode);
1104 this.supportsConcurrentIO = supportsConcurrentIO;
1106 if (FxTrace.Trace.IsEnd2EndActivityTracingEnabled)
1108 this.eventTraceActivity = EventTraceActivityHelper.TryExtractActivity(message);
1112 protected virtual bool IsChannelBindingSupportEnabled { get { return false; } }
1113 protected virtual ChannelBinding ChannelBinding { get { return null; } }
1115 protected void Abort()
1117 Abort(HttpAbortReason.Aborted);
1120 public virtual void Abort(HttpAbortReason reason)
1127 this.abortReason = reason;
1129 TraceRequestResponseAborted(reason);
1134 private void TraceRequestResponseAborted(HttpAbortReason reason)
1138 if (TD.HttpChannelRequestAbortedIsEnabled())
1140 TD.HttpChannelRequestAborted(this.eventTraceActivity);
1143 else if (TD.HttpChannelResponseAbortedIsEnabled())
1145 TD.HttpChannelResponseAborted(this.eventTraceActivity);
1148 if (DiagnosticUtility.ShouldTraceWarning)
1150 TraceUtility.TraceEvent(TraceEventType.Warning,
1151 isRequest ? TraceCode.HttpChannelRequestAborted : TraceCode.HttpChannelResponseAborted,
1152 isRequest ? SR.GetString(SR.TraceCodeHttpChannelRequestAborted) : SR.GetString(SR.TraceCodeHttpChannelResponseAborted),
1166 if (this.outputStream != null)
1168 outputStream.Close();
1177 void CleanupBuffer()
1179 byte[] bufferToRecycleSnapshot = Interlocked.Exchange<byte[]>(ref this.bufferToRecycle, null);
1180 if (bufferToRecycleSnapshot != null)
1182 bufferManager.ReturnBuffer(bufferToRecycleSnapshot);
1188 protected abstract void AddMimeVersion(string version);
1189 protected abstract void AddHeader(string name, string value);
1190 protected abstract void SetContentType(string contentType);
1191 protected abstract void SetContentEncoding(string contentEncoding);
1192 protected abstract void SetStatusCode(HttpStatusCode statusCode);
1193 protected abstract void SetStatusDescription(string statusDescription);
1194 protected virtual bool CleanupChannelBinding { get { return true; } }
1195 protected virtual void SetContentLength(int contentLength)
1199 protected virtual string HttpMethod { get { return null; } }
1201 public virtual ChannelBinding TakeChannelBinding()
1206 private void ApplyChannelBinding()
1208 if (this.IsChannelBindingSupportEnabled)
1210 ChannelBindingUtility.TryAddToMessage(this.ChannelBinding, this.message, this.CleanupChannelBinding);
1214 protected abstract Stream GetOutputStream();
1216 protected virtual bool WillGetOutputStreamCompleteSynchronously
1218 get { return true; }
1221 protected bool CanSendCompressedResponses
1223 get { return this.canSendCompressedResponses; }
1226 protected virtual IAsyncResult BeginGetOutputStream(AsyncCallback callback, object state)
1228 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException());
1231 protected virtual Stream EndGetOutputStream(IAsyncResult result)
1233 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException());
1236 public void ConfigureHttpResponseMessage(Message message, HttpResponseMessage httpResponseMessage, HttpResponseMessageProperty responseProperty)
1238 HttpChannelUtilities.EnsureHttpResponseMessageContentNotNull(httpResponseMessage);
1240 string action = message.Headers.Action;
1242 if (message.Version.Addressing == AddressingVersion.None)
1244 if (MessageLogger.LogMessagesAtTransportLevel)
1246 message.Properties.Add(AddressingProperty.Name, new AddressingProperty(message.Headers));
1249 message.Headers.Action = null;
1250 message.Headers.To = null;
1253 bool httpResponseMessagePropertyFound = responseProperty != null;
1255 string contentType = null;
1256 if (message.Version == MessageVersion.None && httpResponseMessagePropertyFound && !string.IsNullOrEmpty(responseProperty.Headers[HttpResponseHeader.ContentType]))
1258 contentType = responseProperty.Headers[HttpResponseHeader.ContentType];
1259 responseProperty.Headers.Remove(HttpResponseHeader.ContentType);
1260 if (!messageEncoder.IsContentTypeSupported(contentType))
1262 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
1263 new ProtocolException(SR.GetString(SR.ResponseContentTypeNotSupported,
1268 if (string.IsNullOrEmpty(contentType))
1270 MtomMessageEncoder mtomMessageEncoder = messageEncoder as MtomMessageEncoder;
1271 if (mtomMessageEncoder == null)
1273 contentType = messageEncoder.ContentType;
1277 contentType = mtomMessageEncoder.GetContentType(out this.mtomBoundary);
1278 // For MTOM messages, add a MIME version header
1279 httpResponseMessage.Headers.Add(HttpChannelUtilities.MIMEVersionHeader, DefaultMimeVersion);
1283 if (isRequest && FxTrace.Trace.IsEnd2EndActivityTracingEnabled)
1285 EnsureEventTraceActivity(message);
1288 if (this.CanSendCompressedResponses)
1290 string contentEncoding;
1291 string compressionContentType = contentType;
1292 if (HttpChannelUtilities.GetHttpResponseTypeAndEncodingForCompression(ref compressionContentType, out contentEncoding))
1294 contentType = compressionContentType;
1295 this.SetContentEncoding(contentEncoding);
1299 if (httpResponseMessage.Content != null && !string.IsNullOrEmpty(contentType))
1301 MediaTypeHeaderValue mediaTypeHeaderValue;
1302 if (!MediaTypeHeaderValue.TryParse(contentType, out mediaTypeHeaderValue))
1304 throw FxTrace.Exception.Argument("contentType", SR.GetString(SR.InvalidContentTypeError, contentType));
1306 httpResponseMessage.Content.Headers.ContentType = mediaTypeHeaderValue;
1309 bool httpMethodIsHead = string.Compare(this.HttpMethod, "HEAD", StringComparison.OrdinalIgnoreCase) == 0;
1311 if (httpMethodIsHead ||
1312 httpResponseMessagePropertyFound && responseProperty.SuppressEntityBody)
1314 httpResponseMessage.Content.Headers.ContentLength = 0;
1315 httpResponseMessage.Content.Headers.ContentType = null;
1318 if (httpResponseMessagePropertyFound)
1320 httpResponseMessage.StatusCode = responseProperty.StatusCode;
1321 if (responseProperty.StatusDescription != null)
1323 responseProperty.StatusDescription = responseProperty.StatusDescription;
1326 foreach (string key in responseProperty.Headers.AllKeys)
1328 httpResponseMessage.AddHeader(key, responseProperty.Headers[key]);
1332 if (!message.IsEmpty)
1334 using (HttpContent content = httpResponseMessage.Content)
1338 IStreamedMessageEncoder streamedMessageEncoder = this.messageEncoder as IStreamedMessageEncoder;
1339 Stream stream = null;
1340 if (streamedMessageEncoder != null)
1342 stream = streamedMessageEncoder.GetResponseMessageStream(message);
1347 httpResponseMessage.Content = new StreamContent(stream);
1351 httpResponseMessage.Content = new OpaqueContent(this.messageEncoder, message, this.mtomBoundary);
1356 // HttpOutputByteArrayContent assumes responsibility for returning the buffer to the bufferManager.
1357 ArraySegment<byte> messageBytes = this.SerializeBufferedMessage(message, false);
1358 httpResponseMessage.Content = new HttpOutputByteArrayContent(messageBytes.Array, messageBytes.Offset, messageBytes.Count, this.bufferManager);
1361 httpResponseMessage.Content.Headers.Clear();
1362 foreach (var header in content.Headers)
1364 httpResponseMessage.Content.Headers.Add(header.Key, header.Value);
1370 protected virtual bool PrepareHttpSend(Message message)
1372 string action = message.Headers.Action;
1374 if (message.Version.Addressing == AddressingVersion.None)
1376 if (MessageLogger.LogMessagesAtTransportLevel)
1378 message.Properties.Add(AddressingProperty.Name, new AddressingProperty(message.Headers));
1381 message.Headers.Action = null;
1382 message.Headers.To = null;
1385 string contentType = null;
1387 if (message.Version == MessageVersion.None)
1389 object property = null;
1390 if (message.Properties.TryGetValue(HttpResponseMessageProperty.Name, out property))
1392 HttpResponseMessageProperty responseProperty = (HttpResponseMessageProperty)property;
1393 if (!string.IsNullOrEmpty(responseProperty.Headers[HttpResponseHeader.ContentType]))
1395 contentType = responseProperty.Headers[HttpResponseHeader.ContentType];
1396 if (!messageEncoder.IsContentTypeSupported(contentType))
1398 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
1399 new ProtocolException(SR.GetString(SR.ResponseContentTypeNotSupported,
1406 if (string.IsNullOrEmpty(contentType))
1408 MtomMessageEncoder mtomMessageEncoder = messageEncoder as MtomMessageEncoder;
1409 if (mtomMessageEncoder == null)
1411 contentType = messageEncoder.ContentType;
1415 contentType = mtomMessageEncoder.GetContentType(out this.mtomBoundary);
1416 // For MTOM messages, add a MIME version header
1417 AddMimeVersion("1.0");
1421 if (isRequest && FxTrace.Trace.IsEnd2EndActivityTracingEnabled)
1423 EnsureEventTraceActivity(message);
1426 SetContentType(contentType);
1427 return message is NullMessage;
1430 protected bool PrepareHttpSend(HttpResponseMessage httpResponseMessage)
1432 this.PrepareHttpSendCore(httpResponseMessage);
1433 return HttpChannelUtilities.IsEmpty(httpResponseMessage);
1436 protected abstract void PrepareHttpSendCore(HttpResponseMessage message);
1438 private static void EnsureEventTraceActivity(Message message)
1440 //We need to send this only if there is no message id.
1441 if (message.Headers.MessageId == null)
1443 EventTraceActivity eventTraceActivity = EventTraceActivityHelper.TryExtractActivity(message);
1444 if (eventTraceActivity == null)
1446 //Whoops no activity on the message yet.
1447 eventTraceActivity = new EventTraceActivity();
1448 EventTraceActivityHelper.TryAttachActivity(message, eventTraceActivity);
1451 HttpRequestMessageProperty httpProperties;
1452 if (!message.Properties.TryGetValue<HttpRequestMessageProperty>(HttpRequestMessageProperty.Name, out httpProperties))
1454 httpProperties = new HttpRequestMessageProperty();
1455 message.Properties.Add(HttpRequestMessageProperty.Name, httpProperties);
1457 httpProperties.Headers.Add(EventTraceActivity.Name, Convert.ToBase64String(eventTraceActivity.ActivityId.ToByteArray()));
1461 ArraySegment<byte> SerializeBufferedMessage(Message message)
1463 // by default, the HttpOutput should own the buffer and clean it up
1464 return SerializeBufferedMessage(message, true);
1467 ArraySegment<byte> SerializeBufferedMessage(Message message, bool shouldRecycleBuffer)
1469 ArraySegment<byte> result;
1471 MtomMessageEncoder mtomMessageEncoder = messageEncoder as MtomMessageEncoder;
1472 if (mtomMessageEncoder == null)
1474 result = messageEncoder.WriteMessage(message, int.MaxValue, bufferManager);
1478 result = mtomMessageEncoder.WriteMessage(message, int.MaxValue, bufferManager, 0, this.mtomBoundary);
1481 if (shouldRecycleBuffer)
1483 // Only set this.bufferToRecycle if the HttpOutput owns the buffer, we will clean it up upon httpOutput.Close()
1484 // Otherwise, caller of SerializeBufferedMessage assumes responsiblity for returning the buffer to the buffer pool
1485 this.bufferToRecycle = result.Array;
1490 Stream GetWrappedOutputStream()
1492 const int ChunkSize = 32768; // buffer size used for synchronous writes
1493 const int BufferSize = 16384; // buffer size used for asynchronous writes
1494 const int BufferCount = 4; // buffer count used for asynchronous writes
1496 // Writing an HTTP request chunk has a high fixed cost, so use BufferedStream to avoid writing
1498 return this.supportsConcurrentIO ? (Stream)new BufferedOutputAsyncStream(this.outputStream, BufferSize, BufferCount) : new BufferedStream(this.outputStream, ChunkSize);
1501 void WriteStreamedMessage(TimeSpan timeout)
1503 this.outputStream = GetWrappedOutputStream();
1505 // Since HTTP streams don't support timeouts, we can't just use TimeoutStream here.
1506 // Rather, we need to run a timer to bound the overall operation
1507 if (onStreamSendTimeout == null)
1509 onStreamSendTimeout = new Action<object>(OnStreamSendTimeout);
1511 IOThreadTimer sendTimer = new IOThreadTimer(onStreamSendTimeout, this, true);
1512 sendTimer.Set(timeout);
1516 MtomMessageEncoder mtomMessageEncoder = messageEncoder as MtomMessageEncoder;
1517 if (mtomMessageEncoder == null)
1519 messageEncoder.WriteMessage(this.message, this.outputStream);
1523 mtomMessageEncoder.WriteMessage(this.message, this.outputStream, this.mtomBoundary);
1526 if (this.supportsConcurrentIO)
1528 this.outputStream.Close();
1537 static void OnStreamSendTimeout(object state)
1539 HttpOutput thisPtr = (HttpOutput)state;
1540 thisPtr.Abort(HttpAbortReason.TimedOut);
1543 IAsyncResult BeginWriteStreamedMessage(HttpResponseMessage httpResponseMessage, TimeSpan timeout, AsyncCallback callback, object state)
1545 return new WriteStreamedMessageAsyncResult(timeout, this, httpResponseMessage, callback, state);
1548 void EndWriteStreamedMessage(IAsyncResult result)
1550 WriteStreamedMessageAsyncResult.End(result);
1553 class HttpOutputByteArrayContent : ByteArrayContent
1555 BufferManager bufferManager;
1556 volatile bool cleaned = false;
1557 ArraySegment<byte> content;
1559 public HttpOutputByteArrayContent(byte[] content, int offset, int count, BufferManager bufferManager)
1560 : base(content, offset, count)
1562 Fx.Assert(bufferManager != null, "bufferManager should not be null");
1563 Fx.Assert(content != null, "content should not be null");
1564 this.content = new ArraySegment<byte>(content, offset, count);
1565 this.bufferManager = bufferManager;
1568 public ArraySegment<byte> Content
1572 return this.content;
1576 protected override Task<Stream> CreateContentReadStreamAsync()
1578 return base.CreateContentReadStreamAsync().ContinueWith<Stream>(t =>
1579 new HttpOutputByteArrayContentStream(t.Result, this));
1582 protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
1584 return base.SerializeToStreamAsync(stream, context).ContinueWith(t =>
1587 HttpChannelUtilities.HandleContinueWithTask(t);
1600 this.bufferManager.ReturnBuffer(this.content.Array);
1606 class HttpOutputByteArrayContentStream : DelegatingStream
1608 HttpOutputByteArrayContent content;
1610 public HttpOutputByteArrayContentStream(Stream innerStream, HttpOutputByteArrayContent content)
1613 this.content = content;
1616 public override void Close()
1619 this.content.Cleanup();
1624 class WriteStreamedMessageAsyncResult : AsyncResult
1626 HttpOutput httpOutput;
1627 IOThreadTimer sendTimer;
1628 static AsyncCallback onWriteStreamedMessage = Fx.ThunkCallback(OnWriteStreamedMessage);
1629 HttpResponseMessage httpResponseMessage;
1631 public WriteStreamedMessageAsyncResult(TimeSpan timeout, HttpOutput httpOutput, HttpResponseMessage httpResponseMessage, AsyncCallback callback, object state)
1632 : base(callback, state)
1634 this.httpResponseMessage = httpResponseMessage;
1635 this.httpOutput = httpOutput;
1636 httpOutput.outputStream = httpOutput.GetWrappedOutputStream();
1638 // Since HTTP streams don't support timeouts, we can't just use TimeoutStream here.
1639 // Rather, we need to run a timer to bound the overall operation
1640 if (onStreamSendTimeout == null)
1642 onStreamSendTimeout = new Action<object>(OnStreamSendTimeout);
1644 this.SetTimer(timeout);
1646 bool completeSelf = false;
1647 bool throwing = true;
1651 completeSelf = HandleWriteStreamedMessage(null);
1656 if (completeSelf || throwing)
1658 this.sendTimer.Cancel();
1664 this.Complete(true);
1668 bool HandleWriteStreamedMessage(IAsyncResult result)
1670 if (this.httpResponseMessage == null)
1674 MtomMessageEncoder mtomMessageEncoder = httpOutput.messageEncoder as MtomMessageEncoder;
1675 if (mtomMessageEncoder == null)
1677 result = httpOutput.messageEncoder.BeginWriteMessage(httpOutput.message, httpOutput.outputStream, onWriteStreamedMessage, this);
1681 result = mtomMessageEncoder.BeginWriteMessage(httpOutput.message, httpOutput.outputStream, httpOutput.mtomBoundary, onWriteStreamedMessage, this);
1684 if (!result.CompletedSynchronously)
1690 httpOutput.messageEncoder.EndWriteMessage(result);
1692 if (this.httpOutput.supportsConcurrentIO)
1694 httpOutput.outputStream.Close();
1701 OpaqueContent content = this.httpResponseMessage.Content as OpaqueContent;
1704 Fx.Assert(this.httpResponseMessage.Content != null, "httpOutput.httpResponseMessage.Content should not be null.");
1706 if (content != null)
1708 result = content.BeginWriteToStream(httpOutput.outputStream, onWriteStreamedMessage, this);
1712 result = this.httpResponseMessage.Content.CopyToAsync(httpOutput.outputStream).AsAsyncResult(onWriteStreamedMessage, this);
1715 if (!result.CompletedSynchronously)
1721 if (content != null)
1723 content.EndWriteToStream(result);
1726 if (this.httpOutput.supportsConcurrentIO)
1728 httpOutput.outputStream.Close();
1735 static void OnWriteStreamedMessage(IAsyncResult result)
1737 if (result.CompletedSynchronously)
1742 WriteStreamedMessageAsyncResult thisPtr = (WriteStreamedMessageAsyncResult)result.AsyncState;
1743 Exception completionException = null;
1744 bool completeSelf = false;
1748 completeSelf = thisPtr.HandleWriteStreamedMessage(result);
1750 catch (Exception ex)
1756 completeSelf = true;
1757 completionException = ex;
1762 thisPtr.sendTimer.Cancel();
1763 thisPtr.Complete(false, completionException);
1767 void SetTimer(TimeSpan timeout)
1769 Fx.Assert(this.sendTimer == null, "SetTimer should only be called once");
1771 this.sendTimer = new IOThreadTimer(onStreamSendTimeout, this.httpOutput, true);
1772 this.sendTimer.Set(timeout);
1775 public static void End(IAsyncResult result)
1777 AsyncResult.End<WriteStreamedMessageAsyncResult>(result);
1781 public IAsyncResult BeginSend(HttpResponseMessage httpResponseMessage, TimeSpan timeout, AsyncCallback callback, object state)
1783 Fx.Assert(httpResponseMessage != null, "httpResponseMessage should not be null.");
1784 return this.BeginSendCore(httpResponseMessage, timeout, callback, state);
1787 public IAsyncResult BeginSend(TimeSpan timeout, AsyncCallback callback, object state)
1789 return this.BeginSendCore(null, timeout, callback, state);
1792 IAsyncResult BeginSendCore(HttpResponseMessage httpResponseMessage, TimeSpan timeout, AsyncCallback callback, object state)
1794 bool throwing = true;
1797 bool suppressEntityBody;
1798 if (httpResponseMessage != null)
1800 suppressEntityBody = this.PrepareHttpSend(httpResponseMessage);
1804 suppressEntityBody = PrepareHttpSend(message);
1807 this.TraceHttpSendStart();
1808 IAsyncResult result = new SendAsyncResult(this, httpResponseMessage, suppressEntityBody, timeout, callback, state);
1821 private void TraceHttpSendStart()
1823 if (TD.HttpSendMessageStartIsEnabled())
1827 TD.HttpSendStreamedMessageStart(this.eventTraceActivity);
1831 TD.HttpSendMessageStart(this.eventTraceActivity);
1836 public virtual void EndSend(IAsyncResult result)
1838 bool throwing = true;
1841 SendAsyncResult.End(result);
1855 if (MessageLogger.LogMessagesAtTransportLevel)
1857 MessageLogger.LogMessage(ref message, MessageLoggingSource.TransportSend);
1861 public void Send(HttpResponseMessage httpResponseMessage, TimeSpan timeout)
1863 bool suppressEntityBody = this.PrepareHttpSend(httpResponseMessage);
1865 TraceHttpSendStart();
1867 if (suppressEntityBody)
1869 // requests can't always support an output stream (for GET, etc)
1872 outputStream = GetOutputStream();
1876 this.SetContentLength(0);
1882 outputStream = this.GetOutputStream();
1883 ApplyChannelBinding();
1885 OpaqueContent content = httpResponseMessage.Content as OpaqueContent;
1886 if (content != null)
1888 content.WriteToStream(this.outputStream);
1892 if (!httpResponseMessage.Content.CopyToAsync(this.outputStream).Wait<CommunicationException>(timeout))
1894 throw FxTrace.Exception.AsError(new TimeoutException(SR.GetString(SR.TimeoutOnSend, timeout)));
1900 if (this.IsChannelBindingSupportEnabled)
1902 //need to get the Channel binding token (CBT), apply channel binding info to the message and then write the message
1903 //CBT is only enabled when message security is in the stack, which also requires an HTTP entity body, so we
1904 //should be safe to always get the stream.
1905 outputStream = this.GetOutputStream();
1907 ApplyChannelBinding();
1909 ArraySegment<byte> buffer = SerializeBufferedMessage(httpResponseMessage);
1911 Fx.Assert(buffer.Count != 0, "We should always have an entity body in this case...");
1912 outputStream.Write(buffer.Array, buffer.Offset, buffer.Count);
1916 ArraySegment<byte> buffer = SerializeBufferedMessage(httpResponseMessage);
1917 SetContentLength(buffer.Count);
1919 // requests can't always support an output stream (for GET, etc)
1920 if (!isRequest || buffer.Count > 0)
1922 outputStream = this.GetOutputStream();
1923 outputStream.Write(buffer.Array, buffer.Offset, buffer.Count);
1931 ArraySegment<byte> SerializeBufferedMessage(HttpResponseMessage httpResponseMessage)
1933 HttpOutputByteArrayContent content = httpResponseMessage.Content as HttpOutputByteArrayContent;
1934 if (content == null)
1936 byte[] byteArray = httpResponseMessage.Content.ReadAsByteArrayAsync().Result;
1937 return new ArraySegment<byte>(byteArray, 0, byteArray.Length);
1941 return content.Content;
1945 public void Send(TimeSpan timeout)
1947 bool suppressEntityBody = PrepareHttpSend(message);
1949 TraceHttpSendStart();
1951 if (suppressEntityBody)
1953 // requests can't always support an output stream (for GET, etc)
1956 outputStream = GetOutputStream();
1960 this.SetContentLength(0);
1966 outputStream = GetOutputStream();
1967 ApplyChannelBinding();
1968 WriteStreamedMessage(timeout);
1972 if (this.IsChannelBindingSupportEnabled)
1974 //need to get the Channel binding token (CBT), apply channel binding info to the message and then write the message
1975 //CBT is only enabled when message security is in the stack, which also requires an HTTP entity body, so we
1976 //should be safe to always get the stream.
1977 outputStream = GetOutputStream();
1979 ApplyChannelBinding();
1981 ArraySegment<byte> buffer = SerializeBufferedMessage(message);
1983 Fx.Assert(buffer.Count != 0, "We should always have an entity body in this case...");
1984 outputStream.Write(buffer.Array, buffer.Offset, buffer.Count);
1988 ArraySegment<byte> buffer = SerializeBufferedMessage(message);
1989 SetContentLength(buffer.Count);
1991 // requests can't always support an output stream (for GET, etc)
1992 if (!isRequest || buffer.Count > 0)
1994 outputStream = GetOutputStream();
1995 outputStream.Write(buffer.Array, buffer.Offset, buffer.Count);
2005 if (DiagnosticUtility.ShouldTraceInformation)
2007 TraceUtility.TraceEvent(TraceEventType.Information, TraceCode.MessageSent, SR.GetString(SR.TraceCodeMessageSent),
2008 new MessageTraceRecord(this.message), this, null);
2011 if (TD.HttpSendStopIsEnabled())
2013 TD.HttpSendStop(this.eventTraceActivity);
2017 class SendAsyncResult : AsyncResult
2019 HttpOutput httpOutput;
2020 static AsyncCallback onGetOutputStream;
2021 static Action<object> onWriteStreamedMessageLater;
2022 static AsyncCallback onWriteStreamedMessage;
2023 static AsyncCallback onWriteBody;
2024 bool suppressEntityBody;
2025 ArraySegment<byte> buffer;
2026 TimeoutHelper timeoutHelper;
2027 HttpResponseMessage httpResponseMessage;
2029 public SendAsyncResult(HttpOutput httpOutput, HttpResponseMessage httpResponseMessage, bool suppressEntityBody, TimeSpan timeout, AsyncCallback callback, object state)
2030 : base(callback, state)
2032 this.httpOutput = httpOutput;
2033 this.httpResponseMessage = httpResponseMessage;
2034 this.suppressEntityBody = suppressEntityBody;
2036 if (suppressEntityBody)
2038 if (httpOutput.isRequest)
2040 httpOutput.SetContentLength(0);
2041 this.httpOutput.TraceSend();
2042 this.httpOutput.LogMessage();
2043 base.Complete(true);
2048 this.timeoutHelper = new TimeoutHelper(timeout);
2054 if (httpOutput.IsChannelBindingSupportEnabled)
2056 SendWithChannelBindingToken();
2060 SendWithoutChannelBindingToken();
2064 void SendWithoutChannelBindingToken()
2066 if (!suppressEntityBody && !httpOutput.streamed)
2068 if (this.httpResponseMessage != null)
2070 buffer = httpOutput.SerializeBufferedMessage(this.httpResponseMessage);
2074 buffer = httpOutput.SerializeBufferedMessage(httpOutput.message);
2077 httpOutput.SetContentLength(buffer.Count);
2081 if (this.httpOutput.WillGetOutputStreamCompleteSynchronously)
2083 httpOutput.outputStream = httpOutput.GetOutputStream();
2087 if (onGetOutputStream == null)
2089 onGetOutputStream = Fx.ThunkCallback(new AsyncCallback(OnGetOutputStream));
2092 IAsyncResult result = httpOutput.BeginGetOutputStream(onGetOutputStream, this);
2094 if (!result.CompletedSynchronously)
2097 httpOutput.outputStream = httpOutput.EndGetOutputStream(result);
2100 if (WriteMessage(true))
2102 this.httpOutput.TraceSend();
2103 base.Complete(true);
2107 void SendWithChannelBindingToken()
2109 if (this.httpOutput.WillGetOutputStreamCompleteSynchronously)
2111 httpOutput.outputStream = httpOutput.GetOutputStream();
2112 httpOutput.ApplyChannelBinding();
2116 if (onGetOutputStream == null)
2118 onGetOutputStream = Fx.ThunkCallback(new AsyncCallback(OnGetOutputStream));
2121 IAsyncResult result = httpOutput.BeginGetOutputStream(onGetOutputStream, this);
2123 if (!result.CompletedSynchronously)
2126 httpOutput.outputStream = httpOutput.EndGetOutputStream(result);
2127 httpOutput.ApplyChannelBinding();
2130 if (!httpOutput.streamed)
2132 if (this.httpResponseMessage != null)
2134 buffer = httpOutput.SerializeBufferedMessage(this.httpResponseMessage);
2138 buffer = httpOutput.SerializeBufferedMessage(httpOutput.message);
2141 httpOutput.SetContentLength(buffer.Count);
2144 if (WriteMessage(true))
2146 this.httpOutput.TraceSend();
2147 base.Complete(true);
2151 bool WriteMessage(bool isStillSynchronous)
2153 if (suppressEntityBody)
2157 if (httpOutput.streamed)
2159 if (isStillSynchronous)
2161 if (onWriteStreamedMessageLater == null)
2163 onWriteStreamedMessageLater = new Action<object>(OnWriteStreamedMessageLater);
2165 ActionItem.Schedule(onWriteStreamedMessageLater, this);
2170 return WriteStreamedMessage();
2175 if (onWriteBody == null)
2177 onWriteBody = Fx.ThunkCallback(new AsyncCallback(OnWriteBody));
2180 IAsyncResult writeResult =
2181 httpOutput.outputStream.BeginWrite(buffer.Array, buffer.Offset, buffer.Count, onWriteBody, this);
2183 if (!writeResult.CompletedSynchronously)
2188 CompleteWriteBody(writeResult);
2194 bool WriteStreamedMessage()
2196 // return a bool to determine if we are sync.
2198 if (onWriteStreamedMessage == null)
2200 onWriteStreamedMessage = Fx.ThunkCallback(OnWriteStreamedMessage);
2203 return HandleWriteStreamedMessage(null); // completed synchronously
2206 bool HandleWriteStreamedMessage(IAsyncResult result)
2210 result = httpOutput.BeginWriteStreamedMessage(this.httpResponseMessage, timeoutHelper.RemainingTime(), onWriteStreamedMessage, this);
2211 if (!result.CompletedSynchronously)
2217 httpOutput.EndWriteStreamedMessage(result);
2221 static void OnWriteStreamedMessage(IAsyncResult result)
2223 if (result.CompletedSynchronously)
2228 SendAsyncResult thisPtr = (SendAsyncResult)result.AsyncState;
2229 Exception completionException = null;
2230 bool completeSelf = false;
2234 completeSelf = thisPtr.HandleWriteStreamedMessage(result);
2236 catch (Exception ex)
2242 completeSelf = true;
2243 completionException = ex;
2248 if (completionException != null)
2250 thisPtr.httpOutput.TraceSend();
2252 thisPtr.Complete(false, completionException);
2256 void CompleteWriteBody(IAsyncResult result)
2258 httpOutput.outputStream.EndWrite(result);
2261 public static void End(IAsyncResult result)
2263 AsyncResult.End<SendAsyncResult>(result);
2266 static void OnGetOutputStream(IAsyncResult result)
2268 if (result.CompletedSynchronously)
2271 SendAsyncResult thisPtr = (SendAsyncResult)result.AsyncState;
2273 Exception completionException = null;
2274 bool completeSelf = false;
2277 thisPtr.httpOutput.outputStream = thisPtr.httpOutput.EndGetOutputStream(result);
2278 thisPtr.httpOutput.ApplyChannelBinding();
2280 if (!thisPtr.httpOutput.streamed && thisPtr.httpOutput.IsChannelBindingSupportEnabled)
2282 thisPtr.buffer = thisPtr.httpOutput.SerializeBufferedMessage(thisPtr.httpOutput.message);
2283 thisPtr.httpOutput.SetContentLength(thisPtr.buffer.Count);
2286 if (thisPtr.WriteMessage(false))
2288 thisPtr.httpOutput.TraceSend();
2289 completeSelf = true;
2292 #pragma warning suppress 56500 // Microsoft, transferring exception to another thread
2299 completeSelf = true;
2300 completionException = e;
2304 thisPtr.Complete(false, completionException);
2308 static void OnWriteStreamedMessageLater(object state)
2310 SendAsyncResult thisPtr = (SendAsyncResult)state;
2312 bool completeSelf = false;
2313 Exception completionException = null;
2316 completeSelf = thisPtr.WriteStreamedMessage();
2318 #pragma warning suppress 56500 // Microsoft, transferring exception to another thread
2325 completeSelf = true;
2326 completionException = e;
2331 if (completionException != null)
2333 thisPtr.httpOutput.TraceSend();
2335 thisPtr.Complete(false, completionException);
2339 static void OnWriteBody(IAsyncResult result)
2341 if (result.CompletedSynchronously)
2344 SendAsyncResult thisPtr = (SendAsyncResult)result.AsyncState;
2346 Exception completionException = null;
2349 thisPtr.CompleteWriteBody(result);
2350 thisPtr.httpOutput.TraceSend();
2352 #pragma warning suppress 56500 // Microsoft, transferring exception to another thread
2359 completionException = e;
2361 thisPtr.Complete(false, completionException);
2365 internal static HttpOutput CreateHttpOutput(HttpWebRequest httpWebRequest, IHttpTransportFactorySettings settings, Message message, bool enableChannelBindingSupport)
2367 return new WebRequestHttpOutput(httpWebRequest, settings, message, enableChannelBindingSupport);
2370 internal static HttpOutput CreateHttpOutput(HttpListenerResponse httpListenerResponse, IHttpTransportFactorySettings settings, Message message, string httpMethod)
2372 return new ListenerResponseHttpOutput(httpListenerResponse, settings, message, httpMethod);
2375 class WebRequestHttpOutput : HttpOutput
2377 HttpWebRequest httpWebRequest;
2378 ChannelBinding channelBindingToken;
2379 bool enableChannelBindingSupport;
2381 public WebRequestHttpOutput(HttpWebRequest httpWebRequest, IHttpTransportFactorySettings settings, Message message, bool enableChannelBindingSupport)
2382 : base(settings, message, true, false)
2384 this.httpWebRequest = httpWebRequest;
2385 this.enableChannelBindingSupport = enableChannelBindingSupport;
2388 public override void Abort(HttpAbortReason abortReason)
2390 httpWebRequest.Abort();
2391 base.Abort(abortReason);
2394 protected override void AddMimeVersion(string version)
2396 httpWebRequest.Headers[HttpChannelUtilities.MIMEVersionHeader] = version;
2399 protected override void AddHeader(string name, string value)
2401 httpWebRequest.Headers.Add(name, value);
2404 protected override void SetContentType(string contentType)
2406 httpWebRequest.ContentType = contentType;
2409 protected override void SetContentEncoding(string contentEncoding)
2411 this.httpWebRequest.Headers.Add(HttpChannelUtilities.ContentEncodingHeader, contentEncoding);
2414 protected override void SetContentLength(int contentLength)
2416 if (contentLength == 0 // work around whidbey issue with setting ContentLength - (see MB36881)
2417 && !this.enableChannelBindingSupport) //When ChannelBinding is enabled, content length isn't supported
2419 httpWebRequest.ContentLength = contentLength;
2423 protected override void SetStatusCode(HttpStatusCode statusCode)
2427 protected override void SetStatusDescription(string statusDescription)
2431 protected override bool WillGetOutputStreamCompleteSynchronously
2433 get { return false; }
2436 protected override bool IsChannelBindingSupportEnabled
2440 return this.enableChannelBindingSupport;
2444 protected override ChannelBinding ChannelBinding
2448 return this.channelBindingToken;
2452 protected override bool CleanupChannelBinding
2456 //client side channel binding token will be attached to the inbound response message also, so
2457 //we need to not clean up the CBT object for this HttpOutput object.
2462 //Used to allow the channel binding object to be transferred to the
2463 //WebResponseHttpInput object.
2464 public override ChannelBinding TakeChannelBinding()
2466 ChannelBinding result = this.channelBindingToken;
2467 this.channelBindingToken = null;
2471 protected override Stream GetOutputStream()
2475 Stream outputStream;
2476 if (this.IsChannelBindingSupportEnabled)
2478 TransportContext context;
2479 outputStream = httpWebRequest.GetRequestStream(out context);
2480 this.channelBindingToken = ChannelBindingUtility.GetToken(context);
2484 outputStream = httpWebRequest.GetRequestStream();
2487 outputStream = new WebRequestOutputStream(outputStream, httpWebRequest, this);
2489 return outputStream;
2491 catch (WebException webException)
2493 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateRequestWebException(webException, httpWebRequest, abortReason));
2497 protected override IAsyncResult BeginGetOutputStream(AsyncCallback callback, object state)
2499 return new GetOutputStreamAsyncResult(httpWebRequest, this, callback, state);
2502 protected override Stream EndGetOutputStream(IAsyncResult result)
2504 return GetOutputStreamAsyncResult.End(result, out this.channelBindingToken);
2507 protected override bool PrepareHttpSend(Message message)
2509 bool wasContentTypeSet = false;
2511 string action = message.Headers.Action;
2515 //This code is calling UrlPathEncode due to MessageBus bug 53362.
2516 //After reviewing this decision, we
2517 //feel that this was probably the wrong thing to do because UrlPathEncode
2518 //doesn't escape some characters like '+', '%', etc. The real issue behind
2519 //bug 53362 may have been as simple as being encoded multiple times on the client
2520 //but being decoded one time on the server. Calling UrlEncode would correctly
2521 //escape these characters, but since we don't want to break any customers and no
2522 //customers have complained, we will leave this as is for now...
2523 action = string.Format(CultureInfo.InvariantCulture, "\"{0}\"", UrlUtility.UrlPathEncode(action));
2526 bool suppressEntityBody = base.PrepareHttpSend(message);
2529 if (message.Properties.TryGetValue(HttpRequestMessageProperty.Name, out property))
2531 HttpRequestMessageProperty requestProperty = (HttpRequestMessageProperty)property;
2532 httpWebRequest.Method = requestProperty.Method;
2533 // Query string was applied in HttpChannelFactory.ApplyManualAddressing
2534 WebHeaderCollection requestHeaders = requestProperty.Headers;
2535 suppressEntityBody = suppressEntityBody || requestProperty.SuppressEntityBody;
2536 for (int i = 0; i < requestHeaders.Count; i++)
2538 string name = requestHeaders.Keys[i];
2539 string value = requestHeaders[i];
2540 if (string.Compare(name, "accept", StringComparison.OrdinalIgnoreCase) == 0)
2542 httpWebRequest.Accept = value;
2544 else if (string.Compare(name, "connection", StringComparison.OrdinalIgnoreCase) == 0)
2546 if (value.IndexOf("keep-alive", StringComparison.OrdinalIgnoreCase) != -1)
2548 httpWebRequest.KeepAlive = true;
2552 httpWebRequest.Connection = value;
2555 else if (string.Compare(name, "SOAPAction", StringComparison.OrdinalIgnoreCase) == 0)
2563 if (value.Length > 0 && string.Compare(value, action, StringComparison.Ordinal) != 0)
2565 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
2566 new ProtocolException(SR.GetString(SR.HttpSoapActionMismatch, action, value)));
2570 else if (string.Compare(name, "content-length", StringComparison.OrdinalIgnoreCase) == 0)
2572 // this will be taken care of by System.Net when we write to the content
2574 else if (string.Compare(name, "content-type", StringComparison.OrdinalIgnoreCase) == 0)
2576 httpWebRequest.ContentType = value;
2577 wasContentTypeSet = true;
2579 else if (string.Compare(name, "expect", StringComparison.OrdinalIgnoreCase) == 0)
2581 if (value.ToUpperInvariant().IndexOf("100-CONTINUE", StringComparison.OrdinalIgnoreCase) != -1)
2583 httpWebRequest.ServicePoint.Expect100Continue = true;
2587 httpWebRequest.Expect = value;
2590 else if (string.Compare(name, "host", StringComparison.OrdinalIgnoreCase) == 0)
2592 // this should be controlled through Via
2594 else if (string.Compare(name, "referer", StringComparison.OrdinalIgnoreCase) == 0)
2596 // referrer is proper spelling, but referer is the what is in the protocol.
2597 httpWebRequest.Referer = value;
2599 else if (string.Compare(name, "transfer-encoding", StringComparison.OrdinalIgnoreCase) == 0)
2601 if (value.ToUpperInvariant().IndexOf("CHUNKED", StringComparison.OrdinalIgnoreCase) != -1)
2603 httpWebRequest.SendChunked = true;
2607 httpWebRequest.TransferEncoding = value;
2610 else if (string.Compare(name, "user-agent", StringComparison.OrdinalIgnoreCase) == 0)
2612 httpWebRequest.UserAgent = value;
2614 else if (string.Compare(name, "if-modified-since", StringComparison.OrdinalIgnoreCase) == 0)
2616 DateTime modifiedSinceDate;
2617 if (DateTime.TryParse(value, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AllowWhiteSpaces | DateTimeStyles.AssumeLocal, out modifiedSinceDate))
2619 httpWebRequest.IfModifiedSince = modifiedSinceDate;
2623 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
2624 new ProtocolException(SR.GetString(SR.HttpIfModifiedSinceParseError, value)));
2627 else if (string.Compare(name, "date", StringComparison.OrdinalIgnoreCase) == 0)
2629 // this will be taken care of by System.Net when we make the request
2631 else if (string.Compare(name, "proxy-connection", StringComparison.OrdinalIgnoreCase) == 0)
2633 // set by System.Net if using a proxy.
2635 else if (string.Compare(name, "range", StringComparison.OrdinalIgnoreCase) == 0)
2637 // we don't support ranges in v1.
2641 httpWebRequest.Headers.Add(name, value);
2648 if (message.Version.Envelope == EnvelopeVersion.Soap11)
2650 httpWebRequest.Headers["SOAPAction"] = action;
2652 else if (message.Version.Envelope == EnvelopeVersion.Soap12)
2654 if (message.Version.Addressing == AddressingVersion.None)
2656 bool shouldSetContentType = true;
2657 if (wasContentTypeSet)
2659 if (httpWebRequest.ContentType.Contains("action")
2660 || httpWebRequest.ContentType.ToUpperInvariant().IndexOf("ACTION", StringComparison.OrdinalIgnoreCase) != -1)
2664 ContentType parsedContentType = new ContentType(httpWebRequest.ContentType);
2665 if (parsedContentType.Parameters.ContainsKey("action"))
2667 string value = string.Format(CultureInfo.InvariantCulture, "\"{0}\"", parsedContentType.Parameters["action"]);
2668 if (string.Compare(value, action, StringComparison.Ordinal) != 0)
2670 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
2671 new ProtocolException(SR.GetString(SR.HttpSoapActionMismatchContentType, action, value)));
2673 shouldSetContentType = false;
2676 catch (FormatException formatException)
2678 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
2679 new ProtocolException(SR.GetString(SR.HttpContentTypeFormatException, formatException.Message, httpWebRequest.ContentType), formatException));
2684 if (shouldSetContentType)
2686 httpWebRequest.ContentType = string.Format(CultureInfo.InvariantCulture, "{0}; action={1}", httpWebRequest.ContentType, action);
2690 else if (message.Version.Envelope != EnvelopeVersion.None)
2692 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
2693 new ProtocolException(SR.GetString(SR.EnvelopeVersionUnknown,
2694 message.Version.Envelope.ToString())));
2698 // since we don't get the output stream in send when retVal == true,
2699 // we need to disable chunking for some verbs (DELETE/PUT)
2700 if (suppressEntityBody)
2702 httpWebRequest.SendChunked = false;
2704 else if (this.IsChannelBindingSupportEnabled)
2706 //force chunked upload since the length of the message is unknown before encoding.
2707 httpWebRequest.SendChunked = true;
2710 return suppressEntityBody;
2713 protected override void PrepareHttpSendCore(HttpResponseMessage message)
2715 // HTTP pipeline for client side is not implemented yet
2716 // DCR CSDMain 216853 is tracking this
2717 Fx.Assert(false, "HTTP pipeline for client is not implemented yet. This method should not be called.");
2720 class GetOutputStreamAsyncResult : AsyncResult
2722 static AsyncCallback onGetRequestStream = Fx.ThunkCallback(new AsyncCallback(OnGetRequestStream));
2723 HttpOutput httpOutput;
2724 HttpWebRequest httpWebRequest;
2725 Stream outputStream;
2726 ChannelBinding channelBindingToken;
2728 public GetOutputStreamAsyncResult(HttpWebRequest httpWebRequest, HttpOutput httpOutput, AsyncCallback callback, object state)
2729 : base(callback, state)
2731 this.httpWebRequest = httpWebRequest;
2732 this.httpOutput = httpOutput;
2734 IAsyncResult result = null;
2737 result = httpWebRequest.BeginGetRequestStream(onGetRequestStream, this);
2739 catch (WebException webException)
2741 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateRequestWebException(webException, httpWebRequest, httpOutput.abortReason));
2744 if (result.CompletedSynchronously)
2746 CompleteGetRequestStream(result);
2747 base.Complete(true);
2751 void CompleteGetRequestStream(IAsyncResult result)
2755 TransportContext context;
2756 this.outputStream = new WebRequestOutputStream(httpWebRequest.EndGetRequestStream(result, out context), httpWebRequest, this.httpOutput);
2757 this.channelBindingToken = ChannelBindingUtility.GetToken(context);
2759 catch (WebException webException)
2761 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateRequestWebException(webException, httpWebRequest, httpOutput.abortReason));
2765 public static Stream End(IAsyncResult result, out ChannelBinding channelBindingToken)
2767 GetOutputStreamAsyncResult thisPtr = AsyncResult.End<GetOutputStreamAsyncResult>(result);
2768 channelBindingToken = thisPtr.channelBindingToken;
2769 return thisPtr.outputStream;
2772 static void OnGetRequestStream(IAsyncResult result)
2774 if (result.CompletedSynchronously)
2777 GetOutputStreamAsyncResult thisPtr = (GetOutputStreamAsyncResult)result.AsyncState;
2779 Exception completionException = null;
2782 thisPtr.CompleteGetRequestStream(result);
2784 #pragma warning suppress 56500 // Microsoft, transferring exception to another thread
2791 completionException = e;
2793 thisPtr.Complete(false, completionException);
2797 class WebRequestOutputStream : BytesReadPositionStream
2799 HttpWebRequest httpWebRequest;
2800 HttpOutput httpOutput;
2803 public WebRequestOutputStream(Stream requestStream, HttpWebRequest httpWebRequest, HttpOutput httpOutput)
2804 : base(requestStream)
2806 this.httpWebRequest = httpWebRequest;
2807 this.httpOutput = httpOutput;
2810 public override void Close()
2816 catch (ObjectDisposedException objectDisposedException)
2818 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateRequestCanceledException(objectDisposedException, httpWebRequest, httpOutput.abortReason));
2820 catch (IOException ioException)
2822 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateRequestIOException(ioException, httpWebRequest));
2824 catch (WebException webException)
2826 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateRequestWebException(webException, httpWebRequest, httpOutput.abortReason));
2830 public override long Position
2838 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.SeekNotSupported)));
2842 public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
2844 this.bytesSent += count;
2847 return base.BeginWrite(buffer, offset, count, callback, state);
2849 catch (ObjectDisposedException objectDisposedException)
2851 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateRequestCanceledException(objectDisposedException, httpWebRequest, httpOutput.abortReason));
2853 catch (IOException ioException)
2855 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateRequestIOException(ioException, httpWebRequest));
2857 catch (WebException webException)
2859 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateRequestWebException(webException, httpWebRequest, httpOutput.abortReason));
2863 public override void EndWrite(IAsyncResult result)
2867 base.EndWrite(result);
2869 catch (ObjectDisposedException objectDisposedException)
2871 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateRequestCanceledException(objectDisposedException, httpWebRequest, httpOutput.abortReason));
2873 catch (IOException ioException)
2875 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateRequestIOException(ioException, httpWebRequest));
2877 catch (WebException webException)
2879 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateRequestWebException(webException, httpWebRequest, httpOutput.abortReason));
2883 public override void Write(byte[] buffer, int offset, int count)
2887 base.Write(buffer, offset, count);
2889 catch (ObjectDisposedException objectDisposedException)
2891 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateRequestCanceledException(objectDisposedException, httpWebRequest, httpOutput.abortReason));
2893 catch (IOException ioException)
2895 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateRequestIOException(ioException, httpWebRequest));
2897 catch (WebException webException)
2899 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateRequestWebException(webException, httpWebRequest, httpOutput.abortReason));
2901 this.bytesSent += count;
2906 class ListenerResponseHttpOutput : HttpOutput
2908 HttpListenerResponse listenerResponse;
2911 [System.Diagnostics.CodeAnalysis.SuppressMessage(FxCop.Category.Usage, "CA2214", Justification = "No one else is inhiriting from this class.")]
2912 public ListenerResponseHttpOutput(HttpListenerResponse listenerResponse, IHttpTransportFactorySettings settings, Message message, string httpMethod)
2913 : base(settings, message, false, true)
2915 this.listenerResponse = listenerResponse;
2916 this.httpMethod = httpMethod;
2918 if (message.IsFault)
2920 this.SetStatusCode(HttpStatusCode.InternalServerError);
2924 this.SetStatusCode(HttpStatusCode.OK);
2928 protected override string HttpMethod
2930 get { return this.httpMethod; }
2933 public override void Abort(HttpAbortReason abortReason)
2935 listenerResponse.Abort();
2936 base.Abort(abortReason);
2939 protected override void AddMimeVersion(string version)
2941 listenerResponse.Headers[HttpChannelUtilities.MIMEVersionHeader] = version;
2944 protected override bool PrepareHttpSend(Message message)
2946 bool result = base.PrepareHttpSend(message);
2948 if (this.CanSendCompressedResponses)
2950 string contentType = this.listenerResponse.ContentType;
2951 string contentEncoding;
2952 if (HttpChannelUtilities.GetHttpResponseTypeAndEncodingForCompression(ref contentType, out contentEncoding))
2954 if (contentType != this.listenerResponse.ContentType)
2956 this.SetContentType(contentType);
2958 this.SetContentEncoding(contentEncoding);
2962 HttpResponseMessageProperty responseProperty = message.Properties.GetValue<HttpResponseMessageProperty>(HttpResponseMessageProperty.Name, true);
2963 bool httpResponseMessagePropertyFound = responseProperty != null;
2964 bool httpMethodIsHead = string.Compare(this.httpMethod, "HEAD", StringComparison.OrdinalIgnoreCase) == 0;
2966 if (httpMethodIsHead ||
2967 httpResponseMessagePropertyFound && responseProperty.SuppressEntityBody)
2970 this.SetContentLength(0);
2971 this.SetContentType(null);
2972 listenerResponse.SendChunked = false;
2975 if (httpResponseMessagePropertyFound)
2977 this.SetStatusCode(responseProperty.StatusCode);
2978 if (responseProperty.StatusDescription != null)
2980 this.SetStatusDescription(responseProperty.StatusDescription);
2983 WebHeaderCollection responseHeaders = responseProperty.Headers;
2984 for (int i = 0; i < responseHeaders.Count; i++)
2986 string name = responseHeaders.Keys[i];
2987 string value = responseHeaders[i];
2988 if (string.Compare(name, "content-length", StringComparison.OrdinalIgnoreCase) == 0)
2990 int contentLength = -1;
2991 if (httpMethodIsHead &&
2992 int.TryParse(value, out contentLength))
2994 this.SetContentLength(contentLength);
2997 //this will be taken care of by System.Net when we write to the content
2999 else if (string.Compare(name, "content-type", StringComparison.OrdinalIgnoreCase) == 0)
3001 if (httpMethodIsHead ||
3002 !responseProperty.SuppressEntityBody)
3004 this.SetContentType(value);
3007 else if (string.Compare(name, "Connection", StringComparison.OrdinalIgnoreCase) == 0 &&
3009 string.Compare(value.Trim(), "close", StringComparison.OrdinalIgnoreCase) == 0 &&
3010 !LocalAppContextSwitches.DisableExplicitConnectionCloseHeader)
3012 // HttpListenerResponse will not serialize the Connection:close header
3013 // if its KeepAlive is true. So in the case where a service has explicitly
3014 // added Connection:close (not added by default) set KeepAlive to false.
3015 // This will cause HttpListenerResponse to add its own Connection:close header
3016 // and to serialize it properly. We do not add a redundant header here.
3017 this.listenerResponse.KeepAlive = false;
3021 this.AddHeader(name, value);
3029 protected override void PrepareHttpSendCore(HttpResponseMessage message)
3031 this.listenerResponse.StatusCode = (int)message.StatusCode;
3032 if (message.ReasonPhrase != null)
3034 this.listenerResponse.StatusDescription = message.ReasonPhrase;
3036 HttpChannelUtilities.CopyHeaders(message, AddHeader);
3039 protected override void AddHeader(string name, string value)
3041 if (string.Compare(name, "WWW-Authenticate", StringComparison.OrdinalIgnoreCase) == 0)
3043 listenerResponse.AddHeader(name, value);
3047 listenerResponse.AppendHeader(name, value);
3051 protected override void SetContentType(string contentType)
3053 listenerResponse.ContentType = contentType;
3056 protected override void SetContentEncoding(string contentEncoding)
3058 this.listenerResponse.AddHeader(HttpChannelUtilities.ContentEncodingHeader, contentEncoding);
3061 protected override void SetContentLength(int contentLength)
3063 listenerResponse.ContentLength64 = contentLength;
3066 protected override void SetStatusCode(HttpStatusCode statusCode)
3068 listenerResponse.StatusCode = (int)statusCode;
3071 protected override void SetStatusDescription(string statusDescription)
3073 listenerResponse.StatusDescription = statusDescription;
3076 protected override Stream GetOutputStream()
3078 return new ListenerResponseOutputStream(listenerResponse);
3081 class ListenerResponseOutputStream : BytesReadPositionStream
3083 public ListenerResponseOutputStream(HttpListenerResponse listenerResponse)
3084 : base(listenerResponse.OutputStream)
3088 public override void Close()
3094 catch (HttpListenerException listenerException)
3096 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
3097 HttpChannelUtilities.CreateCommunicationException(listenerException));
3101 public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
3105 return base.BeginWrite(buffer, offset, count, callback, state);
3107 catch (HttpListenerException listenerException)
3109 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
3110 HttpChannelUtilities.CreateCommunicationException(listenerException));
3112 catch (ApplicationException applicationException)
3114 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
3115 new CommunicationObjectAbortedException(SR.GetString(SR.HttpResponseAborted),
3116 applicationException));
3120 public override void EndWrite(IAsyncResult result)
3124 base.EndWrite(result);
3126 catch (HttpListenerException listenerException)
3128 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
3129 HttpChannelUtilities.CreateCommunicationException(listenerException));
3131 catch (ApplicationException applicationException)
3133 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
3134 new CommunicationObjectAbortedException(SR.GetString(SR.HttpResponseAborted),
3135 applicationException));
3139 public override void Write(byte[] buffer, int offset, int count)
3143 base.Write(buffer, offset, count);
3145 catch (HttpListenerException listenerException)
3147 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
3148 HttpChannelUtilities.CreateCommunicationException(listenerException));
3150 catch (ApplicationException applicationException)
3152 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
3153 new CommunicationObjectAbortedException(SR.GetString(SR.HttpResponseAborted),
3154 applicationException));
3161 enum HttpAbortReason
3168 delegate void AddHeaderDelegate(string headerName, string headerValue);
3170 static class HttpChannelUtilities
3172 internal static class StatusDescriptionStrings
3174 internal const string HttpContentTypeMissing = "Missing Content Type";
3175 internal const string HttpContentTypeMismatch = "Cannot process the message because the content type '{0}' was not the expected type '{1}'.";
3176 internal const string HttpStatusServiceActivationException = "System.ServiceModel.ServiceActivationException";
3179 internal static class ObsoleteDescriptionStrings
3181 internal const string PropertyObsoleteUseAllowCookies = "This property is obsolete. To enable Http CookieContainer, use the AllowCookies property instead.";
3182 internal const string TypeObsoleteUseAllowCookies = "This type is obsolete. To enable the Http CookieContainer, use the AllowCookies property on the http binding or on the HttpTransportBindingElement.";
3185 internal const string HttpStatusCodeKey = "HttpStatusCode";
3186 internal const string HttpStatusCodeExceptionKey = "System.ServiceModel.Channels.HttpInput.HttpStatusCode";
3187 internal const string HttpStatusDescriptionExceptionKey = "System.ServiceModel.Channels.HttpInput.HttpStatusDescription";
3189 internal const int ResponseStreamExcerptSize = 1024;
3191 internal const string MIMEVersionHeader = "MIME-Version";
3193 internal const string ContentEncodingHeader = "Content-Encoding";
3194 internal const string AcceptEncodingHeader = "Accept-Encoding";
3196 private const string ContentLengthHeader = "Content-Length";
3197 private static readonly HashSet<string> httpContentHeaders = new HashSet<string>()
3199 "Allow", "Content-Encoding", "Content-Language", "Content-Location", "Content-MD5",
3200 "Content-Range", "Expires", "Last-Modified", "Content-Type", ContentLengthHeader
3203 static bool allReferencedAssembliesLoaded = false;
3205 public static Exception CreateCommunicationException(HttpListenerException listenerException)
3207 switch (listenerException.NativeErrorCode)
3209 case UnsafeNativeMethods.ERROR_NO_TRACKING_SERVICE:
3210 return new CommunicationException(SR.GetString(SR.HttpNoTrackingService, listenerException.Message), listenerException);
3212 case UnsafeNativeMethods.ERROR_NETNAME_DELETED:
3213 return new CommunicationException(SR.GetString(SR.HttpNetnameDeleted, listenerException.Message), listenerException);
3215 case UnsafeNativeMethods.ERROR_INVALID_HANDLE:
3216 return new CommunicationObjectAbortedException(SR.GetString(SR.HttpResponseAborted), listenerException);
3218 case UnsafeNativeMethods.ERROR_NOT_ENOUGH_MEMORY:
3219 case UnsafeNativeMethods.ERROR_OUTOFMEMORY:
3220 case UnsafeNativeMethods.ERROR_NO_SYSTEM_RESOURCES:
3221 return new InsufficientMemoryException(SR.GetString(SR.InsufficentMemory), listenerException);
3224 return new CommunicationException(listenerException.Message, listenerException);
3228 public static void EnsureHttpRequestMessageContentNotNull(HttpRequestMessage httpRequestMessage)
3230 if (httpRequestMessage.Content == null)
3232 httpRequestMessage.Content = new ByteArrayContent(EmptyArray<byte>.Instance);
3236 public static void EnsureHttpResponseMessageContentNotNull(HttpResponseMessage httpResponseMessage)
3238 if (httpResponseMessage.Content == null)
3240 httpResponseMessage.Content = new ByteArrayContent(EmptyArray<byte>.Instance);
3244 public static bool IsEmpty(HttpResponseMessage httpResponseMessage)
3246 return httpResponseMessage.Content == null
3247 || (httpResponseMessage.Content.Headers.ContentLength.HasValue && httpResponseMessage.Content.Headers.ContentLength.Value == 0);
3250 internal static void HandleContinueWithTask(Task task)
3252 HandleContinueWithTask(task, null);
3255 internal static void HandleContinueWithTask(Task task, Action<Exception> exceptionHandler)
3259 if (exceptionHandler == null)
3261 throw FxTrace.Exception.AsError<FaultException>(task.Exception);
3265 exceptionHandler.Invoke(task.Exception);
3268 else if (task.IsCanceled)
3270 throw FxTrace.Exception.AsError(new TimeoutException(SR.GetString(SR.TaskCancelledError)));
3274 public static void AbortRequest(HttpWebRequest request)
3279 public static void SetRequestTimeout(HttpWebRequest request, TimeSpan timeout)
3281 int millisecondsTimeout = TimeoutHelper.ToMilliseconds(timeout);
3282 if (millisecondsTimeout == 0)
3284 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new TimeoutException(SR.GetString(
3285 SR.HttpRequestTimedOut, request.RequestUri, timeout)));
3287 request.Timeout = millisecondsTimeout;
3288 request.ReadWriteTimeout = millisecondsTimeout;
3291 public static void AddReplySecurityProperty(HttpChannelFactory<IRequestChannel> factory, HttpWebRequest webRequest,
3292 HttpWebResponse webResponse, Message replyMessage)
3294 SecurityMessageProperty securityProperty = factory.CreateReplySecurityProperty(webRequest, webResponse);
3295 if (securityProperty != null)
3297 replyMessage.Properties.Security = securityProperty;
3301 public static void CopyHeaders(HttpRequestMessage request, AddHeaderDelegate addHeader)
3303 HttpChannelUtilities.CopyHeaders(request.Headers, addHeader);
3304 if (request.Content != null)
3306 HttpChannelUtilities.CopyHeaders(request.Content.Headers, addHeader);
3310 public static void CopyHeaders(HttpResponseMessage response, AddHeaderDelegate addHeader)
3312 HttpChannelUtilities.CopyHeaders(response.Headers, addHeader);
3313 if (response.Content != null)
3315 HttpChannelUtilities.CopyHeaders(response.Content.Headers, addHeader);
3319 static void CopyHeaders(HttpHeaders headers, AddHeaderDelegate addHeader)
3321 foreach (KeyValuePair<string, IEnumerable<string>> header in headers)
3323 foreach (string value in header.Value)
3325 TryAddToCollection(addHeader, header.Key, value);
3330 public static void CopyHeaders(NameValueCollection headers, AddHeaderDelegate addHeader)
3332 //this nested loop logic was copied from NameValueCollection.Add(NameValueCollection)
3333 int count = headers.Count;
3334 for (int i = 0; i < count; i++)
3336 string key = headers.GetKey(i);
3338 string[] values = headers.GetValues(i);
3341 for (int j = 0; j < values.Length; j++)
3343 TryAddToCollection(addHeader, key, values[j]);
3348 addHeader(key, null);
3353 public static void CopyHeadersToNameValueCollection(NameValueCollection headers, NameValueCollection destination)
3355 CopyHeaders(headers, destination.Add);
3358 [System.Diagnostics.CodeAnalysis.SuppressMessage(FxCop.Category.ReliabilityBasic, "Reliability104",
3359 Justification = "The exceptions are traced already.")]
3360 static void TryAddToCollection(AddHeaderDelegate addHeader, string headerName, string value)
3364 addHeader(headerName, value);
3366 catch (ArgumentException ex)
3368 string encodedValue = null;
3369 if (TryEncodeHeaderValueAsUri(headerName, value, out encodedValue))
3371 //note: if the hosthame of a referer header contains illegal chars, we will still throw from here
3372 //because Uri will not fix this up for us, which is ok. The request will get rejected in the error code path.
3373 addHeader(headerName, encodedValue);
3377 // In self-hosted scenarios, some of the headers like Content-Length cannot be added directly.
3378 // It will throw ArgumentException instead.
3379 FxTrace.Exception.AsInformation(ex);
3384 static bool TryEncodeHeaderValueAsUri(string headerName, string value, out string result)
3387 //Internet Explorer will send the referrer header on the wire in unicode without encoding it
3388 //this will cause errors when added to a WebHeaderCollection. This is a workaround for sharepoint,
3389 //but will only work for WebHosted Scenarios.
3390 if (String.Compare(headerName, "Referer", StringComparison.OrdinalIgnoreCase) == 0)
3393 if (Uri.TryCreate(value, UriKind.RelativeOrAbsolute, out uri))
3395 if (uri.IsAbsoluteUri)
3397 result = uri.AbsoluteUri;
3401 result = uri.GetComponents(UriComponents.SerializationInfoString, UriFormat.UriEscaped);
3410 internal static Type GetTypeFromAssembliesInCurrentDomain(string typeString)
3412 Type type = Type.GetType(typeString, false);
3415 if (!allReferencedAssembliesLoaded)
3417 allReferencedAssembliesLoaded = true;
3418 AspNetEnvironment.Current.EnsureAllReferencedAssemblyLoaded();
3421 Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
3422 for (int i = 0; i < assemblies.Length; i++)
3424 type = assemblies[i].GetType(typeString, false);
3435 public static NetworkCredential GetCredential(AuthenticationSchemes authenticationScheme,
3436 SecurityTokenProviderContainer credentialProvider, TimeSpan timeout,
3437 out TokenImpersonationLevel impersonationLevel, out AuthenticationLevel authenticationLevel)
3439 impersonationLevel = TokenImpersonationLevel.None;
3440 authenticationLevel = AuthenticationLevel.None;
3442 NetworkCredential result = null;
3444 if (authenticationScheme != AuthenticationSchemes.Anonymous)
3446 result = GetCredentialCore(authenticationScheme, credentialProvider, timeout, out impersonationLevel, out authenticationLevel);
3452 [MethodImpl(MethodImplOptions.NoInlining)]
3453 static NetworkCredential GetCredentialCore(AuthenticationSchemes authenticationScheme,
3454 SecurityTokenProviderContainer credentialProvider, TimeSpan timeout,
3455 out TokenImpersonationLevel impersonationLevel, out AuthenticationLevel authenticationLevel)
3457 impersonationLevel = TokenImpersonationLevel.None;
3458 authenticationLevel = AuthenticationLevel.None;
3460 NetworkCredential result = null;
3462 switch (authenticationScheme)
3464 case AuthenticationSchemes.Basic:
3465 result = TransportSecurityHelpers.GetUserNameCredential(credentialProvider, timeout);
3466 impersonationLevel = TokenImpersonationLevel.Delegation;
3469 case AuthenticationSchemes.Digest:
3470 result = TransportSecurityHelpers.GetSspiCredential(credentialProvider, timeout,
3471 out impersonationLevel, out authenticationLevel);
3473 HttpChannelUtilities.ValidateDigestCredential(ref result, impersonationLevel);
3476 case AuthenticationSchemes.Negotiate:
3477 result = TransportSecurityHelpers.GetSspiCredential(credentialProvider, timeout,
3478 out impersonationLevel, out authenticationLevel);
3481 case AuthenticationSchemes.Ntlm:
3482 result = TransportSecurityHelpers.GetSspiCredential(credentialProvider, timeout,
3483 out impersonationLevel, out authenticationLevel);
3484 if (authenticationLevel == AuthenticationLevel.MutualAuthRequired)
3486 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
3487 new InvalidOperationException(SR.GetString(SR.CredentialDisallowsNtlm)));
3492 // The setter for this property should prevent this.
3493 throw Fx.AssertAndThrow("GetCredential: Invalid authentication scheme");
3500 public static HttpWebResponse ProcessGetResponseWebException(WebException webException, HttpWebRequest request, HttpAbortReason abortReason)
3502 HttpWebResponse response = null;
3504 if (webException.Status == WebExceptionStatus.Success ||
3505 webException.Status == WebExceptionStatus.ProtocolError)
3507 response = (HttpWebResponse)webException.Response;
3510 if (response == null)
3512 Exception convertedException = ConvertWebException(webException, request, abortReason);
3514 if (convertedException != null)
3516 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(convertedException);
3519 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(webException.Message,
3523 if (response.StatusCode == HttpStatusCode.NotFound)
3525 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new EndpointNotFoundException(SR.GetString(SR.EndpointNotFound, request.RequestUri.AbsoluteUri), webException));
3528 if (response.StatusCode == HttpStatusCode.ServiceUnavailable)
3530 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ServerTooBusyException(SR.GetString(SR.HttpServerTooBusy, request.RequestUri.AbsoluteUri), webException));
3533 if (response.StatusCode == HttpStatusCode.UnsupportedMediaType)
3535 string statusDescription = response.StatusDescription;
3536 if (!string.IsNullOrEmpty(statusDescription))
3538 if (string.Compare(statusDescription, HttpChannelUtilities.StatusDescriptionStrings.HttpContentTypeMissing, StringComparison.OrdinalIgnoreCase) == 0)
3540 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ProtocolException(SR.GetString(SR.MissingContentType, request.RequestUri), webException));
3543 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ProtocolException(SR.GetString(SR.FramingContentTypeMismatch, request.ContentType, request.RequestUri), webException));
3546 if (response.StatusCode == HttpStatusCode.GatewayTimeout)
3548 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new TimeoutException(webException.Message, webException));
3551 // if http.sys has a request queue on the TCP port, then if the path fails to match it will send
3552 // back "<h1>Bad Request (Invalid Hostname)</h1>" in the body of a 400 response.
3553 // See code at \\index1\sddnsrv\net\http\sys\httprcv.c for details
3554 if (response.StatusCode == HttpStatusCode.BadRequest)
3556 const string httpSysRequestQueueNotFound = "<h1>Bad Request (Invalid Hostname)</h1>";
3557 const string httpSysRequestQueueNotFoundVista = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\"\"http://www.w3.org/TR/html4/strict.dtd\">\r\n<HTML><HEAD><TITLE>Bad Request</TITLE>\r\n<META HTTP-EQUIV=\"Content-Type\" Content=\"text/html; charset=us-ascii\"></HEAD>\r\n<BODY><h2>Bad Request - Invalid Hostname</h2>\r\n<hr><p>HTTP Error 400. The request hostname is invalid.</p>\r\n</BODY></HTML>\r\n";
3558 string notFoundTestString = null;
3560 if (response.ContentLength == httpSysRequestQueueNotFound.Length)
3562 notFoundTestString = httpSysRequestQueueNotFound;
3564 else if (response.ContentLength == httpSysRequestQueueNotFoundVista.Length)
3566 notFoundTestString = httpSysRequestQueueNotFoundVista;
3569 if (notFoundTestString != null)
3571 Stream responseStream = response.GetResponseStream();
3572 byte[] responseBytes = new byte[notFoundTestString.Length];
3573 int bytesRead = responseStream.Read(responseBytes, 0, responseBytes.Length);
3575 // since the response is buffered by System.Net (it's an error response), we should have read
3576 // the amount we were expecting
3577 if (bytesRead == notFoundTestString.Length
3578 && notFoundTestString == UTF8Encoding.ASCII.GetString(responseBytes))
3580 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new EndpointNotFoundException(SR.GetString(SR.EndpointNotFound, request.RequestUri.AbsoluteUri), webException));
3588 public static Exception ConvertWebException(WebException webException, HttpWebRequest request, HttpAbortReason abortReason)
3590 switch (webException.Status)
3592 case WebExceptionStatus.ConnectFailure:
3593 case WebExceptionStatus.NameResolutionFailure:
3594 case WebExceptionStatus.ProxyNameResolutionFailure:
3595 return new EndpointNotFoundException(SR.GetString(SR.EndpointNotFound, request.RequestUri.AbsoluteUri), webException);
3596 case WebExceptionStatus.SecureChannelFailure:
3597 return new SecurityNegotiationException(SR.GetString(SR.SecureChannelFailure, request.RequestUri.Authority), webException);
3598 case WebExceptionStatus.TrustFailure:
3599 return new SecurityNegotiationException(SR.GetString(SR.TrustFailure, request.RequestUri.Authority), webException);
3600 case WebExceptionStatus.Timeout:
3601 return new TimeoutException(CreateRequestTimedOutMessage(request), webException);
3602 case WebExceptionStatus.ReceiveFailure:
3603 return new CommunicationException(SR.GetString(SR.HttpReceiveFailure, request.RequestUri), webException);
3604 case WebExceptionStatus.SendFailure:
3605 return new CommunicationException(SR.GetString(SR.HttpSendFailure, request.RequestUri), webException);
3606 case WebExceptionStatus.RequestCanceled:
3607 return CreateRequestCanceledException(webException, request, abortReason);
3608 case WebExceptionStatus.ProtocolError:
3609 HttpWebResponse response = (HttpWebResponse)webException.Response;
3610 Fx.Assert(response != null, "'response' MUST NOT be NULL for WebExceptionStatus=='ProtocolError'.");
3611 if (response.StatusCode == HttpStatusCode.InternalServerError &&
3612 string.Compare(response.StatusDescription, HttpChannelUtilities.StatusDescriptionStrings.HttpStatusServiceActivationException, StringComparison.OrdinalIgnoreCase) == 0)
3614 return new ServiceActivationException(SR.GetString(SR.Hosting_ServiceActivationFailed, request.RequestUri));
3625 public static Exception CreateResponseIOException(IOException ioException, TimeSpan receiveTimeout)
3627 if (ioException.InnerException is SocketException)
3629 return SocketConnection.ConvertTransferException((SocketException)ioException.InnerException, receiveTimeout, ioException);
3632 return new CommunicationException(SR.GetString(SR.HttpTransferError, ioException.Message), ioException);
3635 public static Exception CreateResponseWebException(WebException webException, HttpWebResponse response)
3637 switch (webException.Status)
3639 case WebExceptionStatus.RequestCanceled:
3640 return TraceResponseException(new CommunicationObjectAbortedException(SR.GetString(SR.HttpRequestAborted, response.ResponseUri), webException));
3641 case WebExceptionStatus.ConnectionClosed:
3642 return TraceResponseException(new CommunicationException(webException.Message, webException));
3643 case WebExceptionStatus.Timeout:
3644 return TraceResponseException(new TimeoutException(SR.GetString(SR.HttpResponseTimedOut, response.ResponseUri,
3645 TimeSpan.FromMilliseconds(response.GetResponseStream().ReadTimeout)), webException));
3647 return CreateUnexpectedResponseException(webException, response);
3651 public static Exception CreateRequestCanceledException(Exception webException, HttpWebRequest request, HttpAbortReason abortReason)
3653 switch (abortReason)
3655 case HttpAbortReason.Aborted:
3656 return new CommunicationObjectAbortedException(SR.GetString(SR.HttpRequestAborted, request.RequestUri), webException);
3657 case HttpAbortReason.TimedOut:
3658 return new TimeoutException(CreateRequestTimedOutMessage(request), webException);
3660 return new CommunicationException(SR.GetString(SR.HttpTransferError, webException.Message), webException);
3664 public static Exception CreateRequestIOException(IOException ioException, HttpWebRequest request)
3666 return CreateRequestIOException(ioException, request, null);
3669 public static Exception CreateRequestIOException(IOException ioException, HttpWebRequest request, Exception originalException)
3671 Exception exception = originalException == null ? ioException : originalException;
3673 if (ioException.InnerException is SocketException)
3675 return SocketConnection.ConvertTransferException((SocketException)ioException.InnerException, TimeSpan.FromMilliseconds(request.Timeout), exception);
3678 return new CommunicationException(SR.GetString(SR.HttpTransferError, exception.Message), exception);
3681 static string CreateRequestTimedOutMessage(HttpWebRequest request)
3683 return SR.GetString(SR.HttpRequestTimedOut, request.RequestUri, TimeSpan.FromMilliseconds(request.Timeout));
3686 public static Exception CreateRequestWebException(WebException webException, HttpWebRequest request, HttpAbortReason abortReason)
3688 Exception convertedException = ConvertWebException(webException, request, abortReason);
3690 if (webException.Response != null)
3692 //free the connection for use by another request
3693 webException.Response.Close();
3696 if (convertedException != null)
3698 return convertedException;
3701 if (webException.InnerException is IOException)
3703 return CreateRequestIOException((IOException)webException.InnerException, request, webException);
3706 if (webException.InnerException is SocketException)
3708 return SocketConnectionInitiator.ConvertConnectException((SocketException)webException.InnerException, request.RequestUri, TimeSpan.MaxValue, webException);
3711 return new EndpointNotFoundException(SR.GetString(SR.EndpointNotFound, request.RequestUri.AbsoluteUri), webException);
3714 static Exception CreateUnexpectedResponseException(WebException responseException, HttpWebResponse response)
3716 string statusDescription = response.StatusDescription;
3717 if (string.IsNullOrEmpty(statusDescription))
3718 statusDescription = response.StatusCode.ToString();
3720 return TraceResponseException(
3721 new ProtocolException(SR.GetString(SR.UnexpectedHttpResponseCode,
3722 (int)response.StatusCode, statusDescription), responseException));
3725 public static Exception CreateNullReferenceResponseException(NullReferenceException nullReferenceException)
3727 return TraceResponseException(
3728 new ProtocolException(SR.GetString(SR.NullReferenceOnHttpResponse), nullReferenceException));
3731 static string GetResponseStreamString(HttpWebResponse webResponse, out int bytesRead)
3733 Stream responseStream = webResponse.GetResponseStream();
3735 long bufferSize = webResponse.ContentLength;
3737 if (bufferSize < 0 || bufferSize > ResponseStreamExcerptSize)
3739 bufferSize = ResponseStreamExcerptSize;
3742 byte[] responseBuffer = DiagnosticUtility.Utility.AllocateByteArray(checked((int)bufferSize));
3743 bytesRead = responseStream.Read(responseBuffer, 0, (int)bufferSize);
3744 responseStream.Close();
3746 return System.Text.Encoding.UTF8.GetString(responseBuffer, 0, bytesRead);
3749 static Exception TraceResponseException(Exception exception)
3751 if (DiagnosticUtility.ShouldTraceError)
3753 TraceUtility.TraceEvent(TraceEventType.Error, TraceCode.HttpChannelUnexpectedResponse, SR.GetString(SR.TraceCodeHttpChannelUnexpectedResponse), (object)null, exception);
3759 static bool ValidateEmptyContent(HttpWebResponse response)
3761 bool responseIsEmpty = true;
3763 if (response.ContentLength > 0)
3765 responseIsEmpty = false;
3767 else if (response.ContentLength == -1) // chunked
3769 Stream responseStream = response.GetResponseStream();
3770 byte[] testBuffer = new byte[1];
3771 responseIsEmpty = (responseStream.Read(testBuffer, 0, 1) != 1);
3774 return responseIsEmpty;
3777 static void ValidateAuthentication(HttpWebRequest request, HttpWebResponse response,
3778 WebException responseException, HttpChannelFactory<IRequestChannel> factory)
3780 if (response.StatusCode == HttpStatusCode.Unauthorized)
3782 string message = SR.GetString(SR.HttpAuthorizationFailed, factory.AuthenticationScheme,
3783 response.Headers[HttpResponseHeader.WwwAuthenticate]);
3784 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
3785 TraceResponseException(new MessageSecurityException(message, responseException)));
3788 if (response.StatusCode == HttpStatusCode.Forbidden)
3790 string message = SR.GetString(SR.HttpAuthorizationForbidden, factory.AuthenticationScheme);
3791 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
3792 TraceResponseException(new MessageSecurityException(message, responseException)));
3795 if ((request.AuthenticationLevel == AuthenticationLevel.MutualAuthRequired) &&
3796 !response.IsMutuallyAuthenticated)
3798 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
3799 TraceResponseException(new SecurityNegotiationException(SR.GetString(SR.HttpMutualAuthNotSatisfied),
3800 responseException)));
3804 public static void ValidateDigestCredential(ref NetworkCredential credential, TokenImpersonationLevel impersonationLevel)
3806 // this is a work-around to VSWhidbey#470545 (Since the service always uses Impersonation,
3807 // we mitigate EOP by preemtively not allowing Identification)
3808 if (!SecurityUtils.IsDefaultNetworkCredential(credential))
3810 // With a non-default credential, Digest will not honor a client impersonation constraint of
3811 // TokenImpersonationLevel.Identification.
3812 if (!TokenImpersonationLevelHelper.IsGreaterOrEqual(impersonationLevel,
3813 TokenImpersonationLevel.Impersonation))
3815 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(
3816 SR.DigestExplicitCredsImpersonationLevel, impersonationLevel)));
3821 // only valid response codes are 500 (if it's a fault) or 200 (iff it's a response message)
3822 public static HttpInput ValidateRequestReplyResponse(HttpWebRequest request, HttpWebResponse response,
3823 HttpChannelFactory<IRequestChannel> factory, WebException responseException, ChannelBinding channelBinding)
3825 ValidateAuthentication(request, response, responseException, factory);
3827 HttpInput httpInput = null;
3829 // We will close the HttpWebResponse if we got an error code betwen 200 and 300 and
3830 // 1) an exception was thrown out or
3831 // 2) it's an empty message and we are using SOAP.
3832 // For responses with status code above 300, System.Net will close the underlying connection so we don't need to worry about that.
3833 if ((200 <= (int)response.StatusCode && (int)response.StatusCode < 300) || response.StatusCode == HttpStatusCode.InternalServerError)
3835 if (response.StatusCode == HttpStatusCode.InternalServerError
3836 && string.Compare(response.StatusDescription, HttpChannelUtilities.StatusDescriptionStrings.HttpStatusServiceActivationException, StringComparison.OrdinalIgnoreCase) == 0)
3838 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ServiceActivationException(SR.GetString(SR.Hosting_ServiceActivationFailed, request.RequestUri)));
3842 bool throwing = true;
3845 if (string.IsNullOrEmpty(response.ContentType))
3847 if (!ValidateEmptyContent(response))
3849 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(TraceResponseException(
3850 new ProtocolException(
3851 SR.GetString(SR.HttpContentTypeHeaderRequired),
3852 responseException)));
3855 else if (response.ContentLength != 0)
3857 MessageEncoder encoder = factory.MessageEncoderFactory.Encoder;
3858 if (!encoder.IsContentTypeSupported(response.ContentType))
3861 String responseExcerpt = GetResponseStreamString(response, out bytesRead);
3863 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(TraceResponseException(
3864 new ProtocolException(
3866 SR.ResponseContentTypeMismatch,
3867 response.ContentType,
3868 encoder.ContentType,
3870 responseExcerpt), responseException)));
3874 httpInput = HttpInput.CreateHttpInput(response, factory, channelBinding);
3875 httpInput.WebException = responseException;
3889 if (httpInput == null)
3891 if (factory.MessageEncoderFactory.MessageVersion == MessageVersion.None)
3893 httpInput = HttpInput.CreateHttpInput(response, factory, channelBinding);
3894 httpInput.WebException = responseException;
3898 // In this case, we got a response with
3899 // 1) status code between 200 and 300
3900 // 2) Non-empty Content Type string
3901 // 3) Zero content length
3902 // Since we are trying to use SOAP here, the message seems to be malicious and we should
3903 // just close the response directly.
3910 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateUnexpectedResponseException(responseException, response));
3916 public static bool GetHttpResponseTypeAndEncodingForCompression(ref string contentType, out string contentEncoding)
3918 contentEncoding = null;
3919 bool isSession = false;
3920 bool isDeflate = false;
3922 if (string.Equals(BinaryVersion.GZipVersion1.ContentType, contentType, StringComparison.OrdinalIgnoreCase) ||
3923 (isSession = string.Equals(BinaryVersion.GZipVersion1.SessionContentType, contentType, StringComparison.OrdinalIgnoreCase)) ||
3924 (isDeflate = (string.Equals(BinaryVersion.DeflateVersion1.ContentType, contentType, StringComparison.OrdinalIgnoreCase) ||
3925 (isSession = string.Equals(BinaryVersion.DeflateVersion1.SessionContentType, contentType, StringComparison.OrdinalIgnoreCase)))))
3927 contentType = isSession ? BinaryVersion.Version1.SessionContentType : BinaryVersion.Version1.ContentType;
3928 contentEncoding = isDeflate ? MessageEncoderCompressionHandler.DeflateContentEncoding : MessageEncoderCompressionHandler.GZipContentEncoding;
3935 abstract class HttpDelayedAcceptStream : DetectEofStream
3937 HttpOutput httpOutput;
3938 bool isHttpOutputClosed;
3941 /// Indicates whether the HttpOutput should be closed when this stream is closed. In the streamed case,
3942 /// we�ll leave the HttpOutput opened (and it will be closed by the HttpRequestContext, so we won't leak it).
3944 bool closeHttpOutput;
3946 // sometimes we can't flush the HTTP output until we're done reading the end of the
3947 // incoming stream of the HTTP input
3948 protected HttpDelayedAcceptStream(Stream stream)
3953 public bool EnableDelayedAccept(HttpOutput output, bool closeHttpOutput)
3960 this.closeHttpOutput = closeHttpOutput;
3961 this.httpOutput = output;
3965 protected override void OnReceivedEof()
3967 if (this.closeHttpOutput)
3973 public override void Close()
3975 if (this.closeHttpOutput)
3983 void CloseHttpOutput()
3985 if (this.httpOutput != null && !this.isHttpOutputClosed)
3987 this.httpOutput.Close();
3988 this.isHttpOutputClosed = true;
3993 abstract class BytesReadPositionStream : DelegatingStream
3997 protected BytesReadPositionStream(Stream stream)
4002 public override long Position
4010 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.SeekNotSupported)));
4014 public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
4016 this.bytesSent += count;
4017 return BaseStream.BeginWrite(buffer, offset, count, callback, state);
4020 public override void Write(byte[] buffer, int offset, int count)
4022 BaseStream.Write(buffer, offset, count);
4023 this.bytesSent += count;
4026 public override void WriteByte(byte value)
4028 BaseStream.WriteByte(value);
4033 class PreReadStream : DelegatingStream
4035 byte[] preReadBuffer;
4037 public PreReadStream(Stream stream, byte[] preReadBuffer)
4040 this.preReadBuffer = preReadBuffer;
4043 bool ReadFromBuffer(byte[] buffer, int offset, int count, out int bytesRead)
4045 if (this.preReadBuffer != null)
4049 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("buffer");
4052 if (offset >= buffer.Length)
4054 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("offset", offset,
4055 SR.GetString(SR.OffsetExceedsBufferBound, buffer.Length - 1)));
4060 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("count", count,
4061 SR.GetString(SR.ValueMustBeNonNegative)));
4070 buffer[offset] = this.preReadBuffer[0];
4071 this.preReadBuffer = null;
4082 public override int Read(byte[] buffer, int offset, int count)
4085 if (ReadFromBuffer(buffer, offset, count, out bytesRead))
4090 return base.Read(buffer, offset, count);
4093 public override int ReadByte()
4095 if (this.preReadBuffer != null)
4097 byte[] tempBuffer = new byte[1];
4099 if (ReadFromBuffer(tempBuffer, 0, 1, out bytesRead))
4101 return tempBuffer[0];
4104 return base.ReadByte();
4107 public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
4110 if (ReadFromBuffer(buffer, offset, count, out bytesRead))
4112 return new CompletedAsyncResult<int>(bytesRead, callback, state);
4115 return base.BeginRead(buffer, offset, count, callback, state);
4118 public override int EndRead(IAsyncResult result)
4120 if (result is CompletedAsyncResult<int>)
4122 return CompletedAsyncResult<int>.End(result);
4126 return base.EndRead(result);
4131 class HttpRequestMessageHttpInput : HttpInput, HttpRequestMessageProperty.IHttpHeaderProvider
4133 const string SoapAction = "SOAPAction";
4134 HttpRequestMessage httpRequestMessage;
4135 ChannelBinding channelBinding;
4137 public HttpRequestMessageHttpInput(HttpRequestMessage httpRequestMessage, IHttpTransportFactorySettings settings, bool enableChannelBinding, ChannelBinding channelBinding)
4138 : base(settings, true, enableChannelBinding)
4140 this.httpRequestMessage = httpRequestMessage;
4141 this.channelBinding = channelBinding;
4144 public override long ContentLength
4148 if (this.httpRequestMessage.Content.Headers.ContentLength == null)
4150 // Chunked transfer mode
4154 return this.httpRequestMessage.Content.Headers.ContentLength.Value;
4158 protected override ChannelBinding ChannelBinding
4162 return this.channelBinding;
4166 public HttpRequestMessage HttpRequestMessage
4168 get { return this.httpRequestMessage; }
4171 protected override bool HasContent
4175 // In Chunked transfer mode, the ContentLength header is null
4176 // Otherwise we just rely on the ContentLength header
4177 return this.httpRequestMessage.Content.Headers.ContentLength == null || this.httpRequestMessage.Content.Headers.ContentLength.Value > 0;
4181 protected override string ContentTypeCore
4185 if (!this.HasContent)
4190 return this.httpRequestMessage.Content.Headers.ContentType == null ? null : this.httpRequestMessage.Content.Headers.ContentType.MediaType;
4194 public override void ConfigureHttpRequestMessage(HttpRequestMessage message)
4196 throw FxTrace.Exception.AsError(new InvalidOperationException());
4199 protected override Stream GetInputStream()
4201 if (this.httpRequestMessage.Content == null)
4206 return this.httpRequestMessage.Content.ReadAsStreamAsync().Result;
4209 protected override void AddProperties(Message message)
4211 HttpRequestMessageProperty requestProperty = new HttpRequestMessageProperty(this.httpRequestMessage);
4212 message.Properties.Add(HttpRequestMessageProperty.Name, requestProperty);
4213 message.Properties.Via = this.httpRequestMessage.RequestUri;
4215 foreach (KeyValuePair<string, object> property in this.httpRequestMessage.Properties)
4217 message.Properties.Add(property.Key, property.Value);
4220 this.httpRequestMessage.Properties.Clear();
4223 protected override string SoapActionHeader
4227 IEnumerable<string> values;
4228 if (this.httpRequestMessage.Headers.TryGetValues(SoapAction, out values))
4230 foreach (string headerValue in values)
4240 public void CopyHeaders(WebHeaderCollection headers)
4242 // No special-casing for the "WWW-Authenticate" header required here,
4243 // because this method is only called for the incoming request
4244 // and the WWW-Authenticate header is a header only applied to responses.
4245 HttpChannelUtilities.CopyHeaders(this.httpRequestMessage, headers.Add);
4248 internal void SetHttpRequestMessage(HttpRequestMessage httpRequestMessage)
4250 Fx.Assert(httpRequestMessage != null, "httpRequestMessage should not be null.");
4251 this.httpRequestMessage = httpRequestMessage;