[mapguide-commits] r7756 - in sandbox/jng/streaming_v2: Common/Foundation/System Web/src/ApacheAgent Web/src/CgiAgent Web/src/HttpHandler Web/src/IsapiAgent

svn_mapguide at osgeo.org svn_mapguide at osgeo.org
Sun Aug 11 07:41:25 PDT 2013


Author: jng
Date: 2013-08-11 07:41:25 -0700 (Sun, 11 Aug 2013)
New Revision: 7756

Added:
   sandbox/jng/streaming_v2/Web/src/HttpHandler/ReaderByteSourceImpl.cpp
   sandbox/jng/streaming_v2/Web/src/HttpHandler/ReaderByteSourceImpl.h
Modified:
   sandbox/jng/streaming_v2/Common/Foundation/System/ByteSourceImpl.h
   sandbox/jng/streaming_v2/Web/src/ApacheAgent/ApacheReaderStreamer.cpp
   sandbox/jng/streaming_v2/Web/src/ApacheAgent/ApacheReaderStreamer.h
   sandbox/jng/streaming_v2/Web/src/ApacheAgent/ApacheResponseHandler.cpp
   sandbox/jng/streaming_v2/Web/src/CgiAgent/CgiReaderStreamer.cpp
   sandbox/jng/streaming_v2/Web/src/CgiAgent/CgiReaderStreamer.h
   sandbox/jng/streaming_v2/Web/src/CgiAgent/CgiResponseHandler.cpp
   sandbox/jng/streaming_v2/Web/src/HttpHandler/HttpHandler.vcxproj
   sandbox/jng/streaming_v2/Web/src/HttpHandler/HttpHandler.vcxproj.filters
   sandbox/jng/streaming_v2/Web/src/HttpHandler/HttpHandlerBuild.cpp
   sandbox/jng/streaming_v2/Web/src/HttpHandler/HttpReaderStreamer.cpp
   sandbox/jng/streaming_v2/Web/src/HttpHandler/HttpReaderStreamer.h
   sandbox/jng/streaming_v2/Web/src/HttpHandler/HttpResourceStrings.cpp
   sandbox/jng/streaming_v2/Web/src/HttpHandler/HttpResourceStrings.h
   sandbox/jng/streaming_v2/Web/src/HttpHandler/HttpResult.h
   sandbox/jng/streaming_v2/Web/src/HttpHandler/HttpSelectFeatures.cpp
   sandbox/jng/streaming_v2/Web/src/HttpHandler/HttpSelectFeaturesSpatially.cpp
   sandbox/jng/streaming_v2/Web/src/IsapiAgent/IsapiReaderStreamer.cpp
   sandbox/jng/streaming_v2/Web/src/IsapiAgent/IsapiReaderStreamer.h
   sandbox/jng/streaming_v2/Web/src/IsapiAgent/IsapiResponseHandler.cpp
Log:
Refactor the streaming mechanism.
 - Modify MgHttpReaderStreamer (and its derivatives) to work off a MgByteReader instead of a MgReader
 - dllexport the ByteSourceImpl class from the Foundation library
 - Implement a new MgReaderByteSourceImpl class that inherits from ByteSourceImpl. It takes a MgReader and mime type as input and provides an adapter that allows a MgReader to be streamed out as a MgByteReader. 

The benefit of this approach is that if one were to implement a mapagent handler outside of CGI/ISAPI/Apache, they don't have to implement a specialized MgReader streaming class. Any HTTP operation that would return MgReader now returns a MgByteReader instead so output logic of a SELECTFEATURES or SELECTAGGREGATES operation is the same as any other MgByteReader. The only difference here is that the handler should check for a chunked response hint from the header of the MgHttpResponse and apply appropriate chunked response headers if this hint exists. The existing Apache/CGI/ISAPI handlers have been modified to check for this hint.

Modified: sandbox/jng/streaming_v2/Common/Foundation/System/ByteSourceImpl.h
===================================================================
--- sandbox/jng/streaming_v2/Common/Foundation/System/ByteSourceImpl.h	2013-08-09 16:16:41 UTC (rev 7755)
+++ sandbox/jng/streaming_v2/Common/Foundation/System/ByteSourceImpl.h	2013-08-11 14:41:25 UTC (rev 7756)
@@ -23,7 +23,7 @@
 /// \brief
 /// Placeholder for various ByteSourceInfo derived classes, which supply
 /// the actual access to the bytes in the source
