XML :: LibXML无法通过HTTPS验证可用的DTD

问题描述 投票:1回答:1

当DTD可通过HTTPS使用时,如何使用XML::LibXML验证XML文档?

Test code:

#!/usr/bin/perl -w

use XML::LibXML;

use strict;

my $xml = XML::LibXML->load_xml(IO => \*DATA);
my $dtd = XML::LibXML::Dtd->new( "-//NLM//DTD LinkOut 1.0//EN", "https://www.ncbi.nlm.nih.gov/projects/linkout/doc/LinkOut.dtd" );
my $https_is_valid = $xml->is_valid( $dtd );
print "HTTPS dtd: ", ref $dtd, "\n Is valid: $https_is_valid\n";

my $dtd_http = XML::LibXML::Dtd->new( "-//NLM//DTD LinkOut 1.0//EN", "http://www.ncbi.nlm.nih.gov/projects/linkout/doc/LinkOut.dtd" );
my $http_is_valid = $xml->is_valid( $dtd_http );
print "HTTP dtd: ", ref $dtd_http, "\n Is valid: $http_is_valid\n";

__DATA__
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE LinkSet PUBLIC "-//NLM//DTD LinkOut 1.0//EN" "https://www.ncbi.nlm.nih.gov/projects/linkout/doc/LinkOut.dtd" [
<!ENTITY base.url "https://some.domain.com">
<!ENTITY icon.url "https://some.domain.com/logo.png">
]>
<LinkSet>
  <Link>
    <LinkId>1</LinkId>
    <ProviderId>XXXX</ProviderId>
    <IconUrl>&icon.url;</IconUrl>
    <ObjectSelector>
      <Database>PubMed</Database>
      <ObjectList>
        <ObjId>1234567890</ObjId>
      </ObjectList>
    </ObjectSelector>
    <ObjectUrl>
      <Base>&base.url;</Base>
      <Rule>/1/</Rule>
    </ObjectUrl>
  </Link>
</LinkSet>

上面的代码产生以下输出:

HTTPS dtd:
  Is valid: 0
HTTP dtd: XML::LibXML::Dtd
  Is valid: 1

DTD无法从HTTPS URL加载,因此无法用于验证XML。

我已经通过HTTPS下载了DTD并检查了HTTP重定向 - 没有任何重定向。

我也看过XML::LibXML::InputCallback,但看不出我如何将它与XML::LibXML::Dtd->new( ... );合并。

我该如何实现此验证?

DTD可通过HTTP获得,因此我可以使用它进行验证,但这感觉就像我正在避免问题而不是正确解决它!

perl https dtd xml-libxml
1个回答
2
投票

请注意,XML已包含DTD的URL,因此您无需创建XML::LibXML::Dtd即可传递给->is_valid

我同意评论员Grant McLean的说法,你可能不想一直在网络上出去。事实上,不久前我写了一些代码,使用XML::LibXML::InputCallback将所有网络请求重定向到我缓存网络资源的本地FS。

但是要回答你的问题,通过HTTP::Tiny调整代码来从网络中提取,包括HTTPS,这需要IO::Socket::SSL> = 1.56和Net::SSLeay> = 1.49才能获得SSL支持。以下打印预期的“Is valid: yes”:

use warnings;
use strict;
use XML::LibXML;
use HTTP::Tiny;
use URI;

my $parser = XML::LibXML->new;
my $cb = XML::LibXML::InputCallback->new;
my $http = HTTP::Tiny->new;
my %cache;
$cb->register_callbacks([
    sub { 1 }, # match (URI), returns Bool
    sub { # open (URI), returns Handle
        my $uri = URI->new($_[0]);
        my $file;
        #warn "Handling <<$uri>>\n"; #Debug
        if (!$uri->scheme) { $file = $_[0] }
        elsif ($uri->scheme eq 'file') { $file = $uri->path }
        elsif ($uri->scheme=~/\Ahttps?\z/i) {
            if (!defined $cache{$uri}) {
                my $resp = $http->get($uri);
                die "$uri: $resp->{status} $resp->{reason}\n"
                    unless $resp->{success};
                $cache{$uri} = $resp->{content};
            }
            $file = \$cache{$uri};
        }
        else { die "unsupported URL scheme: ".$uri->scheme }
        open my $fh, '<', $file or die "$file: $!";
        return $fh;
    },
    sub { # read (Handle,Length), returns Data
        my ($fh,$len) = @_;
        read($fh, my $buf, $len);
        return $buf;
    },
    sub { close shift } # close (Handle)
]);
$parser->input_callbacks($cb);

my $doc = $parser->load_xml( IO => \*DATA );
print "Is valid: ", $doc->is_valid ? "yes" : "no", "\n";

__DATA__
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE LinkSet PUBLIC "-//NLM//DTD LinkOut 1.0//EN" "https://www.ncbi.nlm.nih.gov/projects/linkout/doc/LinkOut.dtd" [
<!ENTITY base.url "https://some.domain.com">
<!ENTITY icon.url "https://some.domain.com/logo.png">
]>
<LinkSet>
  <Link>
    <LinkId>1</LinkId>
    <ProviderId>XXXX</ProviderId>
    <IconUrl>&icon.url;</IconUrl>
    <ObjectSelector>
      <Database>PubMed</Database>
      <ObjectList>
        <ObjId>1234567890</ObjId>
      </ObjectList>
    </ObjectSelector>
    <ObjectUrl>
      <Base>&base.url;</Base>
      <Rule>/1/</Rule>
    </ObjectUrl>
  </Link>
</LinkSet>
© www.soinside.com 2019 - 2024. All rights reserved.