Advertisement
DRVTiny

somestub

Jul 7th, 2017
318
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Perl 9.03 KB | None | 0 0
  1. #!/usr/bin/perl -l
  2. use strict;
  3. use utf8;
  4. use 5.16.1;
  5. use constant {
  6.     EAGAIN => 11,
  7.     TCP_KEEPIDLE => 4,
  8.     TCP_KEEPINTVL => 5,
  9.     TCP_KEEPCNT => 6,
  10.     TEMP_BUF_SIZE => 4096,
  11.     CRLF => "\r\n",
  12.     GZIP_MAGIC => "\x1f\x8b",
  13.     GZIP_MAGIC_LENGTH => 2,
  14.     NO_FLAGS => 0,
  15.     LISTEN_QUEUE_SIZE => 3,
  16.     LISTEN_ON_PORT => 1212,
  17.     YES => 1,
  18.     NO => undef
  19. };
  20. my $LOG_FILE = '/tmp/stubCry.log';
  21.  
  22. use IO::Socket::INET;
  23. use Socket qw(IPPROTO_TCP);
  24. use IO::Uncompress::Gunzip qw(gunzip);
  25. use IO::Compress::Gzip;
  26. use EPW;
  27. use JSON::XS qw(encode_json decode_json);
  28.  
  29. my ($S, $fhLog);
  30. my %clientBuf;
  31.  
  32. my %sn2Rate=(
  33.     1   =>  10,
  34.     2   =>  25,
  35.     3   =>  50
  36. );
  37.  
  38. sub logg
  39. {
  40.         printf $fhLog '%s '.$_[0].(substr($_[0],-1) eq "\n"?'':"\n"), scalar(localtime()), @_[1..$#_];
  41. }
  42.  
  43. sub newClient
  44. {
  45.     my $c = $S->accept();
  46.     $c->blocking(0);
  47.     return unless $c;    
  48.     logg '+client #%s %s', fileno($c), inet_ntoa((sockaddr_in $c->peername())[1]);
  49. #    send $c, CRLF, NO_FLAGS;   #only small message
  50.     $c->setsockopt(IPPROTO_TCP, TCP_KEEPIDLE, 100);
  51.     $c->setsockopt(IPPROTO_TCP, TCP_KEEPINTVL, 30);
  52.     $c->setsockopt(IPPROTO_TCP, TCP_KEEPCNT,    5);
  53.     $c->setsockopt(SOL_SOCKET,  SO_KEEPALIVE,   1);
  54.     $clientBuf{$c} = {
  55.         'jsonHndl' => JSON::XS->new(),
  56.         'replyBuf' => '',
  57.         'flGzip'   => undef,
  58.         'reader'   => \&readPlainData,
  59.         'writer'   => \&writePlainData,
  60.         'nDataPortion' => 0,
  61.     };
  62.     EPW::setFDH($c, \&processClientReq, undef, \&dropClient, undef);
  63. }
  64.  
  65. =doc
  66.     Syntax:
  67.         processObjs(client_connection_handle, [object1, object2..objectN])
  68. =cut
  69. sub processObjs {
  70.         my $c=shift;
  71.         return unless $_=$_[0] and ref eq 'ARRAY' and @{$_};
  72.         for my $obj (@{$_}) {
  73.             eval { process($c, $obj) || 1 } or do {
  74.                 chomp(my $msg=sprintf('fail to process object <<%s>>. reason: %s', encode_json($obj), $@));
  75.                 logg($msg=~s/\n/\\n/gr);
  76.                 dropClient($c);
  77.                 return
  78.             }
  79.         }
  80.         return 1
  81. }
  82.  
  83. sub processClientReq
  84. {
  85.     my ($c)=@_;
  86.     return unless defined(my $client=$clientBuf{$c});
  87.     my $flFirstBytes = $client->{'nDataPortion'}++ == 0;
  88.     # If client buffer is empty, then we need to get only first GZIP_MAGIC_LENGTH (i.e. 2) bytes of stream to determine, whether data was compressed
  89.     my ($foodSize, $err)=$client->{'reader'}->($c, \my $foodCont, $flFirstBytes?GZIP_MAGIC_LENGTH:TEMP_BUF_SIZE);
  90.     if (defined $foodSize) {
  91.         unless ($foodSize) {
  92.             if (length $client->{'replyBuf'}) {
  93.             # We have anything to send back to the client
  94.                 EPW::setFDH($c, undef, \&sendReply, \&dropClient, undef)
  95.             } else {
  96.             # Get rid of this client: we tell her all she want to know
  97.                 dropClient($c)
  98.             }
  99.             return
  100.         } elsif ($flFirstBytes and $foodSize==GZIP_MAGIC_LENGTH and $foodCont eq GZIP_MAGIC) {
  101.             logg '<c#%s[*] compressed stream detected, we will use uncompressing on the fly', fileno($c);
  102.             my $zI=IO::Uncompress::Gunzip->new($c, 'Prime' => $foodCont);
  103.             $client->{'flGzip'}=1;
  104.             ($foodSize, $err)=(
  105.                 $client->{'reader'} = sub { $_ = $zI->read(${$_[1]}, $_[2]); return ($_, $!) }
  106.             )->(undef, \$foodCont, TEMP_BUF_SIZE);
  107.             my $zO=IO::Compress::Gzip->new($c, 'TextFlag'=>1) or die "IO::Compress::Gzip failed: ".$IO::Compress::Gzip::GzipError." \n";
  108.             $zO->autoflush(1);
  109.             $client->{'writer'} = sub { print STDERR $_[1]; $zO->print($_[1]); length($_[1]) };
  110.         }
  111.     }
  112.    
  113.     unless (defined $foodSize) {
  114.         dropClient($c) unless $err == EAGAIN;
  115.         return
  116.     }
  117.    
  118.     if (my @objs=eval { $client->{'jsonHndl'}->incr_parse($foodCont) }) {
  119.         processObjs($c, \@objs)
  120.     } elsif ($@) {
  121.         chomp(my $msg="breaking-all data portion from client received: << $foodCont >>. JSON parser say: $@");
  122.         logg($msg=~s%\n%\\n%gr);
  123.         dropClient($c);
  124.         return
  125.     }
  126. }
  127.  
  128. sub sendReply
  129. {
  130.     my ($c)=@_;
  131.     logg('[err] cant sendReply to unknown client'), return unless my $client=$clientBuf{$c};
  132.     my $nBytesSended = $client->{'writer'}->($c, $client->{'replyBuf'}, NO_FLAGS);
  133.     if (length($client->{'replyBuf'}) == $nBytesSended) {
  134.         undef $client->{'replyBuf'};
  135.         EPW::setFDH($c, \&processClientReq, undef, \&dropClient, undef)
  136.     } else {
  137.         substr($client->{'replyBuf'}, 0, $nBytesSended-1) = ''
  138.     }
  139. }
  140.  
  141. sub reply
  142. {
  143.    my ($c,$obj)=@_;
  144.    logg('[err] cant reply to unknown client'), return unless my $client=$clientBuf{$c};
  145.    my $dt = encode_json($obj);
  146.    logg '>c#%s[%s]: %s', fileno($c), $obj->{'ans'}, $dt;
  147.    $dt .= CRLF;
  148.    if (length $client->{'replyBuf'}) {
  149.         $clientBuf{$c}{'replyBuf'} .= $dt
  150.    } else {
  151.         my $nBytesSended = $client->{'writer'}->($c, $dt, NO_FLAGS);
  152.         return 1 if $nBytesSended==length($dt);
  153.         # ?? WDIM ->
  154. #            $clientBuf{$c}{'replyBuf'} = $dt;
  155.         # remaining part we need to send:
  156.         $clientBuf{$c}{'replyBuf'} = substr($dt, $nBytesSended);
  157.         # <-
  158.         EPW::setFDH($c, \&processClientReq, \&sendReply, \&dropClient, undef)
  159.    }
  160.    return 1
  161. }
  162.  
  163. my %hndlByReqType=(
  164.     'verify'    => sub {
  165.          verify($_[0] => $_[1]{'code'})
  166.     },
  167.     'prepare'   => \&calc,
  168.     'close' => \&fin,
  169.     'closed'    => sub {
  170.         dummyClosed($_[0] => $_[1]{'id'})
  171.     },
  172.     'default'   => sub {
  173.         die "wrong type $_[1]{'req'}"
  174.     },
  175. );
  176.  
  177. sub process
  178. {
  179.     my ($c, $obj)=@_;
  180.     my $req = $obj->{'req'};
  181.     logg "<c#".fileno($c)."[$req]: ".encode_json($obj);
  182.     ($hndlByReqType{$req || 'default'} || $hndlByReqType{'default'})->($c, $obj);
  183. }
  184.  
  185. sub verify
  186. {
  187.     my ($c, $ccode)=@_;
  188.     reply( $c =>
  189.             $ccode=~/^cl(?:b|\d\d)\d{7,}/
  190.             ? do {
  191.                 my %opt = verify_onFile($ccode);
  192.                 +{
  193.                     'ans'=>'coupon',
  194.                     'state'=>((!%opt || $opt{'closed'})?'in':'').'valid',
  195.                     'code'=>$ccode
  196.                 }
  197.               }
  198.             : {'ans'=>'unknown', 'code'=>$ccode}
  199.     );
  200. }
  201.  
  202. sub fin
  203. {
  204.     my ($c, $obj)=@_;
  205.    
  206.     for my $cp (@{$obj->{'coupons'}})
  207.     {
  208.         my %opt=verify_onFile($cp);
  209.         close_onFile($cp) if $sn2Rate{$opt{'id'}}
  210.     }
  211.    
  212.     reply($c => {
  213.                     'ans'=>'close',
  214.                     'id'=>$obj->{'id'}
  215.                 }
  216.     )
  217. }
  218.  
  219. sub dummyClosed
  220. {
  221.     my($c, $id)=@_;
  222.     reply($c => {
  223.                     'ans'=>'closed',
  224.                     'id'=>$id
  225.                 }
  226.     )
  227. }
  228.  
  229. sub calc
  230. {
  231.     my ($c, $obj)=@_;
  232.     my @items = grep {!$_->{'exclude'}} @{$obj->{'items'}};
  233.     my @discs =
  234.         @items
  235.             ? map {
  236.                 my $coupon = $_;
  237.                 my %opt = verify_onFile($coupon);
  238.                 unless (my $rate = $sn2Rate{$opt{'id'}}) {
  239.                     ()
  240.                 } else {
  241.                     map {
  242.                         'num'       => $_->{'num'},
  243.                         'discount'  => $_->{'discount'} + $_->{'cost'} * $_->{'amount'} * $rate * 0.01,
  244.                         'code'      => $opt{'aId'},
  245.                     }, @items
  246.                 }
  247.               } @{$obj->{'coupons'}}
  248.             : ();
  249.     reply($c => {
  250.                     'ans'=>'discount',
  251.                     'id'=>$obj->{'id'},
  252.                     'discount'=>\@discs
  253.                 }
  254.     )
  255. }
  256.  
  257. sub dropClient
  258. {
  259.     my $c=shift;
  260.     delete $clientBuf{$c};
  261.     logg "-client #". fileno($c);
  262.     EPW::clearFDH($c);
  263.     shutdown($c,2);
  264.     close $c
  265. }
  266.  
  267. sub verify_onFile
  268. {
  269.     my ($code)=@_;
  270.     $code=~/^cl(b|\d\d)(\d\d\d)(\d\d\d)(\d+)/ or return ();
  271.  
  272.     my $iCode="$1-$2-$3-$4";
  273.     open my $F, '<', "cps/$iCode.data" or return ();
  274.     my %opt;
  275.     while(<$F>)
  276.     {
  277.         $opt{$1}=$2     if /^\s*(id|weight|closed|aId)=(.*)/;
  278.         last        if /\@Coupon\[registered\]:.End$/i;
  279.     }
  280.     return %opt;
  281. }
  282.  
  283. sub close_onFile
  284. {
  285.     my ($code)=@_;
  286.     $code =~ /^cl(b|\d\d)(\d\d\d)(\d\d\d)(\d+)/ or logg ("close <$code> failed"), return;
  287.     my $iCode="$1-$2-$3-$4";
  288.     logg "coupon $iCode closed";
  289.     my $F;
  290.     open $F,'>>',"cps/$iCode.data" or logg("close-open <$iCode> failed: $!"), return;
  291.     print $F "closed=1";
  292.     close $F;
  293. }
  294.  
  295. sub readPlainData {
  296.     $_ = sysread($_[0], ${$_[1]}, $_[2]);
  297.     return ($_, $!)
  298. }
  299.  
  300. sub writePlainData {
  301.     logg('[err] client writer wont to write nothing'), return
  302.         unless defined $_[1];
  303.     send($_[0], $_[1], $_[2] // NO_FLAGS)
  304. }
  305.  
  306. sub main {
  307.     $fhLog=do { open $_, '>>', $LOG_FILE or die 'Cant open log file. No log == No work!'; $_ };
  308.     # ?? WDIM. Why this strange hack stays undocumented?
  309.     select+((select $fhLog),$|=1)[0];
  310.  
  311.     $S=IO::Socket::INET->new(
  312.         'Listen'    =>  LISTEN_QUEUE_SIZE,
  313.         'ReuseAddr' =>  YES,
  314.         'LocalPort' =>  LISTEN_ON_PORT
  315.     ) or die "bindErr: $!";
  316.  
  317.     EPW::setFDH($S, \&newClient, undef, undef);
  318.     EPW::loo();
  319. }
  320.  
  321. main();
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement