Make invalid XML handling more robust, verbose
authorJeff Godin <jgodin@tadl.org>
Thu, 5 Dec 2013 21:24:00 +0000 (16:24 -0500)
committerJeff Godin <jgodin@tadl.org>
Thu, 15 May 2014 18:20:38 +0000 (14:20 -0400)
Differentiate between LibXML and non-LibXML errors when parsing
incoming NCIP messages.

Log the actual error encountered, and log what steps we take (if
any) to mitigate.

Also, remove an invalid use of == introduced in the previous commit.

Signed-off-by: Jeff Godin <jgodin@tadl.org>
iNCIPit.cgi

index 3466dc6..444618f 100644 (file)
@@ -140,18 +140,46 @@ eval {
     $doc = $parser->load_xml( string => $xml );
 };
 
-# If unsuccessful, attempt to modify XML and try again
-# This is based on actual invalid XML encountered in the wild
-# in an INN-REACH environment.
-if ($@ && ref($@) == 'XML::LibXML::Error') {
-    warn "Unable to parse XML on first try. Attempting to de-mangle.\n";
-    $xml =~ s/\x04//g; # Remove ^D from xml
-    eval {
-        $doc = $parser->load_xml( string => $xml );
-    };
+# Attempt to gracefully handle invalid XML, including mitigations for known common issues.
+if ($@ && ref($@) ne 'XML::LibXML::Error') {
+    # We received an error, but it was not a LibXML error object
+    fail("Unknown error parsing XML: $@");
+} elsif ($@) {
+    # We received an error in the form of a LibXML error object
+
+    my $warning = sprintf("Unable to parse XML on the first try. LibXML error code: %s, message: %s", $@->code(), $@->message());
+    warn $warning;
+
+    # If the error was ERR_INVALID_CHAR, attempt to modify XML and try again
+    if ($@->code() == XML::LibXML::ErrNo::ERR_INVALID_CHAR) {
+
+        warn "Attempting to de-mangle by removing known invalid character(s).\n";
+
+        # This is based on actual invalid XML encountered in the wild
+        # in an INN-REACH environment.
+        $xml =~ s/\x04//g; # Remove ^D from xml
+
+        # Attempt to re-parse after de-mangling
+        eval {
+            $doc = $parser->load_xml( string => $xml );
+        };
+
+        if ($@ && ref($@) ne 'XML::LibXML::Error') {
+            # We received an error, but it was not a LibXML error object
+            fail("Unknown error parsing XML on second attempt: $@");
+        } elsif ($@) {
+            # We received an error in the form of a LibXML error object
+            my $error = sprintf("Unable to parse XML even after de-mangling. LibXML error code: %s, message: %s", $@->code(), $@->message());
+            fail($error);
+        }
+        warn "Success parsing XML after de-mangling.\n";
+    } else {
+        # This is not an error that we know how to recover from
+        fail("No known workaround for this error. Giving up.") unless $doc;
+    }
 }
 
-fail("Unable to parse XML. Giving up.") unless $doc;
+fail("XML parsing did not result in a document.") unless $doc && ref($doc) eq 'XML::LibXML::Document';
 
 my %session = login();