[mapguide-commits] r7782 - in trunk/MgDev: Common/Foundation/System Server/src/Wfs Web/src/ApacheAgent Web/src/CgiAgent Web/src/HttpHandler Web/src/IsapiAgent

svn_mapguide at osgeo.org svn_mapguide at osgeo.org
Mon Aug 19 08:52:26 PDT 2013


Author: jng
Date: 2013-08-19 08:52:25 -0700 (Mon, 19 Aug 2013)
New Revision: 7782

Added:
   trunk/MgDev/Web/src/HttpHandler/ReaderByteSourceImpl.cpp
   trunk/MgDev/Web/src/HttpHandler/ReaderByteSourceImpl.h
Removed:
   trunk/MgDev/Web/src/HttpHandler/WfsFeatures.cpp
   trunk/MgDev/Web/src/HttpHandler/WfsFeatures.h
Modified:
   trunk/MgDev/Common/Foundation/System/ByteSourceImpl.h
   trunk/MgDev/Server/src/Wfs/1.0.0.xml.awd
   trunk/MgDev/Server/src/Wfs/1.1.0.xml.awd
   trunk/MgDev/Web/src/ApacheAgent/ApacheReaderStreamer.cpp
   trunk/MgDev/Web/src/ApacheAgent/ApacheReaderStreamer.h
   trunk/MgDev/Web/src/ApacheAgent/ApacheResponseHandler.cpp
   trunk/MgDev/Web/src/CgiAgent/CgiReaderStreamer.cpp
   trunk/MgDev/Web/src/CgiAgent/CgiReaderStreamer.h
   trunk/MgDev/Web/src/CgiAgent/CgiResponseHandler.cpp
   trunk/MgDev/Web/src/HttpHandler/HttpHandler.vcxproj
   trunk/MgDev/Web/src/HttpHandler/HttpHandler.vcxproj.filters
   trunk/MgDev/Web/src/HttpHandler/HttpHandlerBuild.cpp
   trunk/MgDev/Web/src/HttpHandler/HttpReaderStreamer.cpp
   trunk/MgDev/Web/src/HttpHandler/HttpReaderStreamer.h
   trunk/MgDev/Web/src/HttpHandler/HttpResourceStrings.cpp
   trunk/MgDev/Web/src/HttpHandler/HttpResourceStrings.h
   trunk/MgDev/Web/src/HttpHandler/HttpSelectFeatures.cpp
   trunk/MgDev/Web/src/HttpHandler/HttpSelectFeaturesSpatially.cpp
   trunk/MgDev/Web/src/HttpHandler/HttpWfsGetFeature.cpp
   trunk/MgDev/Web/src/HttpHandler/Makefile.am
   trunk/MgDev/Web/src/HttpHandler/OgcServer.h
   trunk/MgDev/Web/src/HttpHandler/OgcWfsServer.cpp
   trunk/MgDev/Web/src/HttpHandler/OgcWfsServer.h
   trunk/MgDev/Web/src/HttpHandler/ResponseStream.h
   trunk/MgDev/Web/src/IsapiAgent/IsapiReaderStreamer.cpp
   trunk/MgDev/Web/src/IsapiAgent/IsapiReaderStreamer.h
   trunk/MgDev/Web/src/IsapiAgent/IsapiResponseHandler.cpp
Log:
Merge in streaming work from streaming_v2 sandbox. This includes the following changes:

MgReader streaming refactoring

 - 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
 - Modify MgHttpReaderStreamer to work off of a MgByteReader instead of a MgReader
 - For MgByteReader responses that should be chunked out, the implement operation writes the Transfer-Encoding = Chunked setting to the MgHttpHeader of the response
 - The CGI/ISAPI/Apache handlers have been modified to
    - No longer handle MgReader responses as they are now wrapped by a MgByteReader
    - If handling a MgByteReader, check for the Transfer-Encoding = Chunked setting in the MgHttpHeader of the response. If the setting exists, forward the byte reader to the modified Apache/Isapi/CgiReaderStreamer that now knows how to chunk output a MgByteReader. Otherwise, they will output the byte reader as normal.

#1070: Stream WFS GetFeature responses from the mapagent. We do this by bypassing the OGC XML templating code.
 
MgFeatureService.GetWfsFeature() is already returning WFS GetFeature response content. I do not know why this response then had to go through the OGC XML templating code, where the full XML content had to be read into memory (the cause of the memory spikes on large GetFeature responses). The only difference between the GetFeature response (that goes through the XML templating code) and the GetFeature response (that bypasses the XML templating code) is that the GetFeature response that goes through the XML templating code contains comment markers denoting each individual feature in the response. Besides that, the responses are identical!

For large responses there will be a small pause because the server-side is still buffered first before being streamed down to the web tier. The problem here is that MgServerFeatureService.GetWfsFeature() is dumping the GML result into a temporary file before returning a MgByteReader from this temp file. Ideally for the server-side to stream this as well, we should have a custom FdoIoStream class that writes direct to the TCP/IP MgStream with some type gymnastics so that we can still return a MgByteReader to respect the original API.

This submission renders the MgWfsFeatures class and some methods of MgOgcWfsServer useless. This submission removes these items in question. Also GetFeature-related template fragments in the .awd files are no longer used and so they have been removed as well.

Modified: trunk/MgDev/Common/Foundation/System/ByteSourceImpl.h
===================================================================
--- trunk/MgDev/Common/Foundation/System/ByteSourceImpl.h	2013-08-18 09:38:27 UTC (rev 7781)
+++ trunk/MgDev/Common/Foundation/System/ByteSourceImpl.h	2013-08-19 15:52:25 UTC (rev 7782)
@@ -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: trunk/MgDev/Server/src/Wfs/1.0.0.xml.awd
===================================================================
--- trunk/MgDev/Server/src/Wfs/1.0.0.xml.awd	2013-08-18 09:38:27 UTC (rev 7781)
+++ trunk/MgDev/Server/src/Wfs/1.0.0.xml.awd	2013-08-19 15:52:25 UTC (rev 7782)
@@ -115,16 +115,6 @@
    <ogc:&Enum.item; />
  </Define>
 
