/// <summary> /// Closes a comunication stream for a specific BindingContext /// </summary> /// <param name="stream">The stream to close.</param> /// <param name="ctx">The context to close the stream for.</param> public void Close( Stream stream, BindingContext ctx ) { if(this.OnClose( stream, ctx ) == ChainResult.Continue && m_Next != null) { m_Next.Close( stream, ctx ); } }
/// <summary> /// Opens a comunication stream for a specific BindingContext /// </summary> /// <param name="stream">The stream to open.</param> /// <param name="ctx">The context to open the stream for.</param> public void Open( ref Stream stream, BindingContext ctx ) { if (this.m_Next != null) { this.m_Next.Open(ref stream, ctx); } this.OnOpen(ref stream, ctx); }
/// <summary> /// Processes an output message for a specific context /// </summary> /// <param name="message">The message to process.</param> /// <param name="ctx">The context to process the message for.</param> /// <returns><c>true</c> if the processing was succesfull, <c>false</c>otherwise.</returns> public bool ProcessOutputMessage(ref WsMessage message, BindingContext ctx) { ChainResult res = this.OnProcessOutputMessage( ref message, ctx ); if( ChainResult.Continue == res && m_Next != null) { return m_Next.ProcessOutputMessage( ref message, ctx ); } return res == ChainResult.Handled; }
/// <summary> /// Opens the stream for the HTTP tansport binding /// </summary> /// <param name="stream">The stream for this binding.</param> /// <param name="ctx">The context associated with the stream for this binding.</param> /// <returns>The handling status for this operation.</returns> protected override ChainResult OnOpen(ref Stream stream, BindingContext ctx) { if(ctx is ServerBindingContext) { m_httpListener = new HttpListener("http", m_endpointUri.Port); m_httpListener.Start(); } return ChainResult.Handled; }
/// <summary> /// A RequestContext object is created by the ReplyChannel when a request is received. /// </summary> /// <param name="message">The request incomming message</param> /// <param name="channel">The communication channel associated with the message</param> /// <param name="ctx">The binding context associated with the channel</param> internal RequestContext(WsMessage message, ReplyChannel channel, BindingContext ctx) { this.m_message = message; this.m_channel = channel; this.m_context = ctx; }
/// <summary> /// Processes a message /// </summary> /// <param name="stream">The message being processed.</param> /// <param name="ctx">The context associated with the message.</param> /// <returns>The handling status for this operation.</returns> protected override ChainResult OnProcessInputMessage( ref WsMessage msg, BindingContext ctx ) { byte[] soapMessage = null; byte[] buffer = new byte[MaxUdpPacketSize]; while (true) { EndPoint remoteEndpoint = new IPEndPoint(IPAddress.Any, 0); int size = m_udpReceiveClient.ReceiveFrom(buffer, MaxUdpPacketSize, SocketFlags.None, ref remoteEndpoint); // If the stack is set to ignore request from this address do so if (m_config.IgnoreRequestsFromThisIp && ((IPEndPoint)remoteEndpoint).Address.Equals(s_localIP)) { continue; } if (size > 0) { soapMessage = new byte[size]; Array.Copy(buffer, soapMessage, size); } else { System.Ext.Console.Write("UDP Receive returned 0 bytes"); } ctx.ContextObject = remoteEndpoint; System.Ext.Console.Write("UDP Request From: " + remoteEndpoint.ToString()); System.Ext.Console.Write(soapMessage); break; } msg.Body = soapMessage; return ChainResult.Continue; }
/// <summary> /// Opens the stream for the UDP tansport binding /// </summary> /// <param name="stream">The stream for this binding.</param> /// <param name="ctx">The context associated with the stream for this binding.</param> /// <returns>The handling status for this operation.</returns> protected override ChainResult OnOpen( ref Stream stream, BindingContext ctx ) { m_udpReceiveClient = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); IPEndPoint localEP = new IPEndPoint(s_localIP, m_config.DiscoveryPort); m_udpReceiveClient.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); m_udpReceiveClient.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveBuffer, 0x5000); // Join Multicast Group byte[] discoveryAddr = m_config.DiscoveryAddress.GetAddressBytes(); byte[] ipAddr = s_localIP.GetAddressBytes(); byte[] multicastOpt = new byte[] { discoveryAddr[0], discoveryAddr[1], discoveryAddr[2], discoveryAddr[3], // WsDiscovery Multicast Address: 239.255.255.250 ipAddr [0], ipAddr [1], ipAddr [2], ipAddr [3] }; // Local IPAddress m_udpReceiveClient.Bind(localEP); m_udpReceiveClient.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastInterface, ipAddr ); m_udpReceiveClient.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, multicastOpt); return ChainResult.Continue; }
/// <summary> /// Processes a message /// </summary> /// <param name="msg">The message being processed.</param> /// <param name="ctx">The context associated with the message.</param> /// <returns>The handling status for this operation.</returns> protected override ChainResult OnProcessOutputMessage( ref WsMessage msg, BindingContext ctx ) { byte[] data = null; // if the body is a byte[] then it is already serialized (UDP stuff) if (msg == null || (msg.Body != null && msg.Body is byte[])) return ChainResult.Continue; // Build response message using (XmlMemoryWriter xmlWriter = XmlMemoryWriter.Create()) { WsSoapMessageWriter smw = new WsSoapMessageWriter(ctx.Version); // Write start message up to body element content smw.WriteSoapMessageStart(xmlWriter, msg, ctx.Version.IncludeSoapHeaders); if (msg.Body != null && msg.Serializer != null) { DataContractSerializer ser = (DataContractSerializer)msg.Serializer; // Serialize the body element ser.WriteObject(xmlWriter, msg.Body); if(ser.BodyParts != null && ser.BodyParts.Count > 0) { msg.BodyParts = ser.BodyParts; } } // Write end message smw.WriteSoapMessageEnd(xmlWriter); data = xmlWriter.ToArray(); } WsMtomBodyParts bodyParts = msg.BodyParts; if (bodyParts != null) { DataContractSerializer reqDcs = (DataContractSerializer)msg.Serializer; bodyParts.Start = "<soap@soap>"; bodyParts.AddStart(reqDcs.CreateNewBodyPart(data, bodyParts.Start)); if (reqDcs.BodyParts.Count > 0) { bodyParts.Add(reqDcs.BodyParts[0]); } WsMtomParams mtomParams = new WsMtomParams(); if (bodyParts.Boundary == null) bodyParts.Boundary = Guid.NewGuid().ToString() + '-' + Guid.NewGuid().ToString().Substring(0, 33); mtomParams.start = bodyParts.Start; mtomParams.boundary = bodyParts.Boundary; msg.MtomPropeties = mtomParams; WsMtom mtom = new WsMtom(); data = mtom.CreateMessage(bodyParts); } msg.Body = data; return ChainResult.Continue; }
internal void SendMessage( WsMessage message, BindingContext ctx ) { // Reset the binding properties as they were likely changed by the receive ctx.BindingProperties = (ArrayList)m_context.BindingProperties.Clone(); m_binding.Elements.ProcessOutputMessage( ref message, ctx ); }
/// <summary> /// A RequestContext object is created by the ReplyChannel when a request is received. /// </summary> /// <param name="message">The request incomming message</param> /// <param name="channel">The communication channel associated with the message</param> /// <param name="ctx">The binding context associated with the channel</param> internal RequestContext( WsMessage message, ReplyChannel channel, BindingContext ctx ) { this.m_message = message; this.m_channel = channel; this.m_context = ctx; }
private void SendMessage( WsMessage message, BindingContext ctx ) { m_binding.Elements.ProcessOutputMessage( ref message, ctx ); }
/// <summary> /// Processes a message /// </summary> /// <param name="msg">The message being processed.</param> /// <param name="ctx">The context associated with the message.</param> /// <returns>The handling status for this operation.</returns> protected override ChainResult OnProcessOutputMessage(ref WsMessage msg, BindingContext ctx) { ArrayList props = ctx.BindingProperties; byte[] message = null; string contentType = "text/plain"; if (msg != null) { message = msg.Body as byte[]; if (msg.BodyParts != null) { contentType = "Multipart/Related;boundary=" + msg.MtomPropeties.boundary + ";type=\"application/xop+xml\";start=\"" + msg.MtomPropeties.start + "\";start-info=\"application/soap+xml\""; ctx.BindingProperties.Add(new BindingProperty("header", HttpKnownHeaderNames.Server, "Microsoft-MF HTTP 1.0")); ctx.BindingProperties.Add(new BindingProperty("header", HttpKnownHeaderNames.MimeVersion, "1.0")); ctx.BindingProperties.Add(new BindingProperty("header", HttpKnownHeaderNames.Date, DateTime.Now.ToString())); } else { contentType = "application/soap+xml; charset=utf-8"; } } if (ctx is ClientBindingContext) { if (message == null) { return(ChainResult.Abort); } HttpWebRequest request = HttpWebRequest.Create(new Uri(m_endpointUri.AbsoluteUri)) as HttpWebRequest; request.Timeout = (int)(ctx.OpenTimeout.Ticks / TimeSpan.TicksPerMillisecond); request.ReadWriteTimeout = (int)(ctx.ReceiveTimeout.Ticks / TimeSpan.TicksPerMillisecond); ctx.ContextObject = request; // Post method request.Method = "POST"; WebHeaderCollection headers = request.Headers; request.ContentType = contentType; request.UserAgent = "MFWsAPI"; request.Headers.Add(HttpKnownHeaderNames.CacheControl, "no-cache"); request.Headers.Add(HttpKnownHeaderNames.Pragma, "no-cache"); if (props != null) { int len = props.Count; for (int i = 0; i < len; i++) { BindingProperty prop = (BindingProperty)props[i]; string container = prop.Container; if (container == "header") { headers.Add(prop.Name, (string)prop.Value); } else if (container == null || container == "") { string name = prop.Name; if (name == HttpKnownHeaderNames.ContentType) { request.ContentType = (string)prop.Value; } else if (name == HttpKnownHeaderNames.UserAgent) { request.UserAgent = (string)prop.Value; } } } } if (message != null) { System.Ext.Console.Write("Http message sent: "); System.Ext.Console.Write(message); request.ContentLength = message.Length; using (Stream stream = request.GetRequestStream()) { // Write soap message stream.Write(message, 0, message.Length); // Flush the stream and force a write stream.Flush(); } } } else { HttpListenerContext listenerContext = ctx.ContextObject as HttpListenerContext; if (listenerContext == null) { return(ChainResult.Abort); } HttpListenerResponse listenerResponse = listenerContext.Response; if (listenerResponse == null || listenerResponse.OutputStream == null) { return(ChainResult.Abort); } try { using (StreamWriter streamWriter = new StreamWriter(listenerResponse.OutputStream)) { // Write Header, if message is null write accepted if (message == null) { listenerResponse.StatusCode = 202; } else { listenerResponse.StatusCode = 200; } // Check to see it the hosted service is sending mtom WebHeaderCollection headers = listenerResponse.Headers; listenerResponse.ContentType = contentType; bool isChunked = false; if (props != null) { int len = props.Count; for (int i = 0; i < len; i++) { BindingProperty prop = (BindingProperty)props[i]; string container = prop.Container; string name = prop.Name; string value = (string)prop.Value; if (container == "header") { if (!isChunked && name == HttpKnownHeaderNames.TransferEncoding && value.ToLower() == "chunked") { isChunked = true; } headers.Add(name, (string)prop.Value); } else if (container == null || container == "") { if (name == HttpKnownHeaderNames.ContentType) { listenerResponse.ContentType = (string)prop.Value; System.Ext.Console.Write(HttpKnownHeaderNames.ContentType + ": " + listenerResponse.ContentType); } } } } // If chunked encoding is enabled write chunked message else write Content-Length if (isChunked) { // Chunk message int bufferIndex = 0; int chunkSize = 0; int defaultChunkSize = 0xff; #if DEBUG byte[] displayBuffer = new byte[defaultChunkSize]; #endif while (bufferIndex < message.Length) { // Calculate chunk size and write to stream chunkSize = message.Length - bufferIndex < defaultChunkSize ? message.Length - bufferIndex : defaultChunkSize; streamWriter.WriteLine(chunkSize.ToString("{0:X}")); System.Ext.Console.Write(chunkSize.ToString("{0:X}")); // Write chunk streamWriter.WriteBytes(message, bufferIndex, chunkSize); streamWriter.WriteLine(); #if DEBUG Array.Copy(message, bufferIndex, displayBuffer, 0, chunkSize); System.Ext.Console.Write(displayBuffer, bufferIndex, chunkSize); #endif // Adjust buffer index bufferIndex = bufferIndex + chunkSize; } // Write 0 length and blank line streamWriter.WriteLine("0"); streamWriter.WriteLine(); System.Ext.Console.Write("0"); System.Ext.Console.Write(""); } else { if (message == null) { listenerResponse.ContentLength64 = 0; } else { listenerResponse.ContentLength64 = message.Length; } System.Ext.Console.Write("Content Length: " + listenerResponse.ContentLength64); // If an empty message is returned (i.e. oneway request response) don't send if (message != null && message.Length > 0) { System.Ext.Console.Write(message); // Write soap message streamWriter.WriteBytes(message, 0, message.Length); } } // Flush the stream and return streamWriter.Flush(); } } catch { return(ChainResult.Abort); } finally { listenerContext.Close(); } } return(ChainResult.Handled); }
/// <summary> /// Closes the stream for the HTTP transport binding /// </summary> /// <param name="stream">The stream for this binding.</param> /// <param name="ctx">The context associated with the stream for this binding.</param> /// <returns>The handling status for this operation.</returns> protected override ChainResult OnClose( Stream stream, BindingContext ctx ) { if (ctx is ServerBindingContext) { if (ctx.ContextObject != null) { ((HttpListenerContext)ctx.ContextObject).Close(); ctx.ContextObject = null; } if (m_httpListener != null) { m_httpListener.Close(); m_httpListener = null; } } else { if(ctx.ContextObject != null) { ((HttpWebRequest)ctx.ContextObject).Dispose(); ctx.ContextObject = null; } } return ChainResult.Handled; }
/// <summary> /// Processes a message /// </summary> /// <param name="stream">The message being processed.</param> /// <param name="ctx">The context associated with the message.</param> /// <returns>The handling status for this operation.</returns> protected override ChainResult OnProcessInputMessage(ref WsMessage msg, BindingContext ctx) { byte[] soapResponse = null; WebHeaderCollection headers = null; if (ctx is ClientBindingContext) { HttpWebRequest request = ctx.ContextObject as HttpWebRequest; if (request == null) { msg = null; return(ChainResult.Abort); } HttpWebResponse resp = request.GetResponse() as HttpWebResponse; if (resp == null) { throw new WebException("", WebExceptionStatus.ReceiveFailure); // No response was received on the HTTP channel } try { headers = (System.Net.WebHeaderCollection)resp.Headers; if (resp.ProtocolVersion != HttpVersion.Version11) { throw new IOException(); // Invalid http version in response line. } if (resp.StatusCode != HttpStatusCode.OK && resp.StatusCode != HttpStatusCode.Accepted) { throw new IOException(); // Bad status code in response } if (resp.ContentLength > 0) { // Return the soap response. soapResponse = new byte[(int)resp.ContentLength]; Stream respStream = resp.GetResponseStream(); respStream.ReadTimeout = (int)(ctx.ReceiveTimeout.Ticks / TimeSpan.TicksPerMillisecond); // Now need to read all data. We read in the loop until resp.ContentLength or zero bytes read. // Zero bytes read means there was error on server and it did not send all data. int respLength = (int)resp.ContentLength; for (int totalBytesRead = 0; totalBytesRead < respLength;) { int bytesRead = respStream.Read(soapResponse, totalBytesRead, (int)resp.ContentLength - totalBytesRead); // If nothing is read - means server closed connection or timeout. In this case no retry. if (bytesRead == 0) { break; } // Adds number of bytes read on this iteration. totalBytesRead += bytesRead; } headers = resp.Headers; } // // ContentLenght == 0 is OK // else if (resp.ContentLength < 0) { throw new ProtocolViolationException(); // Invalid http header, content lenght < 0 } } finally { resp.Dispose(); request.Dispose(); ctx.ContextObject = null; } } else // server waits for messages { if (m_httpListener == null) { msg = null; return(ChainResult.Abort); } HttpListenerContext listenerContext = m_httpListener.GetContext(); if (listenerContext == null) { msg = null; return(ChainResult.Abort); } ctx.ContextObject = listenerContext; // The context returned by m_httpListener.GetContext(); can be null in case the service was stopped. HttpListenerRequest listenerRequest = listenerContext.Request; HttpListenerResponse listenerResponse = listenerContext.Response; listenerRequest.InputStream.ReadTimeout = (int)(ctx.ReceiveTimeout.Ticks / TimeSpan.TicksPerMillisecond); listenerResponse.OutputStream.WriteTimeout = (int)(ctx.SendTimeout.Ticks / TimeSpan.TicksPerMillisecond); headers = (System.Net.WebHeaderCollection)listenerRequest.Headers; System.Ext.Console.Write("Request From: " + listenerRequest.RemoteEndPoint.ToString()); // Checks and process headers important for DPWS if (!ProcessKnownHeaders(listenerContext)) { msg = null; return(ChainResult.Abort); } soapResponse = null; int messageLength = (int)listenerRequest.ContentLength64; if (messageLength > 0) { // If there is content length for the message, we read it complete. soapResponse = new byte[messageLength]; for (int offset = 0; offset < messageLength;) { int noRead = listenerRequest.InputStream.Read(soapResponse, offset, messageLength - offset); if (noRead == 0) { throw new IOException("Http server got only " + offset + " bytes. Expected to read " + messageLength + " bytes."); } offset += noRead; } } else { // In this case the message is chunk encoded, but m_httpRequest.InputStream actually does processing. // So we we read until zero bytes are read. bool readComplete = false; int bufferSize = ReadPayload; soapResponse = new byte[bufferSize]; int offset = 0; while (!readComplete) { while (offset < ReadPayload) { int noRead = listenerRequest.InputStream.Read(soapResponse, offset, messageLength - offset); // If we read zero bytes - means this is end of message. This is how InputStream.Read for chunked encoded data. if (noRead == 0) { readComplete = true; break; } offset += noRead; } // If read was not complete - increase the buffer. if (!readComplete) { bufferSize += ReadPayload; byte[] newMessageBuf = new byte[bufferSize]; Array.Copy(soapResponse, newMessageBuf, offset); soapResponse = newMessageBuf; } } } } if (headers != null) { string [] keys = headers.AllKeys; int len = keys.Length; ArrayList props = ctx.BindingProperties; for (int i = 0; i < len; i++) { string key = keys[i]; props.Add(new BindingProperty("header", key, headers[key])); } } System.Ext.Console.Write(soapResponse); msg.Body = soapResponse; return(ChainResult.Continue); }
/// <summary> /// Opens the stream for the UDP tansport binding /// </summary> /// <param name="stream">The stream for this binding.</param> /// <param name="ctx">The context associated with the stream for this binding.</param> /// <returns>The handling status for this operation.</returns> protected override ChainResult OnOpen( ref Stream stream, BindingContext ctx ) { m_udpReceiveClient = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); IPEndPoint localEP = new IPEndPoint(IPAddress.Any, m_config.DiscoveryPort); m_udpReceiveClient.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); m_udpReceiveClient.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveBuffer, 0x5000); m_udpReceiveClient.Bind(localEP); // Join Multicast Group byte[] discoveryAddr = m_config.DiscoveryAddress.GetAddressBytes(); byte[] multicastOpt = new byte[] { discoveryAddr[0], discoveryAddr[1], discoveryAddr[2], discoveryAddr[3], // WsDiscovery Multicast Address: 239.255.255.250 0, 0, 0, 0 }; // IPAddress.Any: 0.0.0.0 m_udpReceiveClient.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, multicastOpt); // Create a UdpClient used to send request responses. Set SendTimeout. m_udpSendClient = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); m_udpSendClient.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); return ChainResult.Continue; }
/// <summary> /// Processes a message /// </summary> /// <param name="msg">The message being processed.</param> /// <param name="ctx">The context associated with the message.</param> /// <returns>The handling status for this operation.</returns> protected override ChainResult OnProcessOutputMessage( ref WsMessage msg, BindingContext ctx ) { if (m_remoteEndpoint == null) throw new Exception(); byte []message = msg.Body as byte[]; if (message == null) return ChainResult.Abort; System.Ext.Console.Write("UDP Message Sent To: " + m_remoteEndpoint.ToString()); System.Ext.Console.Write(message); try { Random rand = new Random(); for (int i = 0; i < 3; ++i) { int backoff = rand.Next(200) + 50; // 50-250 System.Threading.Thread.Sleep(backoff); m_udpSendClient.SendTo(message, message.Length, SocketFlags.None, m_remoteEndpoint); } } catch { return ChainResult.Abort; } return ChainResult.Handled; }
/// <summary> /// Closes the stream for the UDP transport binding /// </summary> /// <param name="stream">The stream for this binding.</param> /// <param name="ctx">The context associated with the stream for this binding.</param> /// <returns>The handling status for this operation.</returns> protected override ChainResult OnClose( Stream stream, BindingContext ctx ) { m_udpReceiveClient.Close(); m_udpSendClient.Close(); return ChainResult.Handled; }
internal ReplyChannel(Binding binding, BindingContext context) { this.m_binding = binding; this.m_context = context; }
/// <summary> /// Processes an input message for a specific context /// </summary> /// <param name="message">The message to process.</param> /// <param name="ctx">The context to process the message for.</param> /// <returns><c>true</c> if the processing was succesfull, <c>false</c>otherwise.</returns> public bool ProcessInputMessage(ref WsMessage message, BindingContext ctx) { if (m_Next != null) { if(!m_Next.ProcessInputMessage( ref message, ctx )) { return false; } } return this.OnProcessInputMessage(ref message, ctx) != ChainResult.Abort; }
/// <summary> /// Closes the stream for the HTTP transport binding /// </summary> /// <param name="stream">The stream for this binding.</param> /// <param name="ctx">The context associated with the stream for this binding.</param> /// <returns>The handling status for this operation.</returns> protected override ChainResult OnClose( Stream stream, BindingContext ctx ) { if (ctx is ServerBindingContext) { if (ctx.ContextObject != null && ctx.ContextObject is HttpListenerContext) { try { ((HttpListenerContext)ctx.ContextObject).Close(ctx.CloseTimeout.Seconds); } catch { } ctx.ContextObject = null; } if (m_httpListener != null) { try { m_httpListener.Close(); } catch { } m_httpListener = null; } } else { if(ctx.ContextObject != null && ctx.ContextObject is IDisposable) { try { ((IDisposable)ctx.ContextObject).Dispose(); } catch { } ctx.ContextObject = null; } } return ChainResult.Handled; }
private WsMessage ReceiveMessage( object deserializer, BindingContext ctx ) { WsMessage msg = new WsMessage( new WsWsaHeader(), null, WsPrefix.None ); msg.Deserializer = deserializer; // reset the binding properties as they likely were modified during the Send ctx.BindingProperties = (ArrayList)m_context.BindingProperties.Clone(); m_binding.Elements.ProcessInputMessage( ref msg, ctx ); return msg; }
/// <summary> /// Processes a message /// </summary> /// <param name="stream">The message being processed.</param> /// <param name="ctx">The context associated with the message.</param> /// <returns>The handling status for this operation.</returns> protected override ChainResult OnProcessInputMessage(ref WsMessage msg, BindingContext ctx) { byte[] soapResponse = null; WebHeaderCollection headers = null; if(ctx is ClientBindingContext) { HttpWebRequest request = ctx.ContextObject as HttpWebRequest; if(request == null) { msg = null; return ChainResult.Abort; } HttpWebResponse resp = request.GetResponse() as HttpWebResponse; if (resp == null) { throw new WebException("", WebExceptionStatus.ReceiveFailure); // No response was received on the HTTP channel } try { headers = (System.Net.WebHeaderCollection)resp.Headers; if (resp.ProtocolVersion != HttpVersion.Version11) { throw new IOException(); // Invalid http version in response line. } if (resp.StatusCode != HttpStatusCode.OK && resp.StatusCode != HttpStatusCode.Accepted) { if (resp.ContentType.IndexOf("application/soap+xml") == -1) { throw new IOException(); // Bad status code in response } } if (resp.ContentLength > 0) { // Return the soap response. soapResponse = new byte[(int)resp.ContentLength]; Stream respStream = resp.GetResponseStream(); respStream.ReadTimeout = (int)(ctx.ReceiveTimeout.Ticks / TimeSpan.TicksPerMillisecond); // Now need to read all data. We read in the loop until resp.ContentLength or zero bytes read. // Zero bytes read means there was error on server and it did not send all data. int respLength = (int)resp.ContentLength; for (int totalBytesRead = 0; totalBytesRead < respLength; ) { int bytesRead = respStream.Read(soapResponse, totalBytesRead, (int)resp.ContentLength - totalBytesRead); // If nothing is read - means server closed connection or timeout. In this case no retry. if (bytesRead == 0) { break; } // Adds number of bytes read on this iteration. totalBytesRead += bytesRead; } headers = resp.Headers; } // // ContentLenght == 0 is OK // else if(resp.ContentLength < 0) { throw new ProtocolViolationException(); // Invalid http header, content lenght < 0 } } finally { resp.Dispose(); request.Dispose(); ctx.ContextObject = null; } } else // server waits for messages { HttpListenerContext listenerContext; try { if(m_persistConn && ctx.ContextObject != null) { listenerContext = (HttpListenerContext)ctx.ContextObject; listenerContext.Reset(); } else { if (m_httpListener == null) { msg = null; return ChainResult.Abort; } listenerContext = m_httpListener.GetContext(); } if (listenerContext == null) { msg = null; return ChainResult.Abort; } ctx.ContextObject = listenerContext; // The context returned by m_httpListener.GetContext(); can be null in case the service was stopped. HttpListenerRequest listenerRequest = listenerContext.Request; HttpListenerResponse listenerResponse = listenerContext.Response; listenerRequest.InputStream.ReadTimeout = (int)(ctx.ReceiveTimeout.Ticks / TimeSpan.TicksPerMillisecond); listenerResponse.OutputStream.WriteTimeout = (int)(ctx.SendTimeout.Ticks / TimeSpan.TicksPerMillisecond); headers = (System.Net.WebHeaderCollection)listenerRequest.Headers; System.Ext.Console.Write("Request From: " + listenerRequest.RemoteEndPoint.ToString()); // Checks and process headers important for DPWS if (!ProcessKnownHeaders(listenerContext)) { msg = null; return ChainResult.Abort; } soapResponse = null; int messageLength = (int)listenerRequest.ContentLength64; if (messageLength > 0) { // If there is content length for the message, we read it complete. soapResponse = new byte[messageLength]; for (int offset = 0; offset < messageLength; ) { int noRead = listenerRequest.InputStream.Read(soapResponse, offset, messageLength - offset); if (noRead == 0) { throw new IOException("Http server got only " + offset + " bytes. Expected to read " + messageLength + " bytes."); } offset += noRead; } } else { // In this case the message is chunk encoded, but m_httpRequest.InputStream actually does processing. // So we we read until zero bytes are read. bool readComplete = false; int bufferSize = ReadPayload; soapResponse = new byte[bufferSize]; int offset = 0; while (!readComplete) { while (offset < ReadPayload) { int noRead = listenerRequest.InputStream.Read(soapResponse, offset, messageLength - offset); // If we read zero bytes - means this is end of message. This is how InputStream.Read for chunked encoded data. if (noRead == 0) { readComplete = true; break; } offset += noRead; } // If read was not complete - increase the buffer. if (!readComplete) { bufferSize += ReadPayload; byte[] newMessageBuf = new byte[bufferSize]; Array.Copy(soapResponse, newMessageBuf, offset); soapResponse = newMessageBuf; } } } } catch { ctx.ContextObject = null; throw; } } if(headers != null) { string [] keys = headers.AllKeys; int len = keys.Length; ArrayList props = ctx.BindingProperties; for(int i=0; i<len; i++) { string key = keys[i]; if(!WebHeaderCollection.IsRestricted(key)) { props.Add( new BindingProperty( "header", key, headers[key] ) ); } } } System.Ext.Console.Write(soapResponse); msg.Body = soapResponse; return ChainResult.Continue; }
internal ReplyChannel( Binding binding, BindingContext context ) { this.m_binding = binding; this.m_context = context; }
/// <summary> /// Processes a message /// </summary> /// <param name="msg">The message being processed.</param> /// <param name="ctx">The context associated with the message.</param> /// <returns>The handling status for this operation.</returns> protected override ChainResult OnProcessOutputMessage( ref WsMessage msg, BindingContext ctx ) { ArrayList props = ctx.BindingProperties; byte[] message = null; string contentType = "text/plain"; if (msg != null) { message = msg.Body as byte[]; if (msg.BodyParts != null) { contentType = "Multipart/Related;boundary=" + msg.MtomPropeties.boundary + ";type=\"application/xop+xml\";start=\"" + msg.MtomPropeties.start + "\";start-info=\"application/soap+xml\""; ctx.BindingProperties.Add(new BindingProperty("header", HttpKnownHeaderNames.Server, "Microsoft-MF HTTP 1.0")); ctx.BindingProperties.Add(new BindingProperty("header", HttpKnownHeaderNames.MimeVersion, "1.0")); ctx.BindingProperties.Add(new BindingProperty("header", HttpKnownHeaderNames.Date, DateTime.Now.ToString())); } else { contentType = "application/soap+xml; charset=utf-8"; } } if(ctx is ClientBindingContext) { if (message == null) return ChainResult.Abort; HttpWebRequest request; try { if(!m_persistConn || ctx.ContextObject == null) { request = HttpWebRequest.Create(new Uri(m_transportUri.AbsoluteUri)) as HttpWebRequest; request.Timeout = (int)(ctx.OpenTimeout.Ticks / TimeSpan.TicksPerMillisecond); request.ReadWriteTimeout = (int)(ctx.ReceiveTimeout.Ticks / TimeSpan.TicksPerMillisecond); ctx.ContextObject = request; } else { request = (HttpWebRequest)ctx.ContextObject; request.Reset(); } // Post method request.Method = "POST"; WebHeaderCollection headers = request.Headers; request.ContentType = contentType; request.UserAgent = "MFWsAPI"; request.Headers.Add(HttpKnownHeaderNames.CacheControl, "no-cache"); request.Headers.Add(HttpKnownHeaderNames.Pragma , "no-cache"); if (props != null) { int len = props.Count; for (int i = 0; i < len; i++) { BindingProperty prop = (BindingProperty)props[i]; string container = prop.Container; if (container == "header") { headers.Add(prop.Name, (string)prop.Value); } else if (container == null || container == "") { string name = prop.Name; if (name == HttpKnownHeaderNames.ContentType) { request.ContentType = (string)prop.Value; } else if (name == HttpKnownHeaderNames.UserAgent) { request.UserAgent = (string)prop.Value; } } } } if (message != null) { System.Ext.Console.Write("Http message sent: "); System.Ext.Console.Write(message); request.ContentLength = message.Length; using (Stream stream = request.GetRequestStream()) { // Write soap message stream.Write(message, 0, message.Length); // Flush the stream and force a write stream.Flush(); } } } catch { ctx.ContextObject = null; throw; } } else { HttpListenerContext listenerContext = ctx.ContextObject as HttpListenerContext; if(listenerContext == null) return ChainResult.Abort; HttpListenerResponse listenerResponse = listenerContext.Response; if (listenerResponse == null || listenerResponse.OutputStream == null) { ctx.ContextObject = null; return ChainResult.Abort; } try { StreamWriter streamWriter = new StreamWriter(listenerResponse.OutputStream); // Write Header, if message is null write accepted if (message == null || (msg != null && msg.Header != null && msg.Header.IsFaultMessage)) listenerResponse.StatusCode = 202; else listenerResponse.StatusCode = 200; // Check to see it the hosted service is sending mtom WebHeaderCollection headers = listenerResponse.Headers; listenerResponse.ContentType = contentType; bool isChunked = false; if (props != null) { int len = props.Count; for (int i = 0; i < len; i++) { BindingProperty prop = (BindingProperty)props[i]; string container = prop.Container; string name = prop.Name; string value = (string)prop.Value; if (container == "header") { if (!isChunked && name == HttpKnownHeaderNames.TransferEncoding && value.ToLower() == "chunked") { isChunked = true; } headers.Add(name, (string)prop.Value); } else if (container == null || container == "") { if (name == HttpKnownHeaderNames.ContentType) { listenerResponse.ContentType = (string)prop.Value; System.Ext.Console.Write(HttpKnownHeaderNames.ContentType + ": " + listenerResponse.ContentType); } } } } // If chunked encoding is enabled write chunked message else write Content-Length if (isChunked) { // Chunk message int bufferIndex = 0; int chunkSize = 0; int defaultChunkSize = 0xff; #if DEBUG byte[] displayBuffer = new byte[defaultChunkSize]; #endif while (bufferIndex < message.Length) { // Calculate chunk size and write to stream chunkSize = message.Length - bufferIndex < defaultChunkSize ? message.Length - bufferIndex : defaultChunkSize; streamWriter.WriteLine(chunkSize.ToString("{0:X}")); System.Ext.Console.Write(chunkSize.ToString("{0:X}")); // Write chunk streamWriter.WriteBytes(message, bufferIndex, chunkSize); streamWriter.WriteLine(); #if DEBUG Array.Copy(message, bufferIndex, displayBuffer, 0, chunkSize); System.Ext.Console.Write(displayBuffer, bufferIndex, chunkSize); #endif // Adjust buffer index bufferIndex = bufferIndex + chunkSize; } // Write 0 length and blank line streamWriter.WriteLine("0"); streamWriter.WriteLine(); System.Ext.Console.Write("0"); System.Ext.Console.Write(""); } else { if (message == null) { listenerResponse.ContentLength64 = 0; } else { listenerResponse.ContentLength64 = message.Length; } System.Ext.Console.Write("Content Length: " + listenerResponse.ContentLength64); // If an empty message is returned (i.e. oneway request response) don't send if (message != null && message.Length > 0) { System.Ext.Console.Write(message); // Write soap message streamWriter.WriteBytes(message, 0, message.Length); } } // Flush the stream and return streamWriter.Flush(); } catch { return ChainResult.Abort; } finally { if (m_persistConn) { listenerResponse.Detach(); } else { listenerContext.Close( ctx.CloseTimeout.Seconds ); ctx.ContextObject = null; } } } return ChainResult.Handled; }
internal WsMessage ReceiveMessage( BindingContext ctx ) { WsMessage msg = new WsMessage(new WsWsaHeader(), null, WsPrefix.None); while(true) { try { if (!m_binding.Elements.ProcessInputMessage(ref msg, ctx)) { msg = null; } // only need to loop if we received a bad request break; } catch (Faults.WsFaultException ex) { WsMessage faultResp = Faults.WsFault.GenerateFaultResponse(ex, ctx.Version); if (faultResp != null) { SendMessage(faultResp, ctx); } } } return msg; }
/// <summary> /// Signaled by the <ref>Open</ref> method to allow subclasses to create a sub-class of Binding Element with specific behaviour /// </summary> /// <param name="stream">The stream that is being opened.</param> /// <param name="ctx">The context for which the stream is being opened.</param> /// <returns>The handling status for this operation.</returns> protected virtual ChainResult OnOpen(ref Stream stream, BindingContext ctx) { return ChainResult.Continue; }
/// <summary> /// Processes a message /// </summary> /// <param name="stream">The message being processed.</param> /// <param name="ctx">The context associated with the message.</param> /// <returns>The handling status for this operation.</returns> protected override ChainResult OnProcessInputMessage(ref WsMessage msg, BindingContext ctx) { if(msg == null) return ChainResult.Abort; ArrayList props = ctx.BindingProperties; if (props != null) { int len = props.Count; for (int j = 0; j < len; j++) { BindingProperty prop = (BindingProperty)props[j]; if (prop.Name == HttpKnownHeaderNames.ContentType) { string strContentType = ((string)prop.Value).ToLower(); if (strContentType.IndexOf("multipart/related;") == 0) { // Create the mtom header class msg.MtomPropeties = new WsMtomParams(); // Parse Mtom Content-Type parameters string[] fields = strContentType.Substring(18).Split(';'); int fieldsLen = fields.Length; for (int i = 0; i < fieldsLen; ++i) { string type = fields[i]; int idx = type.IndexOf('='); if(idx != -1) { string param = type.Substring(0, idx).Trim(); string value = type.Substring(idx + 1).Trim('\"'); switch (param.ToUpper()) { case "BOUNDARY": if (param.Length > 72) throw new ArgumentException("Mime boundary element length exceeded.", "boundary"); msg.MtomPropeties.boundary = value; break; case "TYPE": msg.MtomPropeties.type = value; break; case "START": msg.MtomPropeties.start = value; break; case "START-INFO": msg.MtomPropeties.startInfo = value; break; default: break; } } } // Check required Mtom fields if (msg.MtomPropeties.boundary == null || msg.MtomPropeties.type == null || msg.MtomPropeties.start == null) { throw new WsFaultException(msg.Header, WsFaultType.WseInvalidMessage); } WsMtom mtom = new WsMtom((byte[])msg.Body); msg.Body = mtom.ParseMessage(msg.MtomPropeties.boundary); msg.BodyParts = mtom.BodyParts; } else if (strContentType.IndexOf("application/soap+xml") != 0) { throw new WsFaultException(msg.Header, WsFaultType.WseInvalidMessage); } } } } if (msg.Body == null) return ChainResult.Continue; MemoryStream requestStream = new MemoryStream((byte[])msg.Body); XmlReader reader = XmlReader.Create(requestStream); WsWsaHeader hdr = new WsWsaHeader(); reader.ReadStartElement("Envelope", WsWellKnownUri.SoapNamespaceUri); if(ctx.Version.IncludeSoapHeaders) { hdr.ParseHeader(reader, ctx.Version); } msg.Header = hdr; reader.ReadStartElement("Body", WsWellKnownUri.SoapNamespaceUri); if(msg.Deserializer != null) { msg.Body = ((DataContractSerializer)msg.Deserializer).ReadObject(reader); reader.Dispose(); } else { msg.Reader = reader; } return ChainResult.Continue; }
/// <summary> /// Signaled by the <ref>Close</ref> method to allow subclasses to create a sub-class of Binding Element with specific behaviour /// </summary> /// <param name="stream">The stream that is being closed.</param> /// <param name="ctx">The context for which the stream is being closed.</param> /// <returns>The handling status for this operation.</returns> protected virtual ChainResult OnClose(Stream stream, BindingContext ctx) { return ChainResult.Continue; }
/// <summary> /// Closes the stream for the UDP transport binding /// </summary> /// <param name="stream">The stream for this binding.</param> /// <param name="ctx">The context associated with the stream for this binding.</param> /// <returns>The handling status for this operation.</returns> protected override ChainResult OnClose( Stream stream, BindingContext ctx ) { m_udpReceiveClient.Close(); s_udpTimer.Change(-1,-1); s_udpTimer.Dispose(); return ChainResult.Handled; }
/// <summary> /// Signaled by the <ref>ProcessInputMessage</ref> method to allow subclasses to create a sub-class of Binding Element with specific behaviour /// </summary> /// <param name="message">The message being processed.</param> /// <param name="ctx">The context associated with the message being processed.</param> /// <returns>The handling status for this operation.</returns> protected abstract ChainResult OnProcessInputMessage(ref WsMessage message, BindingContext ctx);
/// <summary> /// Processes a message /// </summary> /// <param name="msg">The message being processed.</param> /// <param name="ctx">The context associated with the message.</param> /// <returns>The handling status for this operation.</returns> protected override ChainResult OnProcessOutputMessage( ref WsMessage msg, BindingContext ctx ) { if (ctx.ContextObject == null) throw new Exception(); byte []message = msg.Body as byte[]; if (message == null) return ChainResult.Abort; IPEndPoint epRemote = (IPEndPoint)ctx.ContextObject; if(!m_config.IgnoreRequestsFromThisIp && epRemote.Address == IPAddress.GetDefaultLocalAddress()) { epRemote = new IPEndPoint(IPAddress.Loopback, epRemote.Port); } System.Ext.Console.Write("UDP Message Sent To: " + epRemote.ToString()); System.Ext.Console.Write(message); try { /// Add a UDP repeat record to the current list of UDP responses to be processed /// by a common timer. lock (s_repeats) { s_repeats.Add(new UdpResend(message, epRemote, 3)); if (s_repeats.Count == 1) s_udpTimer.Change(50, 100); } } catch { return ChainResult.Abort; } return ChainResult.Handled; }
private void SendMessage(WsMessage message, BindingContext ctx) { m_binding.Elements.ProcessOutputMessage(ref message, ctx); }