Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision f5bc2335

Von Moritz Bunkus vor etwa 14 Jahren hinzugefügt

  • ID f5bc233522743646b118d0756b0041b16418fbdf
  • Vorgänger 514439bd
  • Nachfolger 90edf0b1

Ausgaben mit FCGI richtig codieren

Hintergrund:

FCGI benutzt Perls I/O-Schitensystem nicht. Deshalb kümmert es sich
auch nicht um mit 'binmode STDOUT, ":utf8"' gesetzte
Konvertierungsparameter. Weiterhin enthält FCGI ab Version 0.69 Fixes,
die doppeltes Encoding bei der Ausgabe vermeiden und damit eigentlich
korrektes Verhalten herstellen.

Leider geht damit Lx-Offices Art, wie Strings intern gehandhabt und
ausgegeben werden, in die Hose. Lx-Office speichert Strings in Perls
internem Encoding und verlässt sich auf die mit "binmode" aktivierte,
autoatmische Konvertierung bei der Ausgabe.

Dieser Workaround implementiert das Umcodieren vom internen Encoding
nach UTF-8 mittels Monkeypatching, bevor die FCGIs originale
PRINT-Routine aufgerufen wird.

Das darf allerdings nicht passieren, wenn unbearbeitete Ausgabe
benötigt wird -- z.B. beim Download von
Binärdaten (PDFs). Glücklicherweise ist dies in der Funktion
"with_raw_io" in Locale gekapselt, sodass dieser Workaround den Status
"unbearbeitete Ausgabe?" bei Locale erfragen kann.

Unterschiede anzeigen:

SL/FCGIFixes.pm
1
package SL::FCGIFixes;
2

  
3
use strict;
4

  
5
use Encode;
6
use FCGI;
7

  
8
# FCGI does not use Perl's I/O layer. Therefore it does not honor
9
# setting STDOUT to ":utf8" with "binmode".  Also FCGI starting with
10
# 0.69 implements proper handling for UTF-8 flagged strings -- namely
11
# by downgrading them into bytes. The combination of the two causes
12
# Lx-Office's way of handling strings to go belly up (storing
13
# everything in Perl's internal encoding and using Perl's I/O layer
14
# for automatic conversion on output).
15
#
16
# This workaround monkeypatches FCGI's print routine so that all of
17
# its arguments safe for "$self" are encoded into UTF-8 before calling
18
# FCGI's original PRINT function.
19
#
20
# However, this must not be done if raw I/O is requested -- e.g. when
21
# sending out binary data. Fortunately that has been centralized via
22
# Locale's "with_raw_io" function which sets a variable indicating
23
# that current I/O operations should be raw.
24

  
25
sub fix_print_and_internal_encoding_after_0_68 {
26
  return if version->parse($FCGI::VERSION) <= version->parse("0.68");
27

  
28
  my $encoder             = Encode::find_encoding('UTF-8');
29
  my $original_fcgi_print = \&FCGI::Stream::PRINT;
30

  
31
  no warnings 'redefine';
32

  
33
  *FCGI::Stream::PRINT = sub {
34
    if (!$::locale || !$::locale->raw_io_active) {
35
      my $self = shift;
36
      my @vals = map { $encoder->encode($_, Encode::FB_CROAK|Encode::LEAVE_SRC) } @_;
37
      @_ = ($self, @vals);
38
    }
39

  
40
    goto $original_fcgi_print;
41
  };
42
}
43

  
44
sub apply_fixes {
45
  fix_print_and_internal_encoding_after_0_68();
46
}
47

  
48
1;
SL/Locale.pm
462 462
  return $self->quote_special_chars($dst_format, $self->quote_special_chars("${src_format}-reverse", shift));
463 463
}
464 464

  
465
sub raw_io_active {
466
  my $self = shift;
467

  
468
  return !!$self->{raw_io_active};
469
}
470

  
465 471
sub with_raw_io {
466 472
  my $self = shift;
467 473
  my $fh   = shift;
468 474
  my $code = shift;
469 475

  
476
  $self->{raw_io_active} = 1;
470 477
  binmode $fh, ":raw";
471 478
  $code->();
472 479
  binmode $fh, ":utf8" if $self->is_utf8;
480
  $self->{raw_io_active} = 0;
473 481
}
474 482

  
475 483
1;
dispatcher.fpl
4 4

  
5 5
use FCGI;
6 6
use SL::Dispatcher;
7
use SL::FCGIFixes;
8

  
9
SL::FCGIFixes::apply_fixes();
7 10

  
8 11
SL::Dispatcher::pre_startup();
9 12
my $request = FCGI::Request();

Auch abrufbar als: Unified diff