- <Define item="GetFeature.xml">
-   <!-- Feature &Feature.iteration; -->
-   &Feature.OuterXml;
- </Define>
-
- <Define item="GetFeatureCollection.xml">
-   <wfs:FeatureCollection &FeatureCollection.Namespaces;>
-     <?EnumFeatures using="&GetFeature.xml;" ?> 
-   </wfs:FeatureCollection>
- </Define>
 </Definitions>
 
 <!--
@@ -255,15 +245,6 @@
 
 <!--
 
-  WFS GetFeature - XML, GML 2.1.2
-
--->
-<Response request="GetFeature" content-type="text/xml; subtype=gml/2.1.2">
- <?GetFeatureCollection using="&GetFeatureCollection.xml;" ?>
-</Response>
-
-<!--
-
   Exception Format  - XML
 
 -->

Modified: trunk/MgDev/Server/src/Wfs/1.1.0.xml.awd
===================================================================
--- trunk/MgDev/Server/src/Wfs/1.1.0.xml.awd	2013-08-18 09:38:27 UTC (rev 7781)
+++ trunk/MgDev/Server/src/Wfs/1.1.0.xml.awd	2013-08-19 15:52:25 UTC (rev 7782)
@@ -96,17 +96,6 @@
  <Define item="DescribeFeature.Import.xml">
   <xs:import schemaLocation="&Url.DescribeFeatureType;?request=DescribeFeatureType&service=WFS&version=1.1.0&typeName=&Enum.item;"/>
  </Define>
-
- <Define item="GetFeature.xml">
-  <!-- Feature &Feature.iteration; -->
-  &Feature.OuterXml;
- </Define>
-
- <Define item="GetFeatureCollection.xml">
-    <wfs:FeatureCollection &FeatureCollection.Namespaces;>
-        <?EnumFeatures using="&GetFeature.xml;" ?> 
-    </wfs:FeatureCollection>
- </Define>
  
 </Definitions>
 
@@ -325,27 +314,8 @@
  <?Endif?>
  </xs:schema>
 </Response>
-
 <!--
 
-  WFS GetFeature - XML, GML 2.1.2
-
--->
-<Response request="GetFeature" content-type="text/xml; subtype=gml/2.1.2">
- <?GetFeatureCollection using="&GetFeatureCollection.xml;" ?>
-</Response>
-
-<!--
-
-  WFS GetFeature - XML, GML 3.1.1
-
--->
-<Response request="GetFeature" content-type="text/xml; subtype=gml/3.1.1">
-  <?GetFeatureCollection using="&GetFeatureCollection.xml;" ?>
-</Response>
-
-<!--
-
   Exception Format  - XML
 
 -->