-class ByteSourceImpl
+class MG_FOUNDATION_API ByteSourceImpl
 {
 public:
 

Modified: sandbox/jng/streaming_v2/Web/src/ApacheAgent/ApacheReaderStreamer.cpp
===================================================================
--- sandbox/jng/streaming_v2/Web/src/ApacheAgent/ApacheReaderStreamer.cpp	2013-08-09 16:16:41 UTC (rev 7755)
+++ sandbox/jng/streaming_v2/Web/src/ApacheAgent/ApacheReaderStreamer.cpp	2013-08-11 14:41:25 UTC (rev 7756)
@@ -19,8 +19,8 @@
 #include "http_protocol.h"
 
 
-ApacheReaderStreamer::ApacheReaderStreamer(request_rec* rec, MgReader* reader, CREFSTRING format) :
-    MgHttpReaderStreamer(reader, format), m_r(rec)
+ApacheReaderStreamer::ApacheReaderStreamer(request_rec* rec, MgByteReader* reader) :
+    MgHttpReaderStreamer(reader), m_r(rec)
 {
 }
 

Modified: sandbox/jng/streaming_v2/Web/src/ApacheAgent/ApacheReaderStreamer.h
===================================================================
--- sandbox/jng/streaming_v2/Web/src/ApacheAgent/ApacheReaderStreamer.h	2013-08-09 16:16:41 UTC (rev 7755)
+++ sandbox/jng/streaming_v2/Web/src/ApacheAgent/ApacheReaderStreamer.h	2013-08-11 14:41:25 UTC (rev 7756)
@@ -26,7 +26,7 @@
 class ApacheReaderStreamer : public MgHttpReaderStreamer
 {
 public:
-    ApacheReaderStreamer(request_rec* rec, MgReader* reader, CREFSTRING format);
+    ApacheReaderStreamer(request_rec* rec, MgByteReader* reader);
     virtual ~ApacheReaderStreamer();
 
 protected:

Modified: sandbox/jng/streaming_v2/Web/src/ApacheAgent/ApacheResponseHandler.cpp
===================================================================
--- sandbox/jng/streaming_v2/Web/src/ApacheAgent/ApacheResponseHandler.cpp	2013-08-09 16:16:41 UTC (rev 7755)
+++ sandbox/jng/streaming_v2/Web/src/ApacheAgent/ApacheResponseHandler.cpp	2013-08-11 14:41:25 UTC (rev 7756)
@@ -98,7 +98,6 @@
             m_r->content_type = apr_pstrdup(m_r->pool, tempHeader);
         }
 
-        Ptr<MgReader> outputDataReader;
         Ptr<MgByteReader> outputReader;
         Ptr<MgDisposable> resultObj = result->GetResultObject();
         MgDisposable* pResultObj = (MgDisposable*)resultObj;
@@ -107,22 +106,10 @@
         {
             outputReader = (MgByteReader*) SAFE_ADDREF(pResultObj);
         }
-        else if (NULL != dynamic_cast<MgFeatureReader*>(pResultObj))
-        {
-            outputDataReader = SAFE_ADDREF((MgFeatureReader*)pResultObj); //Need to AddRef because there's now 2 references on this pointer
-        }
         else if (NULL != dynamic_cast<MgStringCollection*>(pResultObj))
         {
             outputReader = ((MgStringCollection*)pResultObj)->ToXml();
         }
-        else if (NULL != dynamic_cast<MgSqlDataReader*>(pResultObj))
-        {
-            outputDataReader = SAFE_ADDREF((MgSqlDataReader*)pResultObj); //Need to AddRef because there's now 2 references on this pointer
-        }
-        else if (NULL != dynamic_cast<MgDataReader*>(pResultObj))
-        {
-            outputDataReader = SAFE_ADDREF((MgDataReader*)pResultObj); //Need to AddRef because there's now 2 references on this pointer
-        }
         else if (NULL != dynamic_cast<MgSpatialContextReader*>(pResultObj))
         {
             outputReader = ((MgSpatialContextReader*)pResultObj)->ToXml();
@@ -145,25 +132,30 @@
             ap_send_http_header(m_r);
             ap_rwrite(utf8.c_str(), (int)utf8.length(), m_r);
         }
-        else if (outputDataReader != NULL)
-        {
-            ApacheReaderStreamer ars(m_r, outputDataReader, result->GetResultContentType());
-            ars.StreamResult();
-        }
         else if (outputReader != NULL)
         {
-            INT64 outLen = outputReader->GetLength();
-            sprintf(tempHeader, "%d", (INT32)outLen);
-            apr_table_set(m_r->headers_out, MapAgentStrings::ContentLengthKey, tempHeader);
-            ap_send_http_header(m_r);
-
-            unsigned char buf[4096];
-            int nBytes = outputReader->Read(buf,4096);
-            while (nBytes > 0)
+            Ptr<MgHttpHeader> respHeader = response->GetHeader();
+            //Check for chunking hint
+            if (respHeader->GetHeaderValue(MgHttpResourceStrings::hrhnTransfer_Encoding) == MgHttpResourceStrings::hrhnChunked)
             {
-                ap_rwrite(buf, nBytes, m_r);
-                nBytes = outputReader->Read(buf,4096);
+                ApacheReaderStreamer ars(m_r, outputReader);
+                ars.StreamResult();
             }
+            else
+            {
+                INT64 outLen = outputReader->GetLength();
+                sprintf(tempHeader, "%d", (INT32)outLen);
+                apr_table_set(m_r->headers_out, MapAgentStrings::ContentLengthKey, tempHeader);
+                ap_send_http_header(m_r);
+
+                unsigned char buf[4096];
+                int nBytes = outputReader->Read(buf,4096);
+                while (nBytes > 0)
+                {
+                    ap_rwrite(buf, nBytes, m_r);
+                    nBytes = outputReader->Read(buf,4096);
+                }
+            }
         }
         else
         {

Modified: sandbox/jng/streaming_v2/Web/src/CgiAgent/CgiReaderStreamer.cpp
===================================================================
--- sandbox/jng/streaming_v2/Web/src/CgiAgent/CgiReaderStreamer.cpp	2013-08-09 16:16:41 UTC (rev 7755)
+++ sandbox/jng/streaming_v2/Web/src/CgiAgent/CgiReaderStreamer.cpp	2013-08-11 14:41:25 UTC (rev 7756)
@@ -22,8 +22,8 @@
 extern void DumpMessage2(const char* msg);
 
 
-CgiReaderStreamer::CgiReaderStreamer(MgReader* reader, CREFSTRING format) :
-    MgHttpReaderStreamer(reader, format),
+CgiReaderStreamer::CgiReaderStreamer(MgByteReader* reader) :
+    MgHttpReaderStreamer(reader),
     m_bEndOfStream(false)
 {
 }

Modified: sandbox/jng/streaming_v2/Web/src/CgiAgent/CgiReaderStreamer.h
===================================================================
--- sandbox/jng/streaming_v2/Web/src/CgiAgent/CgiReaderStreamer.h	2013-08-09 16:16:41 UTC (rev 7755)
+++ sandbox/jng/streaming_v2/Web/src/CgiAgent/CgiReaderStreamer.h	2013-08-11 14:41:25 UTC (rev 7756)
@@ -24,7 +24,7 @@
 class CgiReaderStreamer : public MgHttpReaderStreamer
 {
 public:
-    CgiReaderStreamer(MgReader* reader, CREFSTRING format);
+    CgiReaderStreamer(MgByteReader* reader);
     virtual ~CgiReaderStreamer();
 
 protected:

Modified: sandbox/jng/streaming_v2/Web/src/CgiAgent/CgiResponseHandler.cpp
===================================================================
--- sandbox/jng/streaming_v2/Web/src/CgiAgent/CgiResponseHandler.cpp	2013-08-09 16:16:41 UTC (rev 7755)
+++ sandbox/jng/streaming_v2/Web/src/CgiAgent/CgiResponseHandler.cpp	2013-08-11 14:41:25 UTC (rev 7756)
@@ -102,7 +102,6 @@
             printf(MapAgentStrings::ContentTypeHeader, MapAgentStrings::TextPlain, MapAgentStrings::Utf8Text);
         }
 
-        Ptr<MgReader> outputDataReader;
         Ptr<MgByteReader> outputReader;
         Ptr<MgDisposable> resultObj = result->GetResultObject();
         MgDisposable* pResultObj = (MgDisposable*)resultObj;
@@ -111,22 +110,10 @@
         {
             outputReader = (MgByteReader*) SAFE_ADDREF(pResultObj);
         }
-        else if (NULL != dynamic_cast<MgFeatureReader*>(pResultObj))
-        {
-            outputDataReader = SAFE_ADDREF((MgFeatureReader*)pResultObj); //Need to AddRef because there's now 2 references on this pointer
-        }
         else if (NULL != dynamic_cast<MgStringCollection*>(pResultObj))
         {
             outputReader = ((MgStringCollection*)pResultObj)->ToXml();
         }
-        else if (NULL != dynamic_cast<MgSqlDataReader*>(pResultObj))
-        {
-            outputDataReader = SAFE_ADDREF((MgSqlDataReader*)pResultObj); //Need to AddRef because there's now 2 references on this pointer
-        }
-        else if (NULL != dynamic_cast<MgDataReader*>(pResultObj))
-        {
-            outputDataReader = SAFE_ADDREF((MgDataReader*)pResultObj); //Need to AddRef because there's now 2 references on this pointer
-        }
         else if (NULL != dynamic_cast<MgSpatialContextReader*>(pResultObj))
         {
             outputReader = ((MgSpatialContextReader*)pResultObj)->ToXml();
@@ -146,23 +133,28 @@
             printf(MapAgentStrings::ContentLengthHeader, utf8.length());
             printf("\r\n%s",utf8.c_str());
         }
-        else if (outputDataReader != NULL)
-        {
-            CgiReaderStreamer crs(outputDataReader, result->GetResultContentType());
-            crs.StreamResult();
-        }
         else if (outputReader != NULL)
         {
-            INT64 outLen = outputReader->GetLength();
-            printf(MapAgentStrings::ContentLengthHeader,(INT32)outLen);
-            printf("\r\n");
-            unsigned char buf[4096];
-            int nBytes = outputReader->Read(buf,4096);
-            while (nBytes > 0)
+            Ptr<MgHttpHeader> respHeader = response->GetHeader();
+            //Check for chunking hint
+            if (respHeader->GetHeaderValue(MgHttpResourceStrings::hrhnTransfer_Encoding) == MgHttpResourceStrings::hrhnChunked)
             {
-                fwrite(buf, 1, nBytes, stdout);
-                nBytes = outputReader->Read(buf,4096);
+                CgiReaderStreamer crs(outputReader);
+                crs.StreamResult();
             }
+            else
+            {
+                INT64 outLen = outputReader->GetLength();
+                printf(MapAgentStrings::ContentLengthHeader,(INT32)outLen);
+                printf("\r\n");
+                unsigned char buf[4096];
+                int nBytes = outputReader->Read(buf,4096);
+                while (nBytes > 0)
+                {
+                    fwrite(buf, 1, nBytes, stdout);
+                    nBytes = outputReader->Read(buf,4096);
+                }
+            }
         }
         else
         {

Modified: sandbox/jng/streaming_v2/Web/src/HttpHandler/HttpHandler.vcxproj
===================================================================
--- sandbox/jng/streaming_v2/Web/src/HttpHandler/HttpHandler.vcxproj	2013-08-09 16:16:41 UTC (rev 7755)
+++ sandbox/jng/streaming_v2/Web/src/HttpHandler/HttpHandler.vcxproj	2013-08-11 14:41:25 UTC (rev 7756)
@@ -712,6 +712,12 @@
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
     </ClCompile>
+    <ClCompile Include="ReaderByteSourceImpl.cpp">
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
+    </ClCompile>
     <ClCompile Include="WfsFeatureDefinitions.cpp">
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
@@ -1031,6 +1037,7 @@
     <ClInclude Include="OgcWmsException.h" />
     <ClInclude Include="OgcWmsServer.h" />
     <ClInclude Include="HttpReaderStreamer.h" />
+    <ClInclude Include="ReaderByteSourceImpl.h" />
     <ClInclude Include="ResponseStream.h" />
     <ClInclude Include="Stream.h" />
     <ClInclude Include="StringStream.h" />

Modified: sandbox/jng/streaming_v2/Web/src/HttpHandler/HttpHandler.vcxproj.filters
===================================================================
--- sandbox/jng/streaming_v2/Web/src/HttpHandler/HttpHandler.vcxproj.filters	2013-08-09 16:16:41 UTC (rev 7755)
+++ sandbox/jng/streaming_v2/Web/src/HttpHandler/HttpHandler.vcxproj.filters	2013-08-11 14:41:25 UTC (rev 7756)
@@ -37,6 +37,9 @@
     <Filter Include="Web Applications">
       <UniqueIdentifier>{e21c5925-6c01-4fe4-89d2-1e95e2b3d79e}</UniqueIdentifier>
     </Filter>
+    <Filter Include="Byte Source Adapters">
+      <UniqueIdentifier>{0f2e34d9-18f7-49af-b11f-8d3c874192ec}</UniqueIdentifier>
+    </Filter>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="HttpApplyResourcePackage.cpp">
@@ -383,6 +386,9 @@
     <ClCompile Include="HttpCreateRuntimeMap.cpp">
       <Filter>Mapping Service</Filter>
     </ClCompile>
+    <ClCompile Include="ReaderByteSourceImpl.cpp">
+      <Filter>Byte Source Adapters</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="HttpApplyResourcePackage.h">
@@ -754,6 +760,9 @@
     <ClInclude Include="HttpCreateRuntimeMap.h">
       <Filter>Mapping Service</Filter>
     </ClInclude>
+    <ClInclude Include="ReaderByteSourceImpl.h">
+      <Filter>Byte Source Adapters</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="HttpHandler.rc" />

Modified: sandbox/jng/streaming_v2/Web/src/HttpHandler/HttpHandlerBuild.cpp
===================================================================
--- sandbox/jng/streaming_v2/Web/src/HttpHandler/HttpHandlerBuild.cpp	2013-08-09 16:16:41 UTC (rev 7755)
+++ sandbox/jng/streaming_v2/Web/src/HttpHandler/HttpHandlerBuild.cpp	2013-08-11 14:41:25 UTC (rev 7756)
@@ -125,6 +125,8 @@
 #include "HttpEnumerateUnmanagedData.cpp"
 #include "WmsMapUtil.cpp"
 
+#include "ReaderByteSourceImpl.cpp"
+
 // JSON conversion files
 #include "XmlJsonConvert.cpp"
 #include "JsonDoc.cpp"

Modified: sandbox/jng/streaming_v2/Web/src/HttpHandler/HttpReaderStreamer.cpp
===================================================================
--- sandbox/jng/streaming_v2/Web/src/HttpHandler/HttpReaderStreamer.cpp	2013-08-09 16:16:41 UTC (rev 7755)
+++ sandbox/jng/streaming_v2/Web/src/HttpHandler/HttpReaderStreamer.cpp	2013-08-11 14:41:25 UTC (rev 7756)
@@ -18,11 +18,11 @@
 #include "HttpHandler.h"
 #include "XmlJsonConvert.h"
 
+#define CHUNK_SIZE 8192
 
-MgHttpReaderStreamer::MgHttpReaderStreamer(MgReader* reader, CREFSTRING format)
+MgHttpReaderStreamer::MgHttpReaderStreamer(MgByteReader* reader)
 {
     m_reader = SAFE_ADDREF(reader);
-    m_format = format;
 }
 
 
@@ -53,126 +53,13 @@
 
     SetChunkedEncoding();
 
-    if (m_format == MgMimeType::Json)
+    unsigned char buf[CHUNK_SIZE] = { 0 };
+    INT32 read = m_reader->Read(buf, CHUNK_SIZE);
+    while (read > 0)
     {
-        std::string buf;
-        std::string jsonbuf;
-
-        //How this looks:
-        //
-        // {                            //outer JSON start
-        //   "ResponseElementName":     //root element name
-        //     {                        //root JSON property start
-        //      <header JSON pair>,
-        //      "BodyElementName":[     //body JSON array start
-        //
-        jsonbuf = "{\"";
-        jsonbuf += m_reader->GetResponseElementName();
-        jsonbuf += "\":{";
-        m_reader->HeaderToStringUtf8(buf);
-        std::string jsonbuf2;
-        ToJson(buf, jsonbuf2);
-        //This will have redudant outer { }, so strip them
-        jsonbuf2.erase(0, 1);
-        jsonbuf2.erase(jsonbuf2.length() - 2, 1);
-        //HACK: To match the original output, we have to array-ify this object (crazy? yes!)
-        //
-        //We currently have something like this
-        //
-        // "HeaderElementName":{
-        //    <prop1>:<val1>,
-        //    <prop2>:<val2>
-        // }
-        //
-        //We have to change it to this
-        //
-        // "HeaderElementName":[{
-        //    <prop1>:<val1>,
-        //    <prop2>:<val2>
-        // }]
-
-        //Find first instance of ": and insert [ after it. We use ": because a feature
-        //reader puts out xs:schema as the header element name
-        jsonbuf2.insert(jsonbuf2.find("\":") + 2, "[");
-        //Append ] to the end
-        jsonbuf2.append("]");
-
-        jsonbuf += jsonbuf2;
-        jsonbuf += ",\"";
-        jsonbuf += m_reader->GetBodyElementName();
-        jsonbuf += "\":[";
-
-        WriteChunk(jsonbuf.c_str(), jsonbuf.length());
-
-        bool bNext = m_reader->ReadNext();
-        while(bNext)
-        {
-            buf.clear();
-            jsonbuf.clear();
-            m_reader->CurrentToStringUtf8(buf);
-            //The body is a valid full XML element, so no need for gymnastics like its
-            //surrounding elements
-            ToJson(buf, jsonbuf);
-
-            //Strip outer { }
-            jsonbuf.erase(0, 1);
-            jsonbuf.erase(jsonbuf.length() - 2, 1);
-            //HACK: Same as the header, this needs to be array-ified to match the old output
-            //
-            //Find first instance of ": and insert [ after it.
-            jsonbuf.insert(jsonbuf.find("\":") + 2, "[");
-            //Append ] to the end
-            jsonbuf.append("]");
-            //Put back in outer { }
-            jsonbuf = "{" + jsonbuf;
-            jsonbuf += "}";
-
-            bNext = m_reader->ReadNext();
-            if (bNext)
-                jsonbuf += ",";
-            WriteChunk(jsonbuf.c_str(), jsonbuf.length());
-        }
-
-        buf.clear();
-        jsonbuf.clear();
-
-        // How this looks:
-        //      ]   //End of body JSON array
-        //    }     //End of root JSON property
-        // }        //End of outer JSON
-        jsonbuf = "]}}";
-        WriteChunk(jsonbuf.c_str(), jsonbuf.length());
+        WriteChunk((char*)buf, read);
+        read = m_reader->Read(buf, CHUNK_SIZE);
     }
-    else if (m_format == MgMimeType::Xml)
-    {
-        std::string buf;
-        m_reader->ResponseStartUtf8(buf);
-        m_reader->HeaderToStringUtf8(buf);
-        m_reader->BodyStartUtf8(buf);
 
-        WriteChunk(buf.c_str(), buf.length());
-
-        while(m_reader->ReadNext())
-        {
-            buf.clear();
-            m_reader->CurrentToStringUtf8(buf);
-            WriteChunk(buf.c_str(), buf.length());
-        }
-
-        buf.clear();
-
-        m_reader->BodyEndUtf8(buf);
-        m_reader->ResponseEndUtf8(buf);
-
-        WriteChunk(buf.c_str(), buf.length());
-    }
-
     MG_CATCH_AND_THROW(L"MgHttpReaderStreamer.StreamResult");
-}
-
-
-void MgHttpReaderStreamer::ToJson(string& xmlString, string& jsonString)
-{
-    MgXmlJsonConvert convert;
-    convert.ToJson(xmlString, jsonString);
-}
+}
\ No newline at end of file

Modified: sandbox/jng/streaming_v2/Web/src/HttpHandler/HttpReaderStreamer.h
===================================================================
--- sandbox/jng/streaming_v2/Web/src/HttpHandler/HttpReaderStreamer.h	2013-08-09 16:16:41 UTC (rev 7755)
+++ sandbox/jng/streaming_v2/Web/src/HttpHandler/HttpReaderStreamer.h	2013-08-11 14:41:25 UTC (rev 7756)
@@ -29,16 +29,14 @@
     virtual ~MgHttpReaderStreamer();
 
 protected:
-    MgHttpReaderStreamer(MgReader* reader, CREFSTRING format);
+    MgHttpReaderStreamer(MgByteReader* reader);
     virtual void SetChunkedEncoding();
     virtual void WriteChunk(const char* str, size_t length);
     virtual void Dispose() { delete this; }
     virtual void EndStream();
     
 private:
-    void ToJson(string& xmlString, string& jsonString);
-    Ptr<MgReader> m_reader;
-    STRING m_format;
+    Ptr<MgByteReader> m_reader;
 };
 
 #endif

