zzzcpan / nginx-perl

Full-featured perl support for nginx

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

how to use ngx_connector/writer/reader?

chenryn opened this issue · comments

I wrote a test code as follow:
sub handler {
ngx_connector '127.0.0.1', 6379, 10, sub {
return NGX_CLOSE if $!;
my $c = shift;
my $req_buf = "GET /";
my $res_buf;
ngx_writer $c, $req_buf, 10, sub {
return NGX_CLOSE if $!;
warn time() . "write\n";
$req_buf = '';
return NGX_READ;
};
ngx_reader $c, $res_buf, 0, 0, 1, sub {
warn time() . "read\n";
warn $!;
return NGX_CLOSE if $! && $! != NGX_EOF;
......

And then curl http://127.0.0.1:55555, the stderr show me:
nginx_perl start [OK] at /usr/local/nginx-perl/lib/HelloWorld.pm line 9.
1322732702write
1322732703read
Connection timed out at /usr/local/nginx-perl/lib/HelloWorld.pm line 50.

But the client curl do not close until I stop nginx-perl server.

How can I use those ngx_*r commands?

How can I use those ngx_*r commands?

There are two distinct APIs there: one is to talk to remote hosts,
which you are using and another one to respond to http clients. To
make a connection and send response you have to use both. It is
documented in HTTP REQUEST OBJECT and HTTP CONTENT HANDLER section,
well, kind of. It extends official nginx API, which can be found on
its website.

First, you have to increment request count. It keeps request alive and
allows you to finalize it whenever you want.

sub handler {
my $r = shift;
$r->main_count_inc;

Then you can make your connection, make sure to finalize current
request before NGX_CLOSE:

ngx_connector ... sub {
    if ($!) {
        $r->send_http_header('text/html');
        $r->print("Connection failed, $!\n");

        $r->send_special(NGX_HTTP_LAST);
        $r->finilize_request(NGX_OK);

        return NGX_CLOSE;
    }

    ngx_writer ...

    ngx_reader $c, $buf, ...  sub {
        if ($!) {
            ...
        }

        $r->send_http_header('text/html');
        $r->print("$buf\n");

        $r->send_special(NGX_HTTP_LAST);
        $r->finilize_request(NGX_OK);

        return NGX_CLOSE;
    }

    ...
}

As you can see it's getting uglier and harder to read. Declaring separate
subroutine should be much better.

Take a look into Util.pm for simple example how to do this:
https://github.com/zzzcpan/nginx-perl/blob/master/src/http/modules/perl/Util.pm#L42

Thank you~But I can't find finilize_request in the code, and when I run the nginx, debug log show as follow:
Can't locate object method "finilize_request" via package "Nginx" at /usr/local/nginx-perl/lib/HelloWorld.pm line 65.

Right, sorry, typo.
It should be called $r->finalize_request(); with "a" instead of "i";

Still ’Connect timeout‘ in ngx_reader. I'm learning your Util.pm now. Can you explain '&$cb(), return NGX_CLOSE if $!' ?I don't know this way of writing like "function comma symbol return"... Thanks a lot.

my $req_buf = "GET /";

I think your timeout is related to your request being incomplete, i.e. server still waiting for more data to come.
You are probably just missing CRLF if it's redis you are connecting to:
"GET /\x0d\x0a"

There is actually a working redis example in eg/redis, check it out.

'&$cb(), return NGX_CLOSE if $!"

It's equivalent to:

if ($!) {
    &$cb();
    return NGX_CLOSE;
}

Why do &$cb() function if $!?
And I add CRLF to my $req_buf. However, when I wrote 'warn "$req_buf\n$res_buf\n";' before or in ngx_writer, the client can receive correctly;but delete the warn, server is killed by SIGSEGV after sendto and epoll_wait.
......
So sorry for trouble you so long time with the simple question~

Why do &$cb() function if $!?

We need to callback on error or caller won't be able to finalize the request.

Post your entire code here, it's hard to guess what's going on without it.

package HelloWorld;
use Nginx;

sub handler {
my $r = shift;
$r->main_count_inc;

ngx_connector '127.0.0.1', 80, 10, sub {
    if ($!) {
        $r->send_http_header('text/html');
        $r->print("Connection failed, $!\n");
        $r->send_special(NGX_HTTP_LAST);
        $r->finalize_request(NGX_OK);
        return NGX_CLOSE;
    };
    my $c = shift;
    my $req_buf = "GET /index.php HTTP/1.0\x0d\x0a".
          "Host: chenlinux.com\x0d\x0a".
          "Connection: close\x0d\x0a".
          "\x0d\x0a";
    my $res_buf;

    ngx_writer $c, $req_buf, 10, sub {
        if ($!) {
            $r->send_http_header('text/html');
            $r->print("Connection failed, $!\n");
            $r->send_special(NGX_HTTP_LAST);
            $r->finalize_request(NGX_OK);
            return NGX_CLOSE;
        };
        $req_buf = '';

#warn "$req_buf\n$res_buf\n";
return NGX_READ;
};

    ngx_reader $c, $res_buf, 0, 4000, 10, sub {
        if ($!) {
            $r->send_http_header('text/html');
            $r->print("Connection failed, $!\n");
            $r->send_special(NGX_HTTP_LAST);
            $r->finalize_request(NGX_OK);
            return NGX_CLOSE;
        }
        $r->send_http_header('text/html');
        $r->print($res_buf);
        $r->send_special(NGX_HTTP_LAST);
        $r->finalize_request(NGX_OK);
        return NGX_DONE;
    };

    return NGX_WRITE;
};

};

1;

Thanks.
So, there are 3 problems, one of them is actually mine and got fixed just now.

  1. Uninitialized read buffer caused segfault. Just use my $res_buf = ''; as a workaround or get latest nginx-perl.

        my $req_buf = "GET /index.php HTTP/1.0\x0d\x0a".
              "Host: chenlinux.com\x0d\x0a".
              "Connection: close\x0d\x0a".
              "\x0d\x0a";
        my $res_buf = '';
    
        ngx_writer $c, $req_buf, 10, sub {
    
  2. You are returning wrong constant from the reader, should be return NGX_CLOSE, not NGX_DONE, NGX_DONE is defined for http api only.

  3. And you have to return NGX_DONE from http handler.

            $r->send_special(NGX_HTTP_LAST);
            $r->finalize_request(NGX_OK);
            return NGX_CLOSE;
        };
    
        return NGX_WRITE;
    };
    

    return NGX_DONE;
    };

We should have used gist, this thing breaks the formatting.