Modified: trunk/MgDev/Web/src/ApacheAgent/ApacheReaderStreamer.cpp
===================================================================
--- trunk/MgDev/Web/src/ApacheAgent/ApacheReaderStreamer.cpp	2013-08-18 09:38:27 UTC (rev 7781)
+++ trunk/MgDev/Web/src/ApacheAgent/ApacheReaderStreamer.cpp	2013-08-19 15:52:25 UTC (rev 7782)
@@ -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: trunk/MgDev/Web/src/ApacheAgent/ApacheReaderStreamer.h
===================================================================
--- trunk/MgDev/Web/src/ApacheAgent/ApacheReaderStreamer.h	2013-08-18 09:38:27 UTC (rev 7781)
+++ trunk/MgDev/Web/src/ApacheAgent/ApacheReaderStreamer.h	2013-08-19 15:52:25 UTC (rev 7782)
@@ -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: trunk/MgDev/Web/src/ApacheAgent/ApacheResponseHandler.cpp
===================================================================
--- trunk/MgDev/Web/src/ApacheAgent/ApacheResponseHandler.cpp	2013-08-18 09:38:27 UTC (rev 7781)
+++ trunk/MgDev/Web/src/ApacheAgent/ApacheResponseHandler.cpp	2013-08-19 15:52:25 UTC (rev 7782)
@@ -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: trunk/MgDev/Web/src/CgiAgent/CgiReaderStreamer.cpp
===================================================================
--- trunk/MgDev/Web/src/CgiAgent/CgiReaderStreamer.cpp	2013-08-18 09:38:27 UTC (rev 7781)
+++ trunk/MgDev/Web/src/CgiAgent/CgiReaderStreamer.cpp	2013-08-19 15:52:25 UTC (rev 7782)
@@ -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: trunk/MgDev/Web/src/CgiAgent/CgiReaderStreamer.h
===================================================================
--- trunk/MgDev/Web/src/CgiAgent/CgiReaderStreamer.h	2013-08-18 09:38:27 UTC (rev 7781)
+++ trunk/MgDev/Web/src/CgiAgent/CgiReaderStreamer.h	2013-08-19 15:52:25 UTC (rev 7782)
@@ -24,7 +24,7 @@
 class CgiReaderStreamer : public MgHttpReaderStreamer
 {
 public:
-    CgiReaderStreamer(MgReader* reader, CREFSTRING format);
+    CgiReaderStreamer(MgByteReader* reader);
     virtual ~CgiReaderStreamer();
 
 protected:

Modified: trunk/MgDev/Web/src/CgiAgent/CgiResponseHandler.cpp
===================================================================
--- trunk/MgDev/Web/src/CgiAgent/CgiResponseHandler.cpp	2013-08-18 09:38:27 UTC (rev 7781)
+++ trunk/MgDev/Web/src/CgiAgent/CgiResponseHandler.cpp	2013-08-19 15:52:25 UTC (rev 7782)
@@ -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: trunk/MgDev/Web/src/HttpHandler/HttpHandler.vcxproj
===================================================================
--- trunk/MgDev/Web/src/HttpHandler/HttpHandler.vcxproj	2013-08-18 09:38:27 UTC (rev 7781)
+++ trunk/MgDev/Web/src/HttpHandler/HttpHandler.vcxproj	2013-08-19 15:52:25 UTC (rev 7782)
@@ -718,13 +718,13 @@
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
     </ClCompile>
-    <ClCompile Include="WfsFeatureDefinitions.cpp">
+    <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|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
     </ClCompile>
-    <ClCompile Include="WfsFeatures.cpp">
+    <ClCompile Include="WfsFeatureDefinitions.cpp">
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
@@ -1038,11 +1038,11 @@
     <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" />
     <ClInclude Include="WfsFeatureDefinitions.h" />
-    <ClInclude Include="WfsFeatures.h" />
     <ClInclude Include="WfsGetFeatureParams.h" />
     <ClInclude Include="WmsFeatureInfo.h" />
     <ClInclude Include="WmsFeatureProperties.h" />

Modified: trunk/MgDev/Web/src/HttpHandler/HttpHandler.vcxproj.filters
===================================================================
--- trunk/MgDev/Web/src/HttpHandler/HttpHandler.vcxproj.filters	2013-08-18 09:38:27 UTC (rev 7781)
+++ trunk/MgDev/Web/src/HttpHandler/HttpHandler.vcxproj.filters	2013-08-19 15:52:25 UTC (rev 7782)
@@ -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">
@@ -291,9 +294,6 @@
     <ClCompile Include="WfsFeatureDefinitions.cpp">
       <Filter>Ogc</Filter>
     </ClCompile>
-    <ClCompile Include="WfsFeatures.cpp">
-      <Filter>Ogc</Filter>
-    </ClCompile>
     <ClCompile Include="WfsGetFeatureParams.cpp">
       <Filter>Ogc</Filter>
     </ClCompile>
@@ -383,8 +383,8 @@
     <ClCompile Include="HttpCreateRuntimeMap.cpp">
       <Filter>Mapping Service</Filter>
     </ClCompile>
-    <ClCompile Include="HttpDescribeRuntimeMap.cpp">
-      <Filter>Mapping Service</Filter>
+    <ClCompile Include="ReaderByteSourceImpl.cpp">
+      <Filter>Byte Source Adapters</Filter>
     </ClCompile>
   </ItemGroup>
   <ItemGroup>
@@ -658,9 +658,6 @@
     <ClInclude Include="WfsFeatureDefinitions.h">
       <Filter>Ogc</Filter>
     </ClInclude>
-    <ClInclude Include="WfsFeatures.h">
-      <Filter>Ogc</Filter>
-    </ClInclude>
     <ClInclude Include="WfsGetFeatureParams.h">
       <Filter>Ogc</Filter>
     </ClInclude>
@@ -757,8 +754,8 @@
     <ClInclude Include="HttpCreateRuntimeMap.h">
       <Filter>Mapping Service</Filter>
     </ClInclude>
-    <ClInclude Include="HttpDescribeRuntimeMap.h">
-      <Filter>Mapping Service</Filter>
+    <ClInclude Include="ReaderByteSourceImpl.h">
+      <Filter>Byte Source Adapters</Filter>
     </ClInclude>
   </ItemGroup>
   <ItemGroup>

Modified: trunk/MgDev/Web/src/HttpHandler/HttpHandlerBuild.cpp
===================================================================
--- trunk/MgDev/Web/src/HttpHandler/HttpHandlerBuild.cpp	2013-08-18 09:38:27 UTC (rev 7781)
+++ trunk/MgDev/Web/src/HttpHandler/HttpHandlerBuild.cpp	2013-08-19 15:52:25 UTC (rev 7782)
@@ -126,6 +126,8 @@
 #include "HttpEnumerateUnmanagedData.cpp"
 #include "WmsMapUtil.cpp"
 
+#include "ReaderByteSourceImpl.cpp"
+
 // JSON conversion files
 #include "XmlJsonConvert.cpp"
 #include "JsonDoc.cpp"
@@ -142,7 +144,6 @@
 #include "WmsFeatureInfo.cpp"
 #include "WmsFeatureProperties.cpp"
 #include "WfsFeatureDefinitions.cpp"
-#include "WfsFeatures.cpp"
 #include "XmlParser.cpp"
 #include "Dictionary.cpp"
 #include "NameStringValueCollection.cpp"

Modified: trunk/MgDev/Web/src/HttpHandler/HttpReaderStreamer.cpp
===================================================================
--- trunk/MgDev/Web/src/HttpHandler/HttpReaderStreamer.cpp	2013-08-18 09:38:27 UTC (rev 7781)
+++ trunk/MgDev/Web/src/HttpHandler/HttpReaderStreamer.cpp	2013-08-19 15:52:25 UTC (rev 7782)
@@ -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: trunk/MgDev/Web/src/HttpHandler/HttpReaderStreamer.h
===================================================================
--- trunk/MgDev/Web/src/HttpHandler/HttpReaderStreamer.h	2013-08-18 09:38:27 UTC (rev 7781)
+++ trunk/MgDev/Web/src/HttpHandler/HttpReaderStreamer.h	2013-08-19 15:52:25 UTC (rev 7782)
@@ -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: trunk/MgDev/Web/src/HttpHandler/HttpResourceStrings.cpp
===================================================================
--- trunk/MgDev/Web/src/HttpHandler/HttpResourceStrings.cpp	2013-08-18 09:38:27 UTC (rev 7781)
+++ trunk/MgDev/Web/src/HttpHandler/HttpResourceStrings.cpp	2013-08-19 15:52:25 UTC (rev 7782)
@@ -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: trunk/MgDev/Web/src/HttpHandler/HttpResourceStrings.h
===================================================================
--- trunk/MgDev/Web/src/HttpHandler/HttpResourceStrings.h	2013-08-18 09:38:27 UTC (rev 7781)
+++ trunk/MgDev/Web/src/HttpHandler/HttpResourceStrings.h	2013-08-19 15:52:25 UTC (rev 7782)
@@ -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: trunk/MgDev/Web/src/HttpHandler/HttpSelectFeatures.cpp
===================================================================
--- trunk/MgDev/Web/src/HttpHandler/HttpSelectFeatures.cpp	2013-08-18 09:38:27 UTC (rev 7781)
+++ trunk/MgDev/Web/src/HttpHandler/HttpSelectFeatures.cpp	2013-08-19 15:52:25 UTC (rev 7782)
@@ -17,6 +17,7 @@
 
 #include "HttpHandler.h"
 #include "HttpSelectFeatures.h"
+#include "ReaderByteSourceImpl.h"
 
 HTTP_IMPLEMENT_CREATE_OBJECT(MgHttpSelectFeatures)
 
@@ -104,9 +105,18 @@
     }
 
     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);