Modified: sandbox/jng/streaming_v2/Web/src/HttpHandler/HttpResourceStrings.cpp
===================================================================
--- sandbox/jng/streaming_v2/Web/src/HttpHandler/HttpResourceStrings.cpp	2013-08-09 16:16:41 UTC (rev 7755)
+++ sandbox/jng/streaming_v2/Web/src/HttpHandler/HttpResourceStrings.cpp	2013-08-11 14:41:25 UTC (rev 7756)
@@ -37,6 +37,8 @@
 const STRING MgHttpResourceStrings::hrhnPublic = L"Public";
 const STRING MgHttpResourceStrings::hrhnContent_Length = L"Content-Length";
 const STRING MgHttpResourceStrings::hrhnContent_Type = L"Content-Type";
+const STRING MgHttpResourceStrings::hrhnTransfer_Encoding = L"Transfer-Encoding";
+const STRING MgHttpResourceStrings::hrhnChunked = L"chunked";
 const STRING MgHttpResourceStrings::hrhnContent_Transfer_Encoding = L"Content-Transfer-Encoding";
 const STRING MgHttpResourceStrings::hrhnContent_Encoding = L"Content-Encoding";
 const STRING MgHttpResourceStrings::hrhnDate = L"Date";

Modified: sandbox/jng/streaming_v2/Web/src/HttpHandler/HttpResourceStrings.h
===================================================================
--- sandbox/jng/streaming_v2/Web/src/HttpHandler/HttpResourceStrings.h	2013-08-09 16:16:41 UTC (rev 7755)
+++ sandbox/jng/streaming_v2/Web/src/HttpHandler/HttpResourceStrings.h	2013-08-11 14:41:25 UTC (rev 7756)
@@ -40,6 +40,8 @@
     static const STRING hrhnPublic;
     static const STRING hrhnContent_Length;
     static const STRING hrhnContent_Type;
