1 // 2 // WebMessageEncoder.cs 3 // 4 // Author: 5 // Atsushi Enomoto <atsushi@ximian.com> 6 // 7 // Copyright (C) 2008 Novell, Inc (http://www.novell.com) 8 // 9 // Permission is hereby granted, free of charge, to any person obtaining 10 // a copy of this software and associated documentation files (the 11 // "Software"), to deal in the Software without restriction, including 12 // without limitation the rights to use, copy, modify, merge, publish, 13 // distribute, sublicense, and/or sell copies of the Software, and to 14 // permit persons to whom the Software is furnished to do so, subject to 15 // the following conditions: 16 // 17 // The above copyright notice and this permission notice shall be 18 // included in all copies or substantial portions of the Software. 19 // 20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 27 // 28 using System; 29 using System.IO; 30 using System.Runtime.Serialization.Json; 31 using System.ServiceModel; 32 using System.ServiceModel.Dispatcher; 33 using System.Text; 34 using System.Xml; 35 36 namespace System.ServiceModel.Channels 37 { 38 internal class WebMessageEncoder : MessageEncoder 39 { 40 internal const string ScriptPropertyName = "618BC2B0-38AA-21A3-DB4A-404FC87B9B11"; // randomly generated 41 42 WebMessageEncodingBindingElement source; 43 WebMessageEncoder(WebMessageEncodingBindingElement source)44 public WebMessageEncoder (WebMessageEncodingBindingElement source) 45 { 46 this.source = source; 47 } 48 49 public override string ContentType { 50 #if MOBILE 51 get { return MediaType; } 52 #else 53 get { return MediaType + "; charset=" + source.WriteEncoding.HeaderName; } 54 #endif 55 } 56 57 // FIXME: find out how it can be customized. 58 public override string MediaType { 59 get { return "application/xml"; } 60 } 61 62 public override MessageVersion MessageVersion { 63 get { return MessageVersion.None; } 64 } 65 IsContentTypeSupported(string contentType)66 public override bool IsContentTypeSupported (string contentType) 67 { 68 if (contentType == null) 69 throw new ArgumentNullException ("contentType"); 70 return true; // anything is accepted. 71 } 72 ReadMessage(ArraySegment<byte> buffer, BufferManager bufferManager, string contentType)73 public override Message ReadMessage (ArraySegment<byte> buffer, BufferManager bufferManager, string contentType) 74 { 75 throw new NotImplementedException (); 76 } 77 ReadMessage(Stream stream, int maxSizeOfHeaders, string contentType)78 public override Message ReadMessage (Stream stream, int maxSizeOfHeaders, string contentType) 79 { 80 if (stream == null) 81 throw new ArgumentNullException ("stream"); 82 83 contentType = contentType ?? "application/octet-stream"; 84 85 Encoding enc = Encoding.UTF8; 86 var ct = new System.Net.Mime.ContentType (contentType); 87 if (ct.CharSet != null) 88 enc = Encoding.GetEncoding (ct.CharSet); 89 90 WebContentFormat fmt = WebContentFormat.Xml; 91 if (source.ContentTypeMapper != null) 92 fmt = source.ContentTypeMapper.GetMessageFormatForContentType (contentType); 93 else { 94 switch (ct.MediaType) { 95 case "application/json": 96 fmt = WebContentFormat.Json; 97 break; 98 case "application/xml": 99 fmt = WebContentFormat.Xml; 100 break; 101 default: 102 fmt = WebContentFormat.Raw; 103 break; 104 } 105 } 106 107 Message msg = null; 108 WebBodyFormatMessageProperty wp = null; 109 switch (fmt) { 110 case WebContentFormat.Xml: 111 // FIXME: is it safe/unsafe/required to keep XmlReader open? 112 msg = Message.CreateMessage (MessageVersion.None, null, XmlReader.Create (new StreamReader (stream, enc))); 113 wp = new WebBodyFormatMessageProperty (WebContentFormat.Xml); 114 break; 115 case WebContentFormat.Json: 116 // FIXME: is it safe/unsafe/required to keep XmlReader open? 117 #if MOBILE 118 msg = Message.CreateMessage (MessageVersion.None, null, JsonReaderWriterFactory.CreateJsonReader (stream, source.ReaderQuotas)); 119 #else 120 msg = Message.CreateMessage (MessageVersion.None, null, JsonReaderWriterFactory.CreateJsonReader (stream, enc, source.ReaderQuotas, null)); 121 #endif 122 wp = new WebBodyFormatMessageProperty (WebContentFormat.Json); 123 break; 124 case WebContentFormat.Raw: 125 msg = new WebMessageFormatter.RawMessage (stream); 126 wp = new WebBodyFormatMessageProperty (WebContentFormat.Raw); 127 break; 128 default: 129 throw new SystemException ("INTERNAL ERROR: cannot determine content format"); 130 } 131 if (wp != null) 132 msg.Properties.Add (WebBodyFormatMessageProperty.Name, wp); 133 msg.Properties.Encoder = this; 134 return msg; 135 } 136 GetContentFormat(Message message)137 WebContentFormat GetContentFormat (Message message) 138 { 139 string name = WebBodyFormatMessageProperty.Name; 140 if (message.Properties.ContainsKey (name)) 141 return ((WebBodyFormatMessageProperty) message.Properties [name]).Format; 142 143 switch (MediaType) { 144 case "application/xml": 145 case "text/xml": 146 return WebContentFormat.Xml; 147 case "application/json": 148 case "text/json": 149 return WebContentFormat.Json; 150 case "application/octet-stream": 151 return WebContentFormat.Raw; 152 default: 153 return WebContentFormat.Default; 154 } 155 } 156 WriteMessage(Message message, Stream stream)157 public override void WriteMessage (Message message, Stream stream) 158 { 159 if (message == null) 160 throw new ArgumentNullException ("message"); 161 162 // Handle /js and /jsdebug as the special cases. 163 var script = message.Properties [ScriptPropertyName] as string; 164 if (script != null) { 165 var bytes = source.WriteEncoding.GetBytes (script); 166 stream.Write (bytes, 0, bytes.Length); 167 return; 168 } 169 170 if (!MessageVersion.Equals (message.Version)) 171 throw new ProtocolException (String.Format ("MessageVersion {0} is not supported", message.Version)); 172 if (stream == null) 173 throw new ArgumentNullException ("stream"); 174 175 switch (GetContentFormat (message)) { 176 case WebContentFormat.Xml: 177 #if MOBILE 178 using (XmlWriter w = XmlDictionaryWriter.CreateDictionaryWriter (XmlWriter.Create (new StreamWriter (stream, source.WriteEncoding)))) 179 message.WriteMessage (w); 180 #else 181 using (XmlWriter w = XmlDictionaryWriter.CreateTextWriter (stream, source.WriteEncoding, false)) 182 message.WriteMessage (w); 183 #endif 184 break; 185 case WebContentFormat.Json: 186 using (XmlWriter w = JsonReaderWriterFactory.CreateJsonWriter (stream, source.WriteEncoding, false)) 187 message.WriteMessage (w); 188 break; 189 case WebContentFormat.Raw: 190 var rmsg = (WebMessageFormatter.RawMessage) message; 191 var src = rmsg.Stream; 192 if (src == null) // null output 193 break; 194 195 int len = 0; 196 byte [] buffer = new byte [4096]; 197 while ((len = src.Read (buffer, 0, buffer.Length)) > 0) 198 stream.Write (buffer, 0, len); 199 break; 200 case WebContentFormat.Default: 201 throw new SystemException ("INTERNAL ERROR: cannot determine content format"); 202 } 203 } 204 WriteMessage(Message message, int maxMessageSize, BufferManager bufferManager, int messageOffset)205 public override ArraySegment<byte> WriteMessage (Message message, int maxMessageSize, BufferManager bufferManager, 206 int messageOffset) 207 { 208 throw new NotImplementedException (); 209 } 210 } 211 } 212