+    byteSource->SetMimeType(m_responseFormat);
+    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: trunk/MgDev/Web/src/HttpHandler/HttpSelectFeaturesSpatially.cpp
===================================================================
--- trunk/MgDev/Web/src/HttpHandler/HttpSelectFeaturesSpatially.cpp	2013-08-18 09:38:27 UTC (rev 7781)
+++ trunk/MgDev/Web/src/HttpHandler/HttpSelectFeaturesSpatially.cpp	2013-08-19 15:52:25 UTC (rev 7782)
@@ -17,6 +17,7 @@
 
 #include "HttpHandler.h"
 #include "HttpSelectFeaturesSpatially.h"
+#include "ReaderByteSourceImpl.h"
 
 HTTP_IMPLEMENT_CREATE_OBJECT(MgHttpSelectFeaturesSpatially)
 
@@ -114,9 +115,18 @@
     }
 
     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);
+    byteSource->SetMimeType(m_responseFormat);
+    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")
 }

Modified: trunk/MgDev/Web/src/HttpHandler/HttpWfsGetFeature.cpp
===================================================================
--- trunk/MgDev/Web/src/HttpHandler/HttpWfsGetFeature.cpp	2013-08-18 09:38:27 UTC (rev 7781)
+++ trunk/MgDev/Web/src/HttpHandler/HttpWfsGetFeature.cpp	2013-08-19 15:52:25 UTC (rev 7782)
@@ -34,7 +34,7 @@
             ogc_server.ServiceExceptionReportResponse(                                        \
                 MgOgcWfsException(MgOgcWfsException::ogc_exception_code,                      \
                                   sReport.c_str() ));                                         \
-            Ptr<MgByteReader> capabilities = responseStream.Stream().GetReader();             \
+            Ptr<MgByteReader> capabilities = responseStream.GetReader();                      \
             hResult->SetResultObject(capabilities, capabilities->GetMimeType());              \
             e->Release();                                                                     \
         }                                                                                     \
@@ -49,7 +49,7 @@
             ogc_server.ServiceExceptionReportResponse(                                        \
                 MgOgcWfsException(MgOgcWfsException::kpszInternalError,                       \
                                   _("Unexpected exception was thrown.  No additional details available.")));\
-            Ptr<MgByteReader> capabilities = responseStream.Stream().GetReader();             \
+            Ptr<MgByteReader> capabilities = responseStream.GetReader();                      \
             hResult->SetResultObject(capabilities, capabilities->GetMimeType());              \
         }                                                                                     \
 ///////////////////////////////////////////////////////////////////////////////////////////////
@@ -119,7 +119,7 @@
     }
     MgHttpRequestParameters requestParams(origReqParams);
 
-    MgHttpResponseStream responseStream;
+    MgGetWfsFeaturesResponseStream responseStream;
     MgOgcServer::SetLoader(GetDocument);
 
     MgUserInformation::SetCurrentUserInfo(m_userInfo);
@@ -132,8 +132,12 @@
         wfsServer.ProcessRequest(this);
 
         // Obtain the response byte reader
-        Ptr<MgByteReader> responseReader = responseStream.Stream().GetReader();
+        Ptr<MgByteReader> responseReader = responseStream.GetReader();
 
+        Ptr<MgHttpHeader> respHeader = hResponse.GetHeader();
+        //This is the "hint" to chunk the MgByteReader content
+        respHeader->AddHeader(MgHttpResourceStrings::hrhnTransfer_Encoding, MgHttpResourceStrings::hrhnChunked);
+
         // Set the result
         hResult->SetResultObject(responseReader, responseReader->GetMimeType());
     }
@@ -165,6 +169,7 @@
 // Acquire data required to generate the response
 void MgHttpWfsGetFeature::AcquireResponseData(MgOgcServer* ogcServer)
 {
+    Ptr<MgByteReader> resultReader;
     MgOgcWfsServer* wfsServer = (MgOgcWfsServer*)ogcServer;
     if(wfsServer != NULL)
     {
@@ -274,33 +279,26 @@
 
                             // Call the C++ API
                             // NOTE: I updated the maxFeatures value from numFeaturesToRetrieve to numFeaturesToRetrieve-1
-                            // Because the MgServerFdoFeatureReader in MapGudie server uses -1 to mark empty, while MgWfsFeatures
+                            // Because the MgServerFdoFeatureReader in MapGuide server uses -1 to mark empty, while MgWfsFeatures
                             // in MapGuide web tier uses 0
-                            Ptr<MgByteReader> resultReader = featureService->GetWfsFeature(featureSourceId, ((sSchemaHash.size()==0) ? sClass : sSchemaHash + _(":") + sClass),
+                            resultReader = featureService->GetWfsFeature(featureSourceId, ((sSchemaHash.size()==0) ? sClass : sSchemaHash + _(":") + sClass),
                                 requiredProperties, m_getFeatureParams->GetSrs(), filter, numFeaturesToRetrieve-1, sVersion, sOutputFormat, sSortCriteria, sPrefix, oFeatureTypes.GetNamespaceUrl());
 
-                            // TODO How to determine number of features retrieved...?
-                            // Note: for now, maxFeatures is managed by the MgWfsFeatures object. - TMT 2006-3-20
-                            // numFeaturesRetrieved += ?
-
-                            // Write the byte reader's data into our response data object
-                            string thisResponseString;
-                            resultReader->ToStringUtf8(thisResponseString);
-
-                            // just append the entire thing; there's important stuff, like namespace declarations
-                            // that we would lose if we just extracted the <featureMember> elements.
-                            // The MgWfsFeatures object will parse the pseudo-XML that results.
-                            responseString += thisResponseString;
+                            // Store the MgByteReader directly for retrieval
+                            //
+                            // DO NOT PASS THROUGH OGC XML TEMPLATE PROCESSING CODE!
+                            // DO NOT PASS GO!
+                            // DO NOT COLLECT MEMORY SPIKES NEEDLESSLY BUFFERING XML TEMPLATE CONTENT AS A RESULT!
+                            //
+                            // This *is* already the WFS GetFeature response. There is nothing to post-process through the XML templates!
                         }
                     }
                 }
             }
         }
