Author: hqm
Date: 2007-11-05 12:00:28 -0800 (Mon, 05 Nov 2007)
New Revision: 7132

Added:
   openlaszlo/trunk/WEB-INF/lib/xpp3-1.1.4c.jar
   openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/data/HttpData.java
Removed:
   openlaszlo/trunk/WEB-INF/lib/xpp3-1.1.3.4d_b4.jar
Modified:
   openlaszlo/trunk/WEB-INF/lps/lfc/kernel/swf/LzLoadQueue.as
   openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/data/DataSource.java
   
openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/data/HTTPDataSource.java
   openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/data/XMLGrabber.java
Log:
Change 20071105-hqm-0 by [EMAIL PROTECTED] on 2007-11-05 14:54:40 EST
    in /cygdrive/c/users/hqm/openlaszlo/trunk
    for http://svn.openlaszlo.org/openlaszlo/trunk

Summary: put XML parser back into server data proxy pipeline, to do
charset transcoding

New Features:

Bugs Fixed: LPP-4924

Technical Reviewer: max
QA Reviewer: pablo
Doc Reviewer:

Details:

The server data proxy now uses the XMLPULL parser to parse
the data from the backend, in order to use Java to force a translation
into UTF-8
coding if needed.

This change also uses a worker Thread to read from the backend, while
simultaneously
pipelining the data back throug the XML PULL parser to the client.
This should improve
response time and also removes a potential memory overflow and DOS
attack on the server.

Tests:

test/lfc/data/alldata.lzx
amazon
calendar




Deleted: openlaszlo/trunk/WEB-INF/lib/xpp3-1.1.3.4d_b4.jar

Added: openlaszlo/trunk/WEB-INF/lib/xpp3-1.1.4c.jar


