cat STDERR | rot13

Submitted by gwolf on Fri, 02/08/2008 - 11:52

Cannot help but laugh and share.
I've been triaging and trying to reproduce some oldish bugs on pkg-perl's packages. Some bugs are no longer there, some have to be forwarded upstream, and so on. Usual tasks, yes.
Until I stumbled with #406227. I just have to laugh and share! Hope nobody feels ashamed - The bug is the result of different people coding maybe under pressure and with quite different mindsets :)
For some reason I fail to understand, the submitter's test case (rot13 implemented over a HTTP proxy) is invoked in the report as ./rot13 2>/dev/null. Of course, when trying to debug a bug report, the first thing to do is not to ignore STDERR. So, off goes the 2>/dev/null. What happens next?

  1. 0 gwolf@mosca[2]/tmp$ perl ./rot13 &
  2. [1] 4394
  3. 0 gwolf@mosca[3]/tmp$ GET -p <a href="http://localhost:8080/" title="http://localhost:8080/">http://localhost:8080/</a> <a href="<br />
  4. Can't" title="<br />
  5. Can't"><br />
  6. Can't</a> locate object method "filter" via package "UGGC::Cebkl::ObqlSvygre::fvzcyr=UNFU(0k604160)" (perhaps you forgot to load "UGGC::Cebkl::ObqlSvygre::fvzcyr=UNFU(0k604160)"?) at /usr/share/perl5/HTTP/Proxy/ line 126.
  7. 500 EOF when chunk header expected

WTF... Well, at least the program name gives me a clue... Lets try to "decrypt" the error message...

  1. gwolf@mosca[4]/tmp$ echo 'UGGC::Cebkl::ObqlSvygre::fvzcyr=UNFU(0k604160)' | rot13
  2. HTTP::Proxy::BodyFilter::simple=HASH(0x604160)

hrm... How comes the filter is filtering its own code and only then refusing to find itself!? Ok, time to open up the manpage - Remember, I'm only group-maintaining this pacakge, I am not yet at all familiar with it! Ok, so the core of the filter is when the submitter states:

  1. my $proxy = new HTTP::Proxy();
  2. $proxy->push_filter(response => new HTTP::Proxy::BodyFilter::simple(sub { tr/a-zA-z/n-za-mN-ZA-M/; }));

While the manpage states it should be invoked as:

  1. my $filter = HTTP::Proxy::BodyFilter::simple->new( sub { ${ $_[1] } =~ s/foo/bar/g; } );
  2. $proxy->push_filter( response => $filter );

Of course, once looking at it, the answer is simple: The submitter left out which element to act on in the anonymous function body - The ${ $_[1] } =~ part. Adding it makes gur svygre jbex nf rkcrpgrq... Err, sorry - makes the filter work as expected.

Now, bonus points: For the non-Perlers out ther: How come we get the namespace translated as well? Oh, that's very simple: In Perl, as in Python (and concievably other languages I'm unaware of), the object is passed to any of its methods as the first argument. Functions in Perl receive their arguments via @_ (read: the default array). And, of course, the tr (regex-based transliteration) takes by default the first thing it sees - the object itself. And what happens when you apply a (string-oriented) regex to an object? Of course, it gets stringified - which, by default, in Perl means converting it to the closest possible description: "a hash reference blessed as an object of the class such-and-such at this memory location". That string gets worked on, and we get UGGC::Cebkl::ObqlSvygre::fvzcyr=UNFU(0k604160). This proxy does not die on very-very-short web pages, where the whole content fits on one iteration of the code (although it does not work correctly - the text remains unaltered, of course, as it was not worked on), but if the request spans several chunks, the second time the filter is called, it will be... just gibberish.

Oh, and what about the extra ${ (...) } around $_[1]? Oh, simple: The string is passed as a scalar reference, so it can be modified in place. Yes, it's the Perl way of pass-by-reference instead of pass-by-value (the default behaviour): Of course the parameter is only passed as a value. Only that the value is incidentally a reference - but who cares? ;-)

Anyway... Many oddities. I would implement the module in a completely different way, and it looks quite backwardish in my book. But then again, TIMTOWTDI.

( categories: )
chromatic's picture

I'm Confused

The unbound transliteration operator applies to $_, not @_ or $_[0].