-        if(responseString.length() > 0)
+        if (NULL != resultReader.p)
         {
-            STRING wResponseString = MgUtil::MultiByteToWideChar(responseString);
-            Ptr<MgWfsFeatures> features = new MgWfsFeatures(wResponseString.c_str(),m_getFeatureParams->GetMaxFeatures());
-            wfsServer->SetFeatures(features);
+            wfsServer->SetFeatures(resultReader);
         }
     }
 }

Modified: trunk/MgDev/Web/src/HttpHandler/Makefile.am
===================================================================
--- trunk/MgDev/Web/src/HttpHandler/Makefile.am	2013-08-18 09:38:27 UTC (rev 7781)
+++ trunk/MgDev/Web/src/HttpHandler/Makefile.am	2013-08-19 15:52:25 UTC (rev 7782)
@@ -132,8 +132,8 @@
   OgcWfsServer.cpp \
   OgcWmsException.cpp \
   OgcWmsServer.cpp \
+  ReaderByteSourceImpl.cpp \
   WfsFeatureDefinitions.cpp \
-  WfsFeatures.cpp \
   WfsGetFeatureParams.cpp \
   WmsFeatureInfo.cpp \
   WmsFeatureProperties.cpp \
@@ -266,6 +266,7 @@
   OgcWfsServer.h \
   OgcWmsException.h \
   OgcWmsServer.h \
+  ReaderByteSourceImpl.h \
   ResponseStream.h \
   Stream.h \
   StringStream.h \

Modified: trunk/MgDev/Web/src/HttpHandler/OgcServer.h
===================================================================
--- trunk/MgDev/Web/src/HttpHandler/OgcServer.h	2013-08-18 09:38:27 UTC (rev 7781)
+++ trunk/MgDev/Web/src/HttpHandler/OgcServer.h	2013-08-19 15:52:25 UTC (rev 7782)
@@ -512,9 +512,7 @@
     // These are the links to the outside world.  The input (request) and
     // the output (response) objects.
     MgHttpRequestParameters& m_Request;
-    CStream*                m_pResponse;
-
-
+    
     // This determines whether writing is enabled.
     bool m_bWriteEnabled;
     EscapeState m_eEscapeState;
@@ -528,8 +526,11 @@
     int  m_iExpansionRecursionDepth;
 
 
+protected:
+    CStream*                m_pResponse;
+
     // Statics...
-protected:
+
     // Given a parser and a dictionary, shove definitions found in the former
     // into the latter.
     static void ProcessDefinitions(MgXmlParser& Template,MgUtilDictionary& Dictionary);

Modified: trunk/MgDev/Web/src/HttpHandler/OgcWfsServer.cpp
===================================================================
--- trunk/MgDev/Web/src/HttpHandler/OgcWfsServer.cpp	2013-08-18 09:38:27 UTC (rev 7781)
+++ trunk/MgDev/Web/src/HttpHandler/OgcWfsServer.cpp	2013-08-19 15:52:25 UTC (rev 7782)
@@ -367,22 +367,10 @@
 {
     try
     {
-        // See what format they requested the GetFeature response in...
-        CPSZ pszFormat = RequestParameter(kpszQueryStringOutputFormat);
-        STRING sOutputFormat;
-
-        if(pszFormat == NULL)
-        {
-            CPSZ pszVersion = RequestParameter(MgHttpResourceStrings::reqWfsVersion.c_str());
-            sOutputFormat = this->GetDefaultGetFeatureOutputFormat(pszVersion);
-            pszFormat = sOutputFormat.c_str();
-        }
-        // Generate a response to the GetFeature request
-        if(GenerateResponse(kpszQueryValueGetFeature,pszFormat))
-        {
-            // The response was generated successfully, so we return
-            return;
-        }
+        //MgHttpWfsGetFeature will pass a MgGetWfsFeaturesResponseStream to this instance when executing
+        MgGetWfsFeaturesResponseStream* wfsResponse = static_cast<MgGetWfsFeaturesResponseStream*>(m_pResponse);
+        wfsResponse->SetReader(m_pFeatureSet);
+        return;
     }
     catch(MgException* pEx)
     {
@@ -453,14 +441,6 @@
     {
         ProcedureEnumFeatureTypes(PI);
     }
-    else if(sProc == kpszPiEnumFeatures)
-    {
-        ProcedureEnumFeatures(PI);
-    }
-    else if(sProc == kpszPiGetFeatureCollection)
-    {
-        ProcedureGetFeatureCollection(PI);
-    }
     else
     {
         return false; // Unknown
@@ -511,41 +491,6 @@
     }
 }
 
-
-void MgOgcWfsServer::ProcedureEnumFeatures(MgXmlProcessingInstruction& PIEnum)
-{
-    STRING sFormat;
-    if(!PIEnum.GetAttribute(kpszPiAttributeUsing,sFormat))
-        sFormat = kpszPiGetFeatureCollectionDefaultFormat;
-
-    STRING sSubset;
-    if(!PIEnum.GetAttribute(kpszPiAttributeSubset,sSubset))
-        sSubset = kpszEmpty;
-    ProcessExpandableTextIntoString(sSubset,sSubset);
-
-    int iNum = 0;
-
-    if(m_pFeatureSet != NULL) {
-        while(m_pFeatureSet->Next())
-        {
-            // We ensure that each feature gets its own stack frame
-            // so definitions don't get carried over to the next feature.
-            CDictionaryStackFrame ForEachFeature(this);
-
-            m_pFeatureSet->GenerateDefinitions(*m_pTopOfDefinitions);
-
-            if(IsIterationInSubset(++iNum,sSubset,kpszPiDefinitionFeatureIteration) && (*m_pTopOfDefinitions)[L"Feature.OuterXml"] )
-            {
-                ProcessExpandableText(sFormat);
-            }
-        }
-    }
-}
-
-
-
-
-
 void MgOgcWfsServer::GenerateTypeNameException(CREFSTRING sTypeName)
 {
     sTypeName; // RESERVED FOR FUTURE USE; unused for now... to provide this info in exception body.
@@ -554,30 +499,8 @@
                                                      MgHttpResourceStrings::reqWfsTypeName.c_str()));
 }
 