+    static const STRING hrhnTransfer_Encoding;
+    static const STRING hrhnChunked;
     static const STRING hrhnContent_Transfer_Encoding;
     static const STRING hrhnContent_Encoding;
     static const STRING hrhnDate;

Modified: sandbox/jng/streaming_v2/Web/src/HttpHandler/HttpResult.h
===================================================================
--- sandbox/jng/streaming_v2/Web/src/HttpHandler/HttpResult.h	2013-08-09 16:16:41 UTC (rev 7755)
+++ sandbox/jng/streaming_v2/Web/src/HttpHandler/HttpResult.h	2013-08-11 14:41:25 UTC (rev 7756)
@@ -152,6 +152,8 @@
 
     MgHttpResult(MgDisposable* resultObject);
 
+    void SetChunked(bool bChunked);
+
     ///////////////////////////////////////////////////////////////////////////
     /// <summary>
     /// Sets the error information based on a Mg exception.
@@ -182,6 +184,7 @@
     STRING    m_DetailedMessage;
     STRING    m_HttpStatusMessage;
     STRING    m_contentType;
+    bool      m_chunkResult;
 
 CLASS_ID:
     static const INT32 m_cls_id = HttpHandler_MapAgent_HttpResult;

Modified: sandbox/jng/streaming_v2/Web/src/HttpHandler/HttpSelectFeatures.cpp
===================================================================
--- sandbox/jng/streaming_v2/Web/src/HttpHandler/HttpSelectFeatures.cpp	2013-08-09 16:16:41 UTC (rev 7755)
+++ sandbox/jng/streaming_v2/Web/src/HttpHandler/HttpSelectFeatures.cpp	2013-08-11 14:41:25 UTC (rev 7756)
@@ -17,6 +17,7 @@
 
 #include "HttpHandler.h"
 #include "HttpSelectFeatures.h"