Property changes on: openlaszlo/trunk/WEB-INF/lib/xpp3-1.1.4c.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Modified: openlaszlo/trunk/WEB-INF/lps/lfc/kernel/swf/LzLoadQueue.as
===================================================================
--- openlaszlo/trunk/WEB-INF/lps/lfc/kernel/swf/LzLoadQueue.as  2007-11-05 
19:52:33 UTC (rev 7131)
+++ openlaszlo/trunk/WEB-INF/lps/lfc/kernel/swf/LzLoadQueue.as  2007-11-05 
20:00:28 UTC (rev 7132)
@@ -94,8 +94,10 @@
   */
 LzLoadQueue.XMLOnDataHandler = function (src) {
     if (src == undefined) {
-        Debug.warn("LzLoadQueue.XMLOnDataHandler load failed from URL %w, no 
data received.", this.url);
-        Debug.warn("Failure to load data in serverless apps may be caused by 
Flash player security policies. Check your data server crossdomain.xml file");
+        if (!this.proxied) {
+            Debug.warn("LzLoadQueue.XMLOnDataHandler load failed from URL %w, 
no data received.", this.url);
+            Debug.warn("Failure to load data in serverless apps may be caused 
by Flash player security policies. Check your data server crossdomain.xml 
file");
+        }
         this.onload(false);
         //Debug.write("this.loader.onerror.ready =", 
this.loader.onerror.ready);
         if (this.loader.onerror.ready) {

Modified: 
openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/data/DataSource.java
===================================================================
--- openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/data/DataSource.java 
2007-11-05 19:52:33 UTC (rev 7131)
+++ openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/data/DataSource.java 
2007-11-05 20:00:28 UTC (rev 7132)
@@ -3,7 +3,7 @@
  * 
****************************************************************************/
 
 /* J_LZ_COPYRIGHT_BEGIN *******************************************************
-* Copyright 2001-2004 Laszlo Systems, Inc.  All Rights Reserved.              *
+* Copyright 2001-2007 Laszlo Systems, Inc.  All Rights Reserved.              *
 * Use is subject to license terms.                                            *
 * J_LZ_COPYRIGHT_END *********************************************************/
 
@@ -169,7 +169,7 @@
                         org.openlaszlo.i18n.LaszloMessages.getMessage(
                                 DataSource.class.getName(),"051018-169", new 
Object[] {new Long(size)})
 );
-                res.setContentLength((int)size);
+                //res.setContentLength((int)size);
             }
 
             if (doClientCache) {

Modified: 
openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/data/HTTPDataSource.java
===================================================================
--- 
openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/data/HTTPDataSource.java 
    2007-11-05 19:52:33 UTC (rev 7131)
+++ 
openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/data/HTTPDataSource.java 
    2007-11-05 20:00:28 UTC (rev 7132)
@@ -614,215 +614,8 @@
                                 HTTPDataSource.class.getName(),"051018-592", 
new Object[] {new Integer(code), HttpStatus.getStatusText(code)});
     }
 
-    /**
-     * A class for holding on to results of an Http fetch.
-     *
-     * @author <a href="mailto:[EMAIL PROTECTED]">Eric Bloch</a>
-     */
 
-    public static class HttpData extends Data {
 
-        /** response code */
-        public final int code;
-    
-        /** Http request */
-        public final HttpMethodBase  request;
-    
-        private PatternMatcher pMatcher = new Perl5Matcher();
-        private static final Pattern charsetPattern;
-        private static final Pattern declEncodingPattern;
-        static {
-            try {
-                Perl5Compiler compiler = new Perl5Compiler();
-                charsetPattern = compiler.compile(";charset=([^ ]*)");
-                declEncodingPattern =
-                    compiler.compile("[ \t\r\n]*<[?]xml .*encoding=[\"']([^ 
\"']*)[\"'] .*[?]>");
-            } catch (MalformedPatternException e) {
-                throw new RuntimeException(e.getMessage());
-            }
-        }
-
-        /** 
-         * @param r filled request
-         * @param c response code
-         */
-        public HttpData(HttpMethodBase r, int c) {
-            code = c;
-            request = r;
-        }
-    
-        /**
-         * @return true if the data was "not modified"
-         */
-        public boolean notModified() {
-            return code == HttpServletResponse.SC_NOT_MODIFIED;
-        }
-    
-        /**
-         * @return the lastModified time of the data
-         */
-        public long lastModified() {
-    
-            Header lastModifiedHdr = request.getResponseHeader(
-                LZHttpUtils.LAST_MODIFIED);
-                        
-            if (lastModifiedHdr != null) {
-                String lm = lastModifiedHdr.getValue();
-                mLogger.debug(
-/* (non-Javadoc)
- * @i18n.test
- * @org-mes="data with last modified at " + p[0]
- */
-                        org.openlaszlo.i18n.LaszloMessages.getMessage(
-                                HTTPDataSource.class.getName(),"051018-655", 
new Object[] {lm})
-);
-                long l = LZHttpUtils.getDate(lm);
-                // Truncate to nearest second
-                return ((l)/1000L) * 1000L;
-            } else {
-                mLogger.debug(
-/* (non-Javadoc)
- * @i18n.test
- * @org-mes="data has no mod time"
- */
-                        org.openlaszlo.i18n.LaszloMessages.getMessage(
-                                HTTPDataSource.class.getName(),"051018-667")
-);
-                return -1;
-            }
-        }
-    
-        /**
-         * append response headers
-         */
-        public void appendResponseHeadersAsXML(StringBuffer xmlResponse) {
-    
-            Header[] hedz = request.getResponseHeaders();
-            for (int i = 0; i < hedz.length; i++) {
-                String name = hedz[i].getName();
-                if (LZHttpUtils.allowForward(name, null)) {
-                    xmlResponse.append("<header name=\""+ XMLUtils.escapeXml( 
name ) + "\" " 
-                                     + "value=\""       + XMLUtils.escapeXml( 
hedz[i].getValue() ) + "\" />");
-                }
-            }
-        }
-    
-        /**
-         * release any resources associated with this data
-         */
-        public void release() {
-            request.releaseConnection();
-        }
-
-        /**
-         * @return mime type 
-         */
-        public String getMimeType() {
-            Header hdr = request.getResponseHeader(LZHttpUtils.CONTENT_TYPE);
-            String contentType = "";
-            if (hdr != null) {
-                contentType = hdr.getValue();
-            }
-            mLogger.debug(
-/* (non-Javadoc)
- * @i18n.test
- * @org-mes="content type: " + p[0]
- */
-                        org.openlaszlo.i18n.LaszloMessages.getMessage(
-                                HTTPDataSource.class.getName(),"051018-710", 
new Object[] {contentType})
-);
-            return contentType;
-        }
-
-        /**
-         * @return string
-         */
-        public String getAsString() throws IOException {
-            byte rawbytes[] = request.getResponseBody();
-            if (rawbytes == null || rawbytes.length == 0) {
-                throw new InterruptedIOException("null http response body");
-            }
-            String encoding = "UTF-8";
-            String content = getMimeType();
-            // search for ;charset=XXXX in Content-Type header
-            if (pMatcher.matches(content, charsetPattern)) {
-                encoding = pMatcher.getMatch().group(1);
-            }
-            // search for 'encoding' attribute in xml declaration, e.g.,
-            // <?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>
-            
-            String decl = getXMLDeclaration(rawbytes);
-            if (pMatcher.matches(decl, declEncodingPattern)) {
-                encoding = pMatcher.getMatch().group(1);
-                //mLogger.debug("parsed data encoding: " + encoding);
-            }
-            
-            return new String(rawbytes, encoding);
-        }
-
-        /** Returns the first non-whitespace line.
-         *
-         */
-        String getXMLDeclaration(byte buf[]) {
-            String str = new String(buf);
-            BufferedReader br = new BufferedReader(new StringReader(str));
-            String line;
-            while (true) {
-                try { line = br.readLine(); } catch (IOException e) { return 
""; }
-                if (line == null) {
-                    return "";
-                }
-                if (line.length() == 0) continue;
-                if (line.startsWith("<?xml ")) {
-                    return line;
-                } else {
-                    return "";
-                }
-            }
-        }
-
-        /**
-         * @return input stream
-         */
-        public InputStream getInputStream() throws IOException {
-            InputStream str = request.getResponseBodyAsStream();
-            if (str == null) {
-                throw new IOException(
-/* (non-Javadoc)
- * @i18n.test
- * @org-mes="http response body is null"
- */
-                        org.openlaszlo.i18n.LaszloMessages.getMessage(
-                                HTTPDataSource.class.getName(),"051018-774")
-);
-            }
-            return str;
-        }
-
-        /**
-         * @return size, if known
-         */
-        public long size() {
-            Header hdr = request.getResponseHeader(LZHttpUtils.CONTENT_LENGTH);
-            if (hdr != null) {
-                String contentLength = hdr.getValue();
-                if (contentLength != null) {
-                    mLogger.debug(
-/* (non-Javadoc)
- * @i18n.test
- * @org-mes="content length: " + p[0]
- */
-                        org.openlaszlo.i18n.LaszloMessages.getMessage(
-                                HTTPDataSource.class.getName(),"051018-794", 
new Object[] {contentLength})
-);
-                    int cl = Integer.parseInt(contentLength);
-                    return cl;
-                }
-            }
-            return -1;
-        }
-    }
-
     public static int getConnectionPoolTimeout() {
         return mConnectionPoolTimeout;
     }

Added: openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/data/HttpData.java


Property changes on: 
openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/data/HttpData.java
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + text/plain
Name: svn:eol-style
   + native

Modified: 
openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/data/XMLGrabber.java
===================================================================
--- openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/data/XMLGrabber.java 
2007-11-05 19:52:33 UTC (rev 7131)
+++ openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/data/XMLGrabber.java 
2007-11-05 20:00:28 UTC (rev 7132)
@@ -8,7 +8,7 @@
 * ****************************************************************************/
 
 /* J_LZ_COPYRIGHT_BEGIN *******************************************************
-* Copyright 2001-2006 Laszlo Systems, Inc.  All Rights Reserved.              *
+* Copyright 2001-2007 Laszlo Systems, Inc.  All Rights Reserved.              *
 * Use is subject to license terms.                                            *
 * J_LZ_COPYRIGHT_END *********************************************************/
 
@@ -23,6 +23,7 @@
 import org.apache.log4j.*;
 
 import org.openlaszlo.utils.ContentEncoding;
+import org.openlaszlo.utils.LZHttpUtils;
 
 import org.openlaszlo.media.MimeType;
 import org.openlaszlo.server.LPS;
@@ -35,7 +36,14 @@
 import org.jdom.input.SAXBuilder;
 import org.jdom.output.XMLOutputter;
 
+import org.apache.commons.httpclient.*;
+import org.apache.commons.httpclient.methods.*;
+import org.apache.commons.httpclient.util.*;
 
+import org.xmlpull.v1.*;
+
+
+
 /**
  * XML Converter
  *
@@ -43,10 +51,33 @@
 public class XMLGrabber extends Converter {
     
     private static Logger mLogger  = Logger.getLogger(XMLGrabber.class);
+    private static XmlPullParserFactory factory = null;
 
+    private static XmlPullParserFactory getXPPFactory () {
+        if (factory == null) {
+            // Set up the XML Parser factory
+            try {
+                String sys = null; 
+                try {
+                    sys = 
System.getProperty(XmlPullParserFactory.PROPERTY_NAME);
+                } catch (SecurityException se) {
+                }
+                factory = XmlPullParserFactory.newInstance(sys, null);
+                factory.setNamespaceAware(false);
+                factory.setValidating(false);
+            } catch (XmlPullParserException e) {
+                throw new RuntimeException(e.getMessage());
+            } 
+        }
+        return factory;
+    }
+
     /**
      * Convert incoming XML to ... XML 
      *
+     * This method is called convertToSWF for historical reasons, and nobody
+     * has changed the API call name yet. 
+     *
      * A dataset will look like this:
      * <resultset>
      * <body>
@@ -61,63 +92,193 @@
      * </headers>
      * </resultset>
      */
+
     public InputStream convertToSWF(Data data, HttpServletRequest req,
                                     HttpServletResponse res)
        throws ConversionException, IOException {
 
-        String body = data.getAsString();
+        try {
+            PipedOutputStream pout = new PipedOutputStream();
+            PipedInputStream in = new PipedInputStream(pout);
 
-        // Get headers
-        String sendheaders = req.getParameter("sendheaders");
-        StringBuffer headerbuf = new StringBuffer();
-        headerbuf.append("<headers>\n");
-        if (sendheaders == null || sendheaders.equals("true") ) {
-            data.appendResponseHeadersAsXML(headerbuf);
-        }
-        headerbuf.append("</headers>");
-        String headers = headerbuf.toString();
+            XmlSerializer serializer;
+            XmlPullParser parser;
+            parser = getXPPFactory().newPullParser();
+            InputStream dstream = data.getInputStream();
+            parser.setInput( dstream, null );
+            serializer = factory.newSerializer();
+            serializer.setOutput(pout , "UTF-8");
 
-        if (mLogger.isDebugEnabled()) {
-            mLogger.info("Output:" + body.length());
-            mLogger.info("Output:\n" + body);
-            mLogger.info("Output Headers:" + headers.length());
-            mLogger.info("Output Headers:\n" + headers);
+            HttpMethodBase request = ((HttpData) data).getRequest();
+
+            final String sendheaders = req.getParameter("sendheaders");
+
+            XMLCopyThread worker = new XMLCopyThread(pout, parser,serializer, 
request, sendheaders);
+            worker.start();
+
+            return in;
+
+        } catch (XmlPullParserException ex) {
+            throw new ConversionException("Parsing XML: " + ex.getMessage());
         }
+    }
 
-        // Default to true, for back compatibility (sigh)
-        boolean trimWhitespace = true;
-        String trimval = req.getParameter("trimwhitespace");
-        if ("false".equals(trimval)) {
-                trimWhitespace = false;
+    // Worker thread to parse XML (which serves to translate obscure
+    // charsets to UTF-8) and wrap it in <resultset>, possibly adding
+    // proxied HTTP headers from backedn response.
+    // This is written to the PipedOutputStream which we were passed.
+    class XMLCopyThread extends Thread implements Runnable {
+        OutputStream pout = null;    
+        XmlPullParser parser;
+        XmlSerializer serializer;
+        HttpMethodBase request;
+        String sendheaders = null;
+        
+        XMLCopyThread( OutputStream pout, XmlPullParser parser, XmlSerializer 
serializer,
+                       HttpMethodBase request,
+                       String sendheaders) {
+            this.pout = pout;
+            this.parser = parser;
+            this.serializer = serializer;
+            this.request = request;
+            this.sendheaders = sendheaders;
         }
 
-        boolean compress = "true".equals(req.getParameter("compress"));
-
-        // nsprefix now defaults to true
-        boolean nsprefix = true;
-        if ("false".equals(req.getParameter("nsprefix"))) {
-            nsprefix = false;
+        public void run() {
+            try {
+                writeXMLDataToOutputStream();
+                pout.flush();
+                pout.close();
+            } catch (XmlPullParserException ex) {
+                throw new RuntimeException(ex);
+            } catch (IOException ex) {
+                throw new RuntimeException(ex);
+            }
         }
 
-        // Need to parse body into a DOM, and add the headers, then re-emit as 
XML
-        // of the form
+        // Generates an XML document with this structure
         // <resultset>
-        //   <body> ... </body>
+        //   <body> [PROXIED_XMLDOC] </body>
         //   <headers> ... </headers>
         // </resultset>
+        void writeXMLDataToOutputStream() throws XmlPullParserException, 
IOException {
+            //Run through XML PULL parser, to convert to UTF8, and
+            // wrap in <resultset> tag, plus optional headers 
 
-        StringBuffer xdata = new StringBuffer();
-        xdata.append("<resultset>\n");
-        xdata.append("<body>\n");
-        String xbody = body.replaceFirst("<[?]xml.*?[?]>","");
-        xdata.append(xbody);
-        xdata.append("</body>\n");
-        xdata.append(headers);
-        xdata.append("</resultset>\n");
+            // Start a standalone document;
+            //serializer.startDocument("UTF-8", Boolean.TRUE);
 
-        // generate inputstream from DOM
-        ByteArrayInputStream dis = new 
ByteArrayInputStream((xdata.toString()).getBytes("UTF-8"));
-        return dis;
+            serializer.startTag("", "resultset");
+            serializer.startTag("", "body");
+
+            parser.nextToken(); // read first token
+
+            while (parser.getEventType () != XmlPullParser.END_DOCUMENT) {
+                writeToken ( parser.getEventType () );
+                parser.nextToken ();
+            }
+
+            serializer.endTag("", "body");
+
+            //   <headers> ... </headers>
+            serializer.startTag("", "headers");
+
+            // Get headers
+            if (sendheaders == null || sendheaders.equals("true") ) {
+                Header[] hedz = request.getResponseHeaders();
+                for (int i = 0; i < hedz.length; i++) {
+                    String name = hedz[i].getName();
+                    if (LZHttpUtils.allowForward(name, null)) {
+                        serializer.startTag("", "header");                    
+
+                        serializer.attribute (null, "name", name);
+                        serializer.attribute (null, "value", 
hedz[i].getValue());
+                        serializer.endTag("", "header");                    
+                    }
+                }
+            }
+            serializer.endTag("", "headers");
+
+            serializer.endTag("", "resultset");
+            serializer.endDocument();
+        }
+
+        private void writeStartTag ()
+          throws XmlPullParserException, IOException {
+            if (!parser.getFeature 
(XmlPullParser.FEATURE_REPORT_NAMESPACE_ATTRIBUTES)) {
+                for (int i = parser.getNamespaceCount (parser.getDepth ()-1);
+                     i <= parser.getNamespaceCount (parser.getDepth ())-1; 
i++) {
+                    serializer.setPrefix
+                        (parser.getNamespacePrefix (i),
+                         parser.getNamespaceUri (i));
+                }
+            }
+            serializer.startTag(parser.getNamespace (), parser.getName ());
+        
+            for (int i = 0; i < parser.getAttributeCount (); i++) {
+                serializer.attribute
+                    (parser.getAttributeNamespace (i),
+                     parser.getAttributeName (i),
+                     parser.getAttributeValue (i));
+            }
+        }
+
+
+        private void writeToken (int eventType)
+          throws XmlPullParserException, IOException {
+            switch (eventType) {
+                
+              case XmlPullParser.START_TAG:
+                writeStartTag ();
+                break;
+                
+              case XmlPullParser.END_TAG:
+                serializer.endTag(parser.getNamespace (), parser.getName ());
+                break;
+                
+            case XmlPullParser.START_DOCUMENT:
+                //use Boolean.TRUE to make it standalone
+              //Boolean standalone = (Boolean) 
parser.getProperty(PROPERTY_XMLDECL_STANDALONE);
+              //serializer.startDocument(parser.getInputEncoding(), 
standalone);
+                break;
+
+              case XmlPullParser.END_DOCUMENT:
+              //serializer.endDocument();
+                break;
+
+              case XmlPullParser.IGNORABLE_WHITESPACE:
+                //comment it to remove ignorable whtespaces from XML infoset
+                String s = parser.getText ();
+                serializer.ignorableWhitespace (s);
+                break;
+                
+              case XmlPullParser.TEXT:
+                serializer.text (parser.getText ());
+                break;
+                
+              case XmlPullParser.ENTITY_REF:
+                serializer.entityRef (parser.getName ());
+                break;
+                
+              case XmlPullParser.CDSECT:
+                serializer.cdsect( parser.getText () );
+                break;
+                
+              case XmlPullParser.PROCESSING_INSTRUCTION:
+                // serializer.processingInstruction( parser.getText ());
+                break;
+                
+              case XmlPullParser.COMMENT:
+                //serializer.comment (parser.getText ());
+                break;
+                
+              case XmlPullParser.DOCDECL:
+                // serializer.docdecl (parser.getText ());
+                break;
+            }
+        }
+
+
     }
 
     /**


_______________________________________________
Laszlo-checkins mailing list
[email protected]
http://www.openlaszlo.org/mailman/listinfo/laszlo-checkins

Reply via email to