-void MgOgcWfsServer::ProcedureGetFeatureCollection(MgXmlProcessingInstruction& PI)
+void MgOgcWfsServer::SetFeatures(MgByteReader* pFeatures)
 {
-    STRING sFormat;
-    if(!PI.GetAttribute(kpszPiAttributeUsing,sFormat))
-        sFormat = kpszPiGetFeatureCollectionDefaultFormat;
-
-    STRING sSubset;
-    if(!PI.GetAttribute(kpszPiAttributeSubset,sSubset))
-        sSubset = kpszEmpty;
-    ProcessExpandableTextIntoString(sSubset,sSubset);
-
-    bool bHasNamespace = false;
-
-    if(m_pFeatureSet != NULL) {
-        while(!bHasNamespace && m_pFeatureSet->Next()) {
-            bHasNamespace = m_pFeatureSet->GenerateNamespacesDefinition(*m_pTopOfDefinitions);
-        }
-    }
-
-    ProcessExpandableText(sFormat);
-}
-
-void MgOgcWfsServer::SetFeatures(MgWfsFeatures* pFeatures)
-{
     m_pFeatureSet = SAFE_ADDREF(pFeatures);
 }
 

Modified: trunk/MgDev/Web/src/HttpHandler/OgcWfsServer.h
===================================================================
--- trunk/MgDev/Web/src/HttpHandler/OgcWfsServer.h	2013-08-18 09:38:27 UTC (rev 7781)
+++ trunk/MgDev/Web/src/HttpHandler/OgcWfsServer.h	2013-08-19 15:52:25 UTC (rev 7782)
@@ -21,7 +21,6 @@
 #include "OgcServer.h"
 #include "OgcWfsException.h"
 #include "WfsFeatureDefinitions.h"
-#include "WfsFeatures.h"
 
 class MgOgcWfsServer: public MgOgcServer
 {
@@ -30,7 +29,8 @@
     MgOgcWfsServer(MgHttpRequestParameters& Request, CStream& Response);
 
     void GenerateTypeNameException(CREFSTRING sTypeName);
-    void SetFeatures(MgWfsFeatures* pFeatures);
+    
+    void SetFeatures(MgByteReader* pFeatures);
     void SetGetFeatureRequestParams(WfsGetFeatureParams* pGetFeatureParams);
     void SetFeatureDefinitions(MgWfsFeatureDefinitions* pFeatureDefs);
 
@@ -93,16 +93,10 @@
     // ?>
     void ProcedureEnumFeatureTypes(MgXmlProcessingInstruction& PIEnum);
 
-    void ProcedureGetFeatureCollection(MgXmlProcessingInstruction& PIEnum);
-
-    // <?EnumFeatures
-    //
-    void ProcedureEnumFeatures(MgXmlProcessingInstruction& PIEnum);
-
     CPSZ ServiceExceptionReportElement();
 
     MgWfsFeatureDefinitions* m_pFeatures;
-    Ptr<MgWfsFeatures> m_pFeatureSet;
+    Ptr<MgByteReader> m_pFeatureSet;
     Ptr<WfsGetFeatureParams> m_pGetFeatureParams;
 
     // The backing store for the default exception.

Copied: trunk/MgDev/Web/src/HttpHandler/ReaderByteSourceImpl.cpp (from rev 7776, sandbox/jng/streaming_v2/Web/src/HttpHandler/ReaderByteSourceImpl.cpp)
===================================================================
--- trunk/MgDev/Web/src/HttpHandler/ReaderByteSourceImpl.cpp	                        (rev 0)
+++ trunk/MgDev/Web/src/HttpHandler/ReaderByteSourceImpl.cpp	2013-08-19 15:52:25 UTC (rev 7782)
@@ -0,0 +1,291 @@
+//
+//  Copyright (C) 2004-2013 by Autodesk, Inc.
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of version 2.1 of the GNU Lesser
+//  General Public License as published by the Free Software Foundation.
+//
+//  This library is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+//  Lesser General Public License for more details.
+//
+//  You should have received a copy of the GNU Lesser General Public
+//  License along with this library; if not, write to the Free Software
+//  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+#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_buf.clear();
+    MG_TRY()
+    m_reader->Close();
+    MG_CATCH_AND_RELEASE()
+    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)
+            {
+                // 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

Copied: trunk/MgDev/Web/src/HttpHandler/ReaderByteSourceImpl.h (from rev 7776, sandbox/jng/streaming_v2/Web/src/HttpHandler/ReaderByteSourceImpl.h)
===================================================================
--- trunk/MgDev/Web/src/HttpHandler/ReaderByteSourceImpl.h	                        (rev 0)
+++ trunk/MgDev/Web/src/HttpHandler/ReaderByteSourceImpl.h	2013-08-19 15:52:25 UTC (rev 7782)
@@ -0,0 +1,96 @@
+//
+//  Copyright (C) 2004-2013 by Autodesk, Inc.
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of version 2.1 of the GNU Lesser
+//  General Public License as published by the Free Software Foundation.
+//
+//  This library is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+//  Lesser General Public License for more details.
+//
+//  You should have received a copy of the GNU Lesser General Public
+//  License along with this library; if not, write to the Free Software
+//  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+#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: trunk/MgDev/Web/src/HttpHandler/ResponseStream.h
===================================================================
--- trunk/MgDev/Web/src/HttpHandler/ResponseStream.h	2013-08-18 09:38:27 UTC (rev 7781)
+++ trunk/MgDev/Web/src/HttpHandler/ResponseStream.h	2013-08-19 15:52:25 UTC (rev 7782)
@@ -52,6 +52,31 @@
 };
 */
 