+#include "ReaderByteSourceImpl.h"
 
 HTTP_IMPLEMENT_CREATE_OBJECT(MgHttpSelectFeatures)
 
@@ -104,9 +105,17 @@
     }
 
     Ptr<MgFeatureReader> featureReader = service->SelectFeatures(&resId, m_className, qryOptions);
-    //HACK-ish: We're passing conversion responsibility to the caller (agent), so we store the
-    //originally requested format so the caller can determine if conversion is required
-    hResult->SetResultObject(featureReader, m_responseFormat);
+    //MgByteSource owns this and will clean it up when done
+    ByteSourceImpl* bsImpl = new MgReaderByteSourceImpl(featureReader, m_responseFormat);
 
+    Ptr<MgByteSource> byteSource = new MgByteSource(bsImpl);
+    Ptr<MgByteReader> byteReader = byteSource->GetReader();
+    hResult->SetResultObject(byteReader, m_responseFormat);
+
+    Ptr<MgHttpHeader> respHeader = hResponse.GetHeader();
+
+    //This is the "hint" to chunk the MgByteReader content
+    respHeader->AddHeader(MgHttpResourceStrings::hrhnTransfer_Encoding, MgHttpResourceStrings::hrhnChunked);
+
     MG_HTTP_HANDLER_CATCH_AND_THROW_EX(L"MgHttpSelectFeatures.Execute")
 }

Modified: sandbox/jng/streaming_v2/Web/src/HttpHandler/HttpSelectFeaturesSpatially.cpp
===================================================================
--- sandbox/jng/streaming_v2/Web/src/HttpHandler/HttpSelectFeaturesSpatially.cpp	2013-08-09 16:16:41 UTC (rev 7755)
+++ sandbox/jng/streaming_v2/Web/src/HttpHandler/HttpSelectFeaturesSpatially.cpp	2013-08-11 14:41:25 UTC (rev 7756)
@@ -17,6 +17,7 @@
 
 #include "HttpHandler.h"
 #include "HttpSelectFeaturesSpatially.h"
+#include "ReaderByteSourceImpl.h"
 
 HTTP_IMPLEMENT_CREATE_OBJECT(MgHttpSelectFeaturesSpatially)
 
@@ -114,9 +115,17 @@
     }
 
     Ptr<MgDataReader> dataReader = service->SelectAggregate(&resId, m_className, qryOptions);
-    //HACK-ish: We're passing conversion responsibility to the caller (agent), so we store the
-    //originally requested format so the caller can determine if conversion is required
-    hResult->SetResultObject(dataReader, m_responseFormat);
+    //MgByteSource owns this and will clean it up when done
+    ByteSourceImpl* bsImpl = new MgReaderByteSourceImpl(dataReader, m_responseFormat);
 
