first pass at adding required changes
diff --git a/src/ProtocolBuffers.Serialization/Http/MessageFormatFactory.cs b/src/ProtocolBuffers.Serialization/Http/MessageFormatFactory.cs
new file mode 100644
index 0000000..7173d47
--- /dev/null
+++ b/src/ProtocolBuffers.Serialization/Http/MessageFormatFactory.cs
@@ -0,0 +1,168 @@
+using System;
+using System.IO;
+using System.Xml;
+using System.Text;
+
+namespace Google.ProtocolBuffers.Serialization
+{
+ /// <summary>
+ /// Extensions and helpers to abstract the reading/writing of messages by a client-specified content type.
+ /// </summary>
+ public static class MessageFormatFactory
+ {
+ /// <summary>
+ /// Constructs an ICodedInputStream from the input stream based on the contentType provided
+ /// </summary>
+ /// <param name="options">Options specific to reading this message and/or content type</param>
+ /// <param name="contentType">The mime type of the input stream content</param>
+ /// <param name="input">The stream to read the message from</param>
+ /// <returns>The ICodedInputStream that can be given to the IBuilder.MergeFrom(...) method</returns>
+ public static ICodedInputStream CreateInputStream(MessageFormatOptions options, string contentType, Stream input)
+ {
+ FormatType inputType = ContentTypeToFormat(contentType, options.DefaultContentType);
+
+ ICodedInputStream codedInput;
+ if (inputType == FormatType.ProtoBuffer)
+ {
+ codedInput = CodedInputStream.CreateInstance(input);
+ }
+ else if (inputType == FormatType.Json)
+ {
+ JsonFormatReader reader = JsonFormatReader.CreateInstance(input);
+ codedInput = reader.ReadStartMessage();
+ }
+ else if (inputType == FormatType.Xml)
+ {
+ XmlFormatReader reader = XmlFormatReader.CreateInstance(input);
+ reader.RootElementName = options.XmlReaderRootElementName;
+ reader.Options = options.XmlReaderOptions;
+ codedInput = reader.ReadStartMessage();
+ }
+ else
+ throw new NotSupportedException();
+
+ return codedInput;
+ }
+
+ /// <summary>
+ /// Merges the message from the input stream based on the contentType provided
+ /// </summary>
+ /// <typeparam name="TBuilder">A type derived from IBuilderLite</typeparam>
+ /// <param name="builder">An instance of a message builder</param>
+ /// <param name="options">Options specific to reading this message and/or content type</param>
+ /// <param name="contentType">The mime type of the input stream content</param>
+ /// <param name="input">The stream to read the message from</param>
+ /// <returns>The same builder instance that was supplied in the builder parameter</returns>
+ public static TBuilder MergeFrom<TBuilder>(this TBuilder builder, MessageFormatOptions options, string contentType, Stream input) where TBuilder : IBuilderLite
+ {
+ ICodedInputStream codedInput = CreateInputStream(options, contentType, input);
+ return (TBuilder)builder.WeakMergeFrom(codedInput, options.ExtensionRegistry);
+ }
+
+ /// <summary>
+ /// Writes the message instance to the stream using the content type provided
+ /// </summary>
+ /// <param name="message">An instance of a message</param>
+ /// <param name="options">Options specific to writing this message and/or content type</param>
+ /// <param name="contentType">The mime type of the content to be written</param>
+ /// <param name="output">The stream to write the message to</param>
+ public static void WriteTo(this IMessageLite message, MessageFormatOptions options, string contentType, Stream output)
+ {
+ FormatType outputType = ContentTypeToFormat(contentType, options.DefaultContentType);
+
+ ICodedOutputStream codedOutput;
+ if (outputType == FormatType.ProtoBuffer)
+ {
+ codedOutput = CodedOutputStream.CreateInstance(output);
+ }
+ else if (outputType == FormatType.Json)
+ {
+ JsonFormatWriter writer = JsonFormatWriter.CreateInstance(output);
+ if (options.FormattedOutput)
+ {
+ writer.Formatted();
+ }
+ writer.StartMessage();
+ codedOutput = writer;
+ }
+ else if (outputType == FormatType.Xml)
+ {
+ XmlFormatWriter writer;
+ if (options.FormattedOutput)
+ {
+ writer = XmlFormatWriter.CreateInstance(output);
+ }
+ else
+ {
+ XmlWriterSettings settings = new XmlWriterSettings()
+ {
+ CheckCharacters = false,
+ NewLineHandling = NewLineHandling.Entitize,
+ OmitXmlDeclaration = true,
+ Encoding = Encoding.UTF8,
+ Indent = true,
+ IndentChars = " ",
+ NewLineChars = Environment.NewLine,
+ };
+ writer = XmlFormatWriter.CreateInstance(XmlWriter.Create(output, settings));
+ }
+ writer.RootElementName = options.XmlWriterRootElementName;
+ writer.Options = options.XmlWriterOptions;
+ writer.StartMessage();
+ codedOutput = writer;
+ }
+ else
+ throw new NotSupportedException();
+
+ message.WriteTo(codedOutput);
+
+ if (codedOutput is AbstractWriter)
+ ((AbstractWriter) codedOutput).EndMessage();
+
+ codedOutput.Flush();
+ }
+
+
+ enum FormatType { ProtoBuffer, Json, Xml };
+
+ private static FormatType ContentTypeToFormat(string contentType, string defaultType)
+ {
+ switch ((contentType ?? String.Empty).Split(';')[0].Trim().ToLower())
+ {
+ case "application/json":
+ case "application/x-json":
+ case "application/x-javascript":
+ case "text/javascript":
+ case "text/x-javascript":
+ case "text/x-json":
+ case "text/json":
+ {
+ return FormatType.Json;
+ }
+
+ case "text/xml":
+ case "application/xml":
+ {
+ return FormatType.Xml;
+ }
+
+ case "application/binary":
+ case "application/x-protobuf":
+ case "application/vnd.google.protobuf":
+ {
+ return FormatType.ProtoBuffer;
+ }
+
+ case "":
+ case null:
+ if (!String.IsNullOrEmpty(defaultType))
+ {
+ return ContentTypeToFormat(defaultType, null);
+ }
+ break;
+ }
+
+ throw new ArgumentOutOfRangeException("contentType");
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ProtocolBuffers.Serialization/Http/MessageFormatOptions.cs b/src/ProtocolBuffers.Serialization/Http/MessageFormatOptions.cs
new file mode 100644
index 0000000..5b88ac9
--- /dev/null
+++ b/src/ProtocolBuffers.Serialization/Http/MessageFormatOptions.cs
@@ -0,0 +1,82 @@
+using System;
+
+namespace Google.ProtocolBuffers.Serialization
+{
+ /// <summary>
+ /// Defines control information for the various formatting used with HTTP services
+ /// </summary>
+ public struct MessageFormatOptions
+ {
+ /// <summary>The mime type for xml content</summary>
+ /// <remarks>Other valid xml mime types include: application/binary, application/x-protobuf</remarks>
+ public const string ContentTypeProtoBuffer = "application/vnd.google.protobuf";
+
+ /// <summary>The mime type for xml content</summary>
+ /// <remarks>Other valid xml mime types include: text/xml</remarks>
+ public const string ContentTypeXml = "application/xml";
+
+ /// <summary>The mime type for json content</summary>
+ /// <remarks>
+ /// Other valid json mime types include: application/json, application/x-json,
+ /// application/x-javascript, text/javascript, text/x-javascript, text/x-json, text/json
+ /// </remarks>
+ public const string ContentTypeJson = "application/json";
+
+ private string _defaultContentType;
+ private string _xmlReaderRootElementName;
+ private string _xmlWriterRootElementName;
+ private ExtensionRegistry _extensionRegistry;
+
+ /// <summary>
+ /// The default content type to use if the input type is null or empty. If this
+ /// value is not supplied an ArgumentOutOfRangeException exception will be raised.
+ /// </summary>
+ public string DefaultContentType
+ {
+ get { return _defaultContentType ?? String.Empty; }
+ set { _defaultContentType = value; }
+ }
+
+ /// <summary>
+ /// The extension registry to use when reading messages
+ /// </summary>
+ public ExtensionRegistry ExtensionRegistry
+ {
+ get { return _extensionRegistry ?? ExtensionRegistry.Empty; }
+ set { _extensionRegistry = value; }
+ }
+
+ /// <summary>
+ /// The name of the xml root element when reading messages
+ /// </summary>
+ public string XmlReaderRootElementName
+ {
+ get { return _xmlReaderRootElementName ?? XmlFormatReader.DefaultRootElementName; }
+ set { _xmlReaderRootElementName = value; }
+ }
+
+ /// <summary>
+ /// Xml reader options
+ /// </summary>
+ public XmlReaderOptions XmlReaderOptions { get; set; }
+
+ /// <summary>
+ /// True to use formatted output including new-lines and default indentation
+ /// </summary>
+ public bool FormattedOutput { get; set; }
+
+ /// <summary>
+ /// The name of the xml root element when writing messages
+ /// </summary>
+ public string XmlWriterRootElementName
+ {
+ get { return _xmlWriterRootElementName ?? XmlFormatWriter.DefaultRootElementName; }
+ set { _xmlWriterRootElementName = value; }
+ }
+
+ /// <summary>
+ /// Xml writer options
+ /// </summary>
+ public XmlWriterOptions XmlWriterOptions { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/ProtocolBuffers.Serialization/Http/ServiceExtensions.cs b/src/ProtocolBuffers.Serialization/Http/ServiceExtensions.cs
new file mode 100644
index 0000000..3ca9964
--- /dev/null
+++ b/src/ProtocolBuffers.Serialization/Http/ServiceExtensions.cs
@@ -0,0 +1,32 @@
+using System.Collections.Generic;
+using System.Text;
+using Google.ProtocolBuffers;
+using System.IO;
+
+namespace Google.ProtocolBuffers.Serialization
+{
+ /// <summary>
+ /// Extensions for the IRpcServerStub
+ /// </summary>
+ public static class ServiceExtensions
+ {
+ /// <summary>
+ /// Used to implement a service endpoint on an HTTP server. This works with services generated with the
+ /// service_generator_type option set to IRPCDISPATCH.
+ /// </summary>
+ /// <param name="stub">The service execution stub</param>
+ /// <param name="methodName">The name of the method being invoked</param>
+ /// <param name="options">optional arguments for the format reader/writer</param>
+ /// <param name="contentType">The mime type for the input stream</param>
+ /// <param name="input">The input stream</param>
+ /// <param name="responseType">The mime type for the output stream</param>
+ /// <param name="output">The output stream</param>
+ public static void HttpCallMethod(this IRpcServerStub stub, string methodName, MessageFormatOptions options,
+ string contentType, Stream input, string responseType, Stream output)
+ {
+ ICodedInputStream codedInput = MessageFormatFactory.CreateInputStream(options, contentType, input);
+ IMessageLite response = stub.CallMethod(methodName, codedInput, options.ExtensionRegistry);
+ response.WriteTo(options, responseType, output);
+ }
+ }
+}