2

I have the following code:

...
sub setImage {
    my $self=shift;
    my $filename=shift;
    unless(-r $filename) {
        warn "File $filename not found";
        return;
    }
    my $imgn=shift;
    my $operation=&URI::Escape::uri_escape_utf8(
        (shift) ? "Удалить! (Delete)" : "Сохранить! (Store)");
    my $FH=&::File::open($filename, 0, 0);
    my $image;
    # &utf8::downgrade($image);
    sysread($FH, $image, 102400, 0);
    close $FH;
    my $imginfo=eval{&Image::Info::image_info(\$image)};
    if($@ or $imginfo->{"error"}) {
        warn "Invalid image: ".($@ || $imginfo->{"error"});
        return undef;
    }
    my $fields=[
        DIR       => $self->url("fl"),
        OPERATION => $operation,
        FILE_NAME => ".photo$imgn",
        # FILE      => [$filename],
        FILE      => [undef, "image.".$imginfo->{"file_ext"},
            # Content_Type => $imginfo->{"file_media_type"},
            # Content_Type => 'application/octet-stream',
            Content      => $image,
        ],
    ];
    my $response=&ZLR::UA::post(
        &ZLR::UA::absURL("/cgi-bin/file_manager")."",
        $fields,
        Content_Type => "form-data",
    );
    print $response->decoded_content;
}
...

When I try to use function setImage it fails with error HTTP::Message content must be bytes at /usr/lib64/perl5/vendor_perl/5.8.8/HTTP/Request/Common.pm line 91. Worse that I can't reproduce this error without using all of my code and upgrading libwww-perl does nothing. What can cause it?

Versions of libww-perl: dev-perl/libwww-perl-5.836. HTTP::Request and HTTP::Request::Common came from libwww-perl package, versions: 5.827 and 5.824.

Trace:

HTTP::Message content must be bytes at /usr/lib64/perl5/vendor_perl/5.8.8/HTTP/Request/Common.pm line 91
 at Carp::croak(unknown source)
 at HTTP::Message::__ANON__(/usr/lib64/perl5/vendor_perl/5.8.8/HTTP/Message.pm:16)
 at HTTP::Message::_set_content(/usr/lib64/perl5/vendor_perl/5.8.8/HTTP/Message.pm:136)
 at HTTP::Message::content(/usr/lib64/perl5/vendor_perl/5.8.8/HTTP/Message.pm:125)
 at HTTP::Request::Common::POST(/usr/lib64/perl5/vendor_perl/5.8.8/HTTP/Request/Common.pm:91)
 at LWP::UserAgent::post(/usr/lib64/perl5/vendor_perl/5.8.8/LWP/UserAgent.pm:397)
 at ZLR::UA::post(./zlrchecker.pl:71)
 at ZLR::Info::setImage(./zlrchecker.pl:1754)
 at main::main(./zlrchecker.pl:3893)
 at main::(./zlrchecker.pl:4148)
ZyX
  • 52,536
  • 7
  • 114
  • 135
  • Why are you calling subs with a leading `&`? – Ether Jun 01 '10 at 16:32
  • @Ether because I always call subs with a leading `&`. – ZyX Jun 01 '10 at 16:34
  • 1
    @ZyX, Don't it will supply the `@_` of the calling context implicitly, if you want the behavoir use it explicitly `foo(@_)`, not `&foo` – Evan Carroll Jun 01 '10 at 16:35
  • @Evan Carrol I never use `&foo`. Only `&foo(...)` or just `&foo()`. – ZyX Jun 01 '10 at 16:47
  • 1
    @ZyX: **you should not use `&` at all** unless you fully understand the ramifications for doing so and are certain that you need it (which should be a very rare case). – Ether Jun 01 '10 at 16:52
  • 2
    doc perlsub, The "&" is not optional when just naming the subroutine, such as when it's used as an argument to defined() or undef(). Nor is it optional when you want to do an indirect subroutine call with a subroutine name or reference using the "&$subref()" or "&{$subref}()" constructs, although the "$subref->()" notation solves that problem. Not only does the "&" form make the argument list optional, it also disables any prototype checking on arguments you do provide. This is partly for historical reasons, and partly for having a convenient way to cheat if you know what you're doing. – Evan Carroll Jun 01 '10 at 17:00
  • @Evan Carrol it is because `&` disables prototype checks? It is fun that I do not write prototypes because they were useless because I wrote `&` because I did not know about this and using `&` made my functions highlighted. – ZyX Jun 01 '10 at 17:08

2 Answers2

4

Use Devel::SimpleTrace and paste the trace. Install the module with cpan. Then run your program with -MDevel::SimpleTrace like perl -MDevel::SimpleTrace ./myapp_run.pl

And paste the version of HTTP::Request:Common, HTTP::Message, and LWP.

My guess is you'll see this in the stack trace:

This seems to be the code likely causing the error:

*_utf8_downgrade = defined(&utf8::downgrade) ?
    sub {
        utf8::downgrade($_[0], 1) or
            Carp::croak("HTTP::Message content must be bytes")
    }
    :
    sub {
    };

The docs in utf8 say this:

Fails if the original UTF-X sequence cannot be represented in the native 8 bit encoding. On failure dies or, if the value of FAIL_OK is true, returns false.

You should be able to make a test case by running utf8::downgrade($http_message_content)

Evan Carroll
  • 78,363
  • 46
  • 261
  • 468
  • In my function `&utf8::downgrade($image)` does not lead to death, but I still get an error. And I get this error if I use `FILE => [$filename]`. – ZyX Jun 01 '10 at 16:38
  • right, that error is the issue. `HTTP::Message` croaks if you get an error there with its own error (as shown in the code above) -- the second argument (1) means don't die just return failure, then it dies with the message "HTTP::Message content must be bytes" instead. – Evan Carroll Jun 01 '10 at 16:40
  • The problem is that if I try the following one-liner everything is OK: `perl -e 'use utf8; use LWP::UserAgent; my $response=LWP::UserAgent->new()->post("http://zhurnal.lib.ru/cgi-bin/file_manager", [DIR => "z/zyx", OPERATION => "Store", FILE_NAME => ".photo1", FILE => ["/home/zyx/a.a/\{smth\}/\{pic\}/speed.jpg"]], Content_Type => "form-data");'`. And if I do the same things in my function it dies. – ZyX Jun 01 '10 at 16:43
  • Then your function is doing something goofy. Try hand hacking in a dump of `$_[1]` (or otherwise getting to the $content) at HTTP::Message::_set_content(/usr/lib64/perl5/vendor_perl/5.8.8/HTTP/Message.pm:136). Then try running that output through `utf8::downgrade()`. – Evan Carroll Jun 01 '10 at 16:46
  • Commenting out `use encoding 'utf8';` in package main did the trick. Don't remember why I had put it alongside with `use utf8;`. – ZyX Jun 01 '10 at 17:05
0

One effective solution I found to this problem was to parse any text we put into HTTP::Message, during a HTTP request, through unidecode() from Text::Unidecode.

f055
  • 1,190
  • 12
  • 23