+    Ptr<MgByteSource> byteSource = new MgByteSource(bsImpl);
+    Ptr<MgByteReader> byteReader = byteSource->GetReader();
+    hResult->SetResultObject(byteReader, m_responseFormat);
+
+    Ptr<MgHttpHeader> respHeader = hResponse.GetHeader();
+
+    //This is the "hint" to chunk the MgByteReader content
+    respHeader->AddHeader(MgHttpResourceStrings::hrhnTransfer_Encoding, MgHttpResourceStrings::hrhnChunked);
+
     MG_HTTP_HANDLER_CATCH_AND_THROW_EX(L"MgHttpSelectFeaturesSpatially.Execute")
 }

Added: sandbox/jng/streaming_v2/Web/src/HttpHandler/ReaderByteSourceImpl.cpp
===================================================================
--- sandbox/jng/streaming_v2/Web/src/HttpHandler/ReaderByteSourceImpl.cpp	                        (rev 0)
+++ sandbox/jng/streaming_v2/Web/src/HttpHandler/ReaderByteSourceImpl.cpp	2013-08-11 14:41:25 UTC (rev 7756)
@@ -0,0 +1,274 @@
+#include "ReaderByteSourceImpl.h"
+#include "PlatformBase.h"
+
+MgReaderByteSourceImpl::MgReaderByteSourceImpl(MgReader* reader, CREFSTRING format)
+{
+    m_reader = SAFE_ADDREF(reader);
+    m_format = format;
+    m_buf.reserve(8192);
+    m_bufOffset = -1;
+    m_bReadHeader = false;
+    m_bInternalReaderHasMore = true;
+    m_bFirstRecord = true;
+}
+
+MgReaderByteSourceImpl::~MgReaderByteSourceImpl()
+{
+    m_reader = NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////
+/// \brief
+/// Reads a buffer
+///
+/// \param buffer
+/// A buffer receiving the data.
+/// \param length
+/// Maximum number of bytes to read
+///
+/// \return
+/// Actual number of bytes put in the buffer. 0 means end of bytes
+///
+INT32 MgReaderByteSourceImpl::Read(BYTE_ARRAY_OUT buffer, INT32 length)
+{
+    INT32 ret = 0;
+
+    bool bAdvanceReader = false;
+    std::string buf;
+    std::string jsonbuf;
+
+    //Haven't read header, pre-fill this buffer
+    if (!m_bReadHeader)
+    {
+        if (m_format == MgMimeType::Json)
+        {
+            //How this looks:
+            //
+            // {                            //outer JSON start
+            //   "ResponseElementName":     //root element name
+            //     {                        //root JSON property start
+            //      <header JSON pair>,
+            //      "BodyElementName":[     //body JSON array start
+            //
+            jsonbuf = "{\"";
+            jsonbuf += m_reader->GetResponseElementName();
+            jsonbuf += "\":{";
+            m_reader->HeaderToStringUtf8(buf);
+            std::string jsonbuf2;
+            MgXmlJsonConvert convert;
+            convert.ToJson(buf, jsonbuf2);
+            //This will have redudant outer { }, so strip them
+            jsonbuf2.erase(0, 1);
+            jsonbuf2.erase(jsonbuf2.length() - 2, 1);
+            //HACK: To match the original output, we have to array-ify this object (crazy? yes!)
+            //
+            //We currently have something like this
+            //
+            // "HeaderElementName":{
+            //    <prop1>:<val1>,
+            //    <prop2>:<val2>
+            // }
+            //
+            //We have to change it to this
+            //
+            // "HeaderElementName":[{
+            //    <prop1>:<val1>,
+            //    <prop2>:<val2>
+            // }]
+
+            //Find first instance of ": and insert [ after it. We use ": because a feature
+            //reader puts out xs:schema as the header element name
+            jsonbuf2.insert(jsonbuf2.find("\":") + 2, "[");
+            //Append ] to the end
+            jsonbuf2.append("]");
+            jsonbuf += jsonbuf2;
+            jsonbuf += ",\"";
+            jsonbuf += m_reader->GetBodyElementName();
+            jsonbuf += "\":[";
+
+            m_buf += jsonbuf;
+            m_bReadHeader = true;
+        }
+        else if (m_format == MgMimeType::Xml)
+        {
+            m_reader->ResponseStartUtf8(m_buf);
+#ifdef _DEBUG
+            m_buf += "<!-- BEGIN HEADER -->\n";
+#endif
+            m_reader->HeaderToStringUtf8(m_buf);
+#ifdef _DEBUG
+            m_buf += "<!-- END HEADER -->\n";
+            m_buf += "<!-- BEGIN BODY -->\n";
+#endif
+            m_reader->BodyStartUtf8(m_buf);
+            m_bReadHeader = true;
+        }
+    }
+
+    INT32 maxIndex = m_buf.length() - 1;
+    //We have an internal buffer. Clear this out first
+    if (m_bufOffset < maxIndex)
+    {
+        INT32 read = ReadInternalBuffer(buffer, ret, length);
+        ret += read;
+    }
+
+    //If we've filled the whole internal buffer and the requested length is still
+    //bigger, we're good to advance to the next feature/record
+    if (ret < length)
+        bAdvanceReader = true;
+    else //Filled up to requested length
+        return ret;
+
+    if (m_bInternalReaderHasMore && bAdvanceReader)
+    {
+        m_bInternalReaderHasMore = m_reader->ReadNext();
+        if (m_bInternalReaderHasMore)
+        {
+            if (m_format == MgMimeType::Json)
+            {
+                m_reader->CurrentToStringUtf8(buf);
+                //The body is a valid full XML element, so no need for gymnastics like its
+                //surrounding elements
+                MgXmlJsonConvert convert;
+                convert.ToJson(buf, jsonbuf);
+
+                //Strip outer { }
+                jsonbuf.erase(0, 1);
+                jsonbuf.erase(jsonbuf.length() - 2, 1);
+                //HACK: Same as the header, this needs to be array-ified to match the old output
+                //
+                //Find first instance of ": and insert [ after it.
+                jsonbuf.insert(jsonbuf.find("\":") + 2, "[");
+                //Append ] to the end
+                jsonbuf.append("]");
+                //Put back in outer { }
+                jsonbuf = "{" + jsonbuf;
+                jsonbuf += "}";
+
+                if (!m_bFirstRecord)
+                {
+                    m_buf += ",";
+                }
+                else
+                {
+                    m_bFirstRecord = false;
+                }
+
+                m_buf += jsonbuf;
+            }
+            else if (m_format == MgMimeType::Xml)
+            {
+#ifdef _DEBUG
+                m_buf += "<!-- BEGIN RECORD -->\n";
+#endif
+                m_reader->CurrentToStringUtf8(m_buf);
+#ifdef _DEBUG
+                m_buf += "<!-- END RECORD -->\n";
+#endif
+            }
+        }
+        else //End of reader
+        {
+            if (m_format == MgMimeType::Json)
+            {
+#ifdef _DEBUG
+                //m_buf += "//END BODY\n";
+#endif
+                // How this looks:
+                //      ]   //End of body JSON array
+                //    }     //End of root JSON property
+                // }        //End of outer JSON
+                m_buf += "]}}";
+            }
+            else if (m_format == MgMimeType::Xml)
+            {
+                m_reader->BodyEndUtf8(m_buf);
+#ifdef _DEBUG
+                m_buf += "<!-- END BODY -->\n";
+#endif
+                m_reader->ResponseEndUtf8(m_buf);
+            }
+        }
+    }
+
+    //Clear out however many remaining content up to the requested
+    //length
+    maxIndex = m_buf.length() - 1;
+    if (m_bufOffset < maxIndex)
+    {
+        INT32 remaining = length - ret;
+        if (remaining > 0)
+        {
+            INT32 read = ReadInternalBuffer(buffer, ret, remaining);
+            ret += read;
+        }
+    }
+
+    return ret;
+}
+
+INT32 MgReaderByteSourceImpl::ReadInternalBuffer(BYTE_ARRAY_OUT buffer, INT32 fromIndex, INT32 length)
+{
+    INT32 ret = 0;
+    while (ret < length)
+    {
+        m_bufOffset++;
+        INT32 maxIndex = m_buf.length() - 1;
+        if (m_bufOffset <= maxIndex)
+        {
+            buffer[fromIndex + ret] = m_buf[m_bufOffset];
+            ret++;
+        }
+        else //End of internal buffer. Reset offset
+        {
+            m_buf.clear();
+            m_bufOffset = -1;
+            break;
+        }
+    }
+    return ret;
+}
+
+///////////////////////////////////////////////////////////////////////////
+/// \brief
+/// Returns the remaining length of the byte source.  This length is
+/// adjusted for previous reads.  If the returned length is zero
+/// then the underlying source may be a streaming format and the length
+/// is not known.
+///
+/// \return
+/// Remaining length of the byte source
+///
+INT64 MgReaderByteSourceImpl::GetLength()
+{
+    return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////
+/// \brief
+/// Determines if the source is rewindable.
+///
+/// \return
+/// true if the source is rewindable, false otherwise.
+///
+bool MgReaderByteSourceImpl::IsRewindable()
+{
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////
+/// \brief
+/// Rewinds to the start of the source.  Depending on the source of
+/// the reader, Rewind may not be supported.  Readers sourced from
+/// true streams cannot be rewound.
+///
+/// \return
+/// Nothing
+///
+/// \exception MgInvalidOperationException if source cannot be rewound
+///
+void MgReaderByteSourceImpl::Rewind()
+{
+    throw new MgInvalidOperationException(L"MgReaderByteSourceImpl.Rewind", __LINE__, __WFILE__, NULL, L"", NULL);
+}
\ No newline at end of file

Added: sandbox/jng/streaming_v2/Web/src/HttpHandler/ReaderByteSourceImpl.h
===================================================================
--- sandbox/jng/streaming_v2/Web/src/HttpHandler/ReaderByteSourceImpl.h	                        (rev 0)
+++ sandbox/jng/streaming_v2/Web/src/HttpHandler/ReaderByteSourceImpl.h	2013-08-11 14:41:25 UTC (rev 7756)
@@ -0,0 +1,80 @@
+#ifndef READER_BYTE_SOURCE_IMPL
+#define READER_BYTE_SOURCE_IMPL
+
+#include "Foundation.h"
+#include "XmlJsonConvert.h"
+
+class MgReader;
+
+// MgReaderByteSourceImpl provides an adapter layer to MgReader
+// objects, allowing them to be streamed/iterated with a MgByteReader
+class MG_MAPAGENT_API MgReaderByteSourceImpl : public ByteSourceImpl
+{
+    DECLARE_CLASSNAME(MgReaderByteSourceImpl)
+
+public:
+    MgReaderByteSourceImpl(MgReader* reader, CREFSTRING format);
+    virtual ~MgReaderByteSourceImpl();
+
+    ///////////////////////////////////////////////////////////////////////////
+    /// \brief
+    /// Reads a buffer
+    ///
+    /// \param buffer
+    /// A buffer receiving the data.
+    /// \param length
+    /// Maximum number of bytes to read
+    ///
+    /// \return
+    /// Actual number of bytes put in the buffer. 0 means end of bytes
+    ///
+    virtual INT32 Read(BYTE_ARRAY_OUT buffer, INT32 length);
+
+    ///////////////////////////////////////////////////////////////////////////
+    /// \brief
+    /// Returns the remaining length of the byte source.  This length is
+    /// adjusted for previous reads.  If the returned length is zero
+    /// then the underlying source may be a streaming format and the length
+    /// is not known.
+    ///
+    /// \return
+    /// Remaining length of the byte source
+    ///
+    virtual INT64 GetLength();
+
+    ///////////////////////////////////////////////////////////////////////////
+    /// \brief
+    /// Determines if the source is rewindable.
+    ///
+    /// \return
+    /// true if the source is rewindable, false otherwise.
+    ///
+    virtual bool IsRewindable();
+
+    ///////////////////////////////////////////////////////////////////////////
+    /// \brief
+    /// Rewinds to the start of the source.  Depending on the source of
+    /// the reader, Rewind may not be supported.  Readers sourced from
+    /// true streams cannot be rewound.
+    ///
+    /// \return
+    /// Nothing
+    ///
+    /// \exception MgInvalidOperationException if source cannot be rewound
+    ///
+    virtual void Rewind();
+
+private:
+    INT32 ReadInternalBuffer(BYTE_ARRAY_OUT buffer, INT32 fromIndex, INT32 length);
+
+    Ptr<MgReader> m_reader;
+    STRING m_format;
+    bool m_bReadHeader;
+    bool m_bInternalReaderHasMore;
+    bool m_bFirstRecord;
+
+    std::string m_buf;
+    INT32 m_bufOffset;
+};
+
+#endif
\ No newline at end of file

Modified: sandbox/jng/streaming_v2/Web/src/IsapiAgent/IsapiReaderStreamer.cpp
===================================================================
--- sandbox/jng/streaming_v2/Web/src/IsapiAgent/IsapiReaderStreamer.cpp	2013-08-09 16:16:41 UTC (rev 7755)
+++ sandbox/jng/streaming_v2/Web/src/IsapiAgent/IsapiReaderStreamer.cpp	2013-08-11 14:41:25 UTC (rev 7756)
@@ -3,8 +3,8 @@
 #include "IsapiReaderStreamer.h"
 #include "MapAgentStrings.h"
 
-IsapiReaderStreamer::IsapiReaderStreamer(EXTENSION_CONTROL_BLOCK* rec, const std::string& sResponseHeader, MgReader* reader, CREFSTRING format) :
-    MgHttpReaderStreamer(reader, format), 
+IsapiReaderStreamer::IsapiReaderStreamer(EXTENSION_CONTROL_BLOCK* rec, const std::string& sResponseHeader, MgByteReader* reader) :
+    MgHttpReaderStreamer(reader), 
     m_pECB(rec), 
     m_sResponseHeader(sResponseHeader), 
     m_bEndOfStream(false)

Modified: sandbox/jng/streaming_v2/Web/src/IsapiAgent/IsapiReaderStreamer.h
===================================================================
--- sandbox/jng/streaming_v2/Web/src/IsapiAgent/IsapiReaderStreamer.h	2013-08-09 16:16:41 UTC (rev 7755)
+++ sandbox/jng/streaming_v2/Web/src/IsapiAgent/IsapiReaderStreamer.h	2013-08-11 14:41:25 UTC (rev 7756)
@@ -23,7 +23,7 @@
 class IsapiReaderStreamer : public MgHttpReaderStreamer
 {
 public:
-    IsapiReaderStreamer(EXTENSION_CONTROL_BLOCK* rec, const std::string& sResponseHeader, MgReader* reader, CREFSTRING format);
+    IsapiReaderStreamer(EXTENSION_CONTROL_BLOCK* rec, const std::string& sResponseHeader, MgByteReader* reader);
     virtual ~IsapiReaderStreamer();
 
 protected:

Modified: sandbox/jng/streaming_v2/Web/src/IsapiAgent/IsapiResponseHandler.cpp
===================================================================
--- sandbox/jng/streaming_v2/Web/src/IsapiAgent/IsapiResponseHandler.cpp	2013-08-09 16:16:41 UTC (rev 7755)
+++ sandbox/jng/streaming_v2/Web/src/IsapiAgent/IsapiResponseHandler.cpp	2013-08-11 14:41:25 UTC (rev 7756)
@@ -103,7 +103,6 @@
             sResponseHeader.append(tempHeader);
         }
 
-        Ptr<MgReader> outputDataReader;
         Ptr<MgByteReader> outputReader;
         Ptr<MgDisposable> resultObj = result->GetResultObject();
         MgDisposable* pResultObj = (MgDisposable*)resultObj;
@@ -112,22 +111,10 @@
         {
             outputReader = (MgByteReader*) SAFE_ADDREF(pResultObj);
         }
-        else if (NULL != dynamic_cast<MgFeatureReader*>(pResultObj))
-        {
-            outputDataReader = SAFE_ADDREF((MgFeatureReader*)pResultObj); //Need to AddRef because there's now 2 references on this pointer
-        }
         else if (NULL != dynamic_cast<MgStringCollection*>(pResultObj))
         {
             outputReader = ((MgStringCollection*)pResultObj)->ToXml();
         }
-        else if (NULL != dynamic_cast<MgSqlDataReader*>(pResultObj))
-        {
-            outputDataReader = SAFE_ADDREF((MgSqlDataReader*)pResultObj); //Need to AddRef because there's now 2 references on this pointer
-        }
-        else if (NULL != dynamic_cast<MgDataReader*>(pResultObj))
-        {
-            outputDataReader = SAFE_ADDREF((MgDataReader*)pResultObj); //Need to AddRef because there's now 2 references on this pointer
-        }
         else if (NULL != dynamic_cast<MgSpatialContextReader*>(pResultObj))
         {
             outputReader = ((MgSpatialContextReader*)pResultObj)->ToXml();
@@ -153,28 +140,33 @@
             DWORD dwBufLen = (DWORD)utf8.length();
             m_pECB->WriteClient(m_pECB->ConnID, (LPVOID)utf8.c_str(), &dwBufLen, 0);
         }
-        else if (outputDataReader != NULL)
-        {
-            IsapiReaderStreamer irs(m_pECB, sResponseHeader, outputDataReader, result->GetResultContentType());
-            irs.StreamResult();
-        }
         else if (outputReader != NULL)
         {
-            INT64 outLen = outputReader->GetLength();
-            sprintf(tempHeader, MapAgentStrings::ContentLengthHeader, (INT32)outLen);
-            sResponseHeader.append(tempHeader);
-            sResponseHeader.append(MapAgentStrings::CrLf);
-            WriteHeader(sResponseHeader.c_str());
-
-            unsigned char buf[4096];
-            DWORD dwSize;
-            int nBytes = outputReader->Read(buf,4096);
-            while (nBytes > 0)
+            Ptr<MgHttpHeader> respHeader = response->GetHeader();
+            //Check for chunking hint
+            if (respHeader->GetHeaderValue(MgHttpResourceStrings::hrhnTransfer_Encoding) == MgHttpResourceStrings::hrhnChunked)
             {
-                dwSize = nBytes;
-                m_pECB->WriteClient(m_pECB->ConnID, buf, &dwSize, 0);
-                nBytes = outputReader->Read(buf,4096);
+                IsapiReaderStreamer irs(m_pECB, sResponseHeader, outputReader);
+                irs.StreamResult();
             }
+            else
+            {
+                INT64 outLen = outputReader->GetLength();
+                sprintf(tempHeader, MapAgentStrings::ContentLengthHeader, (INT32)outLen);
+                sResponseHeader.append(tempHeader);
+                sResponseHeader.append(MapAgentStrings::CrLf);
+                WriteHeader(sResponseHeader.c_str());
+
+                unsigned char buf[4096];
+                DWORD dwSize;
+                int nBytes = outputReader->Read(buf,4096);
+                while (nBytes > 0)
+                {
+                    dwSize = nBytes;
+                    m_pECB->WriteClient(m_pECB->ConnID, buf, &dwSize, 0);
+                    nBytes = outputReader->Read(buf,4096);
+                }
+            }
         }
         else
         {



More information about the mapguide-commits mailing list