+// This is not a true stream. It's just a temporary MgByteReader
+// placeholder that passes the CStream type test allowing us to 
+// pass this into a MgOgcWfsServer instance.
+class MgGetWfsFeaturesResponseStream : public CStream
+{
+public:
+    MgGetWfsFeaturesResponseStream() 
+    {
+        m_reader = NULL;
+    }
+    ~MgGetWfsFeaturesResponseStream() 
+    { 
+        m_reader = NULL;
+    }
+
+    virtual void SetContentType(CPSZ pszContentTypeMime) { }
+    virtual long Write(CPSZ pszBuffer,size_t uBytesToWrite,size_t* puBytesWritten) { return -1; }
+
+    void SetReader(MgByteReader* reader) { m_reader = SAFE_ADDREF(reader); }
+    MgByteReader* GetReader() { return SAFE_ADDREF(m_reader); }
+
+private:
+    MgByteReader* m_reader;
+};
+
 class MgHttpResponseStream: public CStream
 {
 public:

Deleted: trunk/MgDev/Web/src/HttpHandler/WfsFeatures.cpp
===================================================================
--- trunk/MgDev/Web/src/HttpHandler/WfsFeatures.cpp	2013-08-18 09:38:27 UTC (rev 7781)
+++ trunk/MgDev/Web/src/HttpHandler/WfsFeatures.cpp	2013-08-19 15:52:25 UTC (rev 7782)
@@ -1,142 +0,0 @@
-//
-//  Copyright (C) 2004-2011 by Autodesk, Inc.
-//
-//  This library is free software; you can redistribute it and/or
-//  modify it under the terms of version 2.1 of the GNU Lesser
-//  General Public License as published by the Free Software Foundation.
-//
-//  This library is distributed in the hope that it will be useful,
-//  but WITHOUT ANY WARRANTY; without even the implied warranty of
-//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-//  Lesser General Public License for more details.
-//
-//  You should have received a copy of the GNU Lesser General Public
-//  License along with this library; if not, write to the Free Software
-//  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-//
-
-#include "OgcFramework.h"
-#include "WfsFeatures.h"
-
-
-CPSZ gszElementNameFeatureMember = _("http://www.opengis.net/gml:featureMember");
-CPSZ gszElementNameFeatureCollection = _("http://www.opengis.net/wfs:FeatureCollection");
-
-MgWfsFeatures::MgWfsFeatures(CPSZ szInputXml,int iMaxFeatures)
-:   m_sFeatureCollection(szInputXml)
-,   m_XmlInput(m_sFeatureCollection.c_str())
-,   m_iMaxFeatures(iMaxFeatures)
-,   m_bOk(true)
-,   m_bDone(false)
-{
-    if(m_iMaxFeatures < 1)
-        m_iMaxFeatures = 1000000; // a million features max should be enough.
-    m_XmlInput.Next();
-}
-
-MgWfsFeatures::~MgWfsFeatures()
-{
-}
-
-bool MgWfsFeatures::Next()
-{
-    // Blitz anything that's there.
-    m_sCurrentFeature = _("");
-
-    if(m_bOk && m_iMaxFeatures--) {
-        while(true) {
-            // End of stream?
-            if(m_XmlInput.AtEnd()) {
-                m_bOk = false;
-                break; // while
-            }
-
-           // Okay; a good sign... let's start poking around.
-            switch(m_XmlInput.Current().Type()) {
-            case keBeginElement:
-                {
-                    MgXmlBeginElement& Begin = (MgXmlBeginElement&)m_XmlInput.Current();
-                    m_Namespaces.TrackBeginElement(Begin);
-
-                    // If it's a <featureMember> element...
-                    if(m_Namespaces.QualifiedName(Begin) == gszElementNameFeatureMember) {
-                        m_sCurrentFeature = Begin.Contents();
-                        m_iCurrentInnerContent = m_sCurrentFeature.length();
-
-                        MgXmlSynchronizeOnNamespaceElement SlurpThis(m_XmlInput,
-                                                                     gszElementNameFeatureMember,
-                                                                     m_Namespaces);
-                        // Declare our intent to go into the element.
-                        SlurpThis.AtBegin();
-                        // Now slurp out the contents.
-                        while(!SlurpThis.AtEnd()) {
-                            m_sCurrentFeature += m_XmlInput.Current().Contents();
-                            m_XmlInput.Next();
-                        }
-                        // Make a note of how much has been added.
-                        m_iCurrentInnerLength = m_sCurrentFeature.length() - m_iCurrentInnerContent;
-                        // We're currently parked over the </featureMember> element; tack that on.
-                        m_sCurrentFeature += m_XmlInput.Current().Contents();
-                        // Track the end element's going.
-                        m_Namespaces.TrackEndElement((MgXmlEndElement&)m_XmlInput.Current());
-                        // And tell the caller we've got a hit.
-                        return m_bOk;
-                    }
-                    else if(m_Namespaces.QualifiedName(Begin) == gszElementNameFeatureCollection){
-                        MgXmlAttribute& attributes = Begin.Attributes();
-                        m_sFeatureCollectionNamespaces = attributes.Contents();
-
-                        m_iMaxFeatures++; // Read schema namespaces should not count the feature
-
-                        m_XmlInput.Next();
-                        return m_bOk;
-                    }
-                    else {
-                        m_XmlInput.Next();
-                    }
-                }
-                break;
-
-            case keEndElement:
-                {
-                    // The </featureMember> element is swallowed above,
-                    // This processes only non-featureMember elements, as is
-                    // the case when querying for two or more feature classes.
-                    MgXmlEndElement& End = (MgXmlEndElement&)m_XmlInput.Current();
-                    m_Namespaces.TrackEndElement(End);
-                    m_XmlInput.Next();
-                }
-                break;
-
-            default:
-                // Unexpected and uninteresting (<?xml?>, white space, comments, etc.); skip it.
-                m_XmlInput.Next();
-            }
-        }
-    }
-    else
-        m_bOk = false;
-
-    return m_bOk;
-}
-
-void MgWfsFeatures::GenerateDefinitions(MgUtilDictionary& Dictionary)
-{
-    if(m_bOk && !m_sCurrentFeature.empty()) {
-        Dictionary.AddDefinition(L"Feature.OuterXml",    m_sCurrentFeature);
-        Dictionary.AddDefinition(L"Feature.InnerXml",    m_sCurrentFeature.substr(m_iCurrentInnerContent,m_iCurrentInnerLength));
-        Dictionary.AddDefinition(L"Feature.EndElement",  m_sCurrentFeature.substr(m_iCurrentInnerContent+m_iCurrentInnerLength));
-        Dictionary.AddDefinition(L"Feature.BeginElement",m_sCurrentFeature.substr(0,m_iCurrentInnerContent));
-    }
-}
-
-bool MgWfsFeatures::GenerateNamespacesDefinition(MgUtilDictionary& Dictionary)
-{
-    if(m_bOk && !m_sFeatureCollectionNamespaces.empty()) {
-        Dictionary.AddDefinition(L"FeatureCollection.Namespaces", m_sFeatureCollectionNamespaces);
-        return true;
-    }
-
-    return false;
-}
-

Deleted: trunk/MgDev/Web/src/HttpHandler/WfsFeatures.h
===================================================================
--- trunk/MgDev/Web/src/HttpHandler/WfsFeatures.h	2013-08-18 09:38:27 UTC (rev 7781)
+++ trunk/MgDev/Web/src/HttpHandler/WfsFeatures.h	2013-08-19 15:52:25 UTC (rev 7782)
@@ -1,54 +0,0 @@
-//
-//  Copyright (C) 2004-2011 by Autodesk, Inc.
-//
-//  This library is free software; you can redistribute it and/or
-//  modify it under the terms of version 2.1 of the GNU Lesser
-//  General Public License as published by the Free Software Foundation.
-//
-//  This library is distributed in the hope that it will be useful,
-//  but WITHOUT ANY WARRANTY; without even the implied warranty of
-//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-//  Lesser General Public License for more details.
-//
-//  You should have received a copy of the GNU Lesser General Public
-//  License along with this library; if not, write to the Free Software
-//  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-//
-
-#ifndef _MgWfsFeatures_h
-#define _MgWfsFeatures_h
-
-#include "XmlParser.h"
-#include "Dictionary.h"
-
-class MgWfsFeatures: public IOgcResourceEnumerator, public MgDisposable
-{
-public:
-    MgWfsFeatures(CPSZ inputXml,int iMaxFeatures);
-    //Default constructor to keep Ptr<> happy
-    MgWfsFeatures();
-    virtual ~MgWfsFeatures();
-
-    bool Next();
-    void GenerateDefinitions(MgUtilDictionary& Dictionary);
-    bool GenerateNamespacesDefinition(MgUtilDictionary& Dictionary);
-    virtual void Dispose()
-    {
-        delete this;
-    }
-
-private:
-    STRING m_sFeatureCollection;  // the entire blob of features
-    MgXmlNamespaceManager m_Namespaces;
-    MgXmlParser m_XmlInput;
-    STRING m_sCurrentFeature;                 // just the current feature.
-    STRING m_sFeatureCollectionNamespaces;    // the namespaces in <wfs:FeatureCollection>
-    STRING::size_type m_iCurrentInnerContent; // index to start of content after <gml:featureMember>
-    STRING::size_type m_iCurrentInnerLength;  // length of content between <gml:featureMember> and </gml:featureMember>
-    bool m_bOk;
-    bool m_bDone;
-    int m_iMaxFeatures;
-};
-
-#endif//_MgWfsFeatures_h
-

Modified: trunk/MgDev/Web/src/IsapiAgent/IsapiReaderStreamer.cpp
===================================================================
--- trunk/MgDev/Web/src/IsapiAgent/IsapiReaderStreamer.cpp	2013-08-18 09:38:27 UTC (rev 7781)
+++ trunk/MgDev/Web/src/IsapiAgent/IsapiReaderStreamer.cpp	2013-08-19 15:52:25 UTC (rev 7782)
@@ -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: trunk/MgDev/Web/src/IsapiAgent/IsapiReaderStreamer.h
===================================================================
--- trunk/MgDev/Web/src/IsapiAgent/IsapiReaderStreamer.h	2013-08-18 09:38:27 UTC (rev 7781)
+++ trunk/MgDev/Web/src/IsapiAgent/IsapiReaderStreamer.h	2013-08-19 15:52:25 UTC (rev 7782)
@@ -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: trunk/MgDev/Web/src/IsapiAgent/IsapiResponseHandler.cpp
===================================================================
--- trunk/MgDev/Web/src/IsapiAgent/IsapiResponseHandler.cpp	2013-08-18 09:38:27 UTC (rev 7781)
+++ trunk/MgDev/Web/src/IsapiAgent/IsapiResponseHandler.cpp	2013-08-19 15:52:25 UTC (rev 7782)
@@ -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