Document and test what handle_unicode_exception is supposed to return
Marco Pessotto [Wed, 20 Jul 2016 07:45:05 +0000 (09:45 +0200)]
lib/Catalyst.pm
t/unicode-exception-return-value.t [new file with mode: 0644]

index f3e5525..154b3d4 100644 (file)
@@ -3581,8 +3581,12 @@ Receives a hashref of debug information.  Example of call:
         encoding_step => 'params',
         });
 
-You can override this for custom handling of unicode errors.  If you want a
-custom response here, one approach is to throw an HTTP style exception:
+It expects to receive a decoded string.
+
+You can override this for custom handling of unicode errors. By
+default we just die. If you want a custom response here, one approach
+is to throw an HTTP style exception, instead of returning a decoded
+string or throwing a generic exception.
 
     sub handle_unicode_encoding_exception {
       my ($c, $params) = @_;
@@ -3595,13 +3599,15 @@ in your application:
     sub handle_unicode_encoding_exception {
       my ($c, $params) = @_;
       $c->stash(BAD_UNICODE_DATA=>$params);
+      # return a dummy string.
       return 1;
     }
 
-<B>NOTE:</b> Please keep in mind that once an error like this occurs, the request
-setup is aborted, which means the state of C<$c> and related context parts like
-the request and response may not be setup up correctly (since we never finished the
-setup.
+<B>NOTE:</b> Please keep in mind that once an error like this occurs,
+the request setup is still ongoing, which means the state of C<$c> and
+related context parts like the request and response may not be setup
+up correctly (since we haven't finished the setup yet). If you throw
+an exception the setup is aborted.
 
 =cut
 
diff --git a/t/unicode-exception-return-value.t b/t/unicode-exception-return-value.t
new file mode 100644 (file)
index 0000000..3f52a2a
--- /dev/null
@@ -0,0 +1,96 @@
+use strict;
+use warnings;
+use Test::More;
+use HTTP::Request::Common;
+
+BEGIN {
+    package TestApp::Controller::Root;
+    $INC{'TestApp/Controller/Root.pm'} = __FILE__;
+
+    use Moose;
+    use MooseX::MethodAttributes;
+    extends 'Catalyst::Controller';
+
+    sub main :Path('') :Args(1) {
+        my ($self, $c, $arg) = @_;
+        my $body = $arg . "\n";
+        my $query_params = $c->request->query_params;
+        my $body_params = $c->request->body_params;
+        foreach my $key (sort keys %$query_params) {
+            $body .= "Q $key => " . $query_params->{$key} . "\n";
+        }
+        foreach my $key (sort keys %$body_params) {
+            $body .= "B $key => " . $body_params->{$key} . "\n";
+        }
+        $c->res->body($body);
+        $c->res->content_type('text/plain');
+    }
+    TestApp::Controller::Root->config(namespace => '');
+}
+
+{
+    package TestApp;
+    $INC{'TestApp.pm'} = __FILE__;
+    use Catalyst;
+
+    sub handle_unicode_encoding_exception {
+        my ( $self, $param_value, $error_msg ) = @_;
+        # totally dummy: we return any invalid string with a fixed
+        # value. a more clever thing would be try to decode it from
+        # latin1 or latin2.
+        return "INVALID-UNICODE";
+    }
+
+    __PACKAGE__->setup;
+}
+use Catalyst::Test 'TestApp';
+
+{
+    my $res = request('/ok');
+    is ($res->content, "ok\n", "app is echoing arguments");
+}
+{
+    my $res = request('/%E2%C3%83%C6%92%C3%8');
+    is ($res->content, "INVALID-UNICODE\n",
+        "replacement ok in arguments");
+}
+{
+    my $res = request('/p?valid_key=%e2');
+    is ($res->content, "p\nQ valid_key => INVALID-UNICODE\n",
+        "replacement ok in query");
+}
+{
+    my $res = request('/p?%e2=%e2');
+    is ($res->content, "p\nQ INVALID-UNICODE => INVALID-UNICODE\n",
+        "replacement ok in query");
+}
+{
+    my $req = POST "/p", Content => "%e2=%e2";
+    my $res = request($req);
+    is ($res->content, "p\nB INVALID-UNICODE => INVALID-UNICODE\n", "replacement ok in body");
+}
+{
+    my $req = POST "/p", Content => "valid_key=%e2";
+    my $res = request($req);
+    is ($res->content, "p\nB valid_key => INVALID-UNICODE\n", "replacement ok in body");
+}
+{
+    # and a superset of problems:
+    my $req = POST "/%e5?%e3=%e3", Content => "%e4=%e4";
+    my $res = request($req);
+    my $expected = <<'BODY';
+INVALID-UNICODE
+Q INVALID-UNICODE => INVALID-UNICODE
+B INVALID-UNICODE => INVALID-UNICODE
+BODY
+    is ($res->content, $expected, "Found the replacement strings everywhere");
+}
+
+
+done_testing;
+
+#TestApp->to_app;