Selenium teszt a böngészőből

Először is letöltjük a Selenium IDE-t a https://www.selenium.dev/ webcímről. Ez egy ipari standard a webalkalmazások automatizált teszteléséhez. Van robusztus megoldása is, a Selenium WebDriver, illetve elosztott teszteléshez a Selenium Grid. Nekünk bőven megfelel egy Chrome plugin a fejlesztő Debian 10.7 Linux gépemre – ahol Chrome-ot használok. Ez a plugin Selenium IDE része. Segítségével elég egyszerűen megnyomhatjuk a “like” gombot mondjuk ezerszer. 🙂

Miután beinstalláltuk a Chrome plugin-t (bővítményt), felvesszük a teszt esetet mintegy makróként, majd lejátszuk. Látható, hogy növekszik a like-ok száma. Lássuk a record/play képernyőrészleteket!.

Ennyi dióhéjban a webalkalmazások tesztelése. Tetszőleges teszt-utakat, teszt-eseteket vehetünk fel egy bonyolultabb weblap esetén, ahogy itt ezt egyszerűen “click”-nek neveztük a “mylike” nevű tesztünkben. Innentől ezt a scriptet bejátszhatjuk a robusztus script-futtató Selenium alrendszerekbe is akár. 🙂

A futtatást nézve egy másodperc alatt lefut a teszt oldal újratöltéssel együtt – persze ez nem mérvadó adat. Többet a skálázhatóságról, azaz a teljesítményről csak akkor tudhtunk meg, ha automatizáljuk pl. Selenium JavaScript motorral a végrehajtást.

A Selenium JavaScript kliens és WebDriver az alábbi oldalakról tölthető le:

https://www.selenium.dev/downloads/

https://www.npmjs.com/package/selenium-webdriver/v/4.0.0-alpha.8

Egyelőre többet erről nem árulok el, mert elvenném a tesztelők kenyerét. Annyit azonban elmondhatok programozóként, hogy egyszerű JavaScript-ben automatizálható a tesztelési folyamat, sőt: terheléses tesztelés is könnyen elvégezhető.

Teszt-világban kétféle filozófia létezik amúgy: ellenséges tesztelő (és coder) illetve a barátságos. Én ez utóbbit támogatom. Együttműködve jobb eredményt érhet el a fejlesztő és a teszt csapat. 🙂

Biztos ami biztos, ráeresztettem a https://wlammpp.wordpress.com/2020/08/14/meres-cgi-bash-perl-c-fastcgi-vagy-mod_perl-melyik-a-gyorsabb/ fejezetben írt parancssori script-et 1000x, hogy mennyi idő alatt fut le a mod_perl “like” alkalmazásunk tesztüzemben. Íme az eredmény.

gvamosi@gergo1:~/Documents/http_meres$ l
 total 4
 -rwxr-xr-x 1 gvamosi gvamosi 196 Aug 14 04:31 1000times_curl.sh
gvamosi@gergo1:~/Documents/http_meres$ ./1000times_curl.sh http://localhost/perl/mylike-dbi.cgi
 1610127040.356192334
 1610127051.815938599
 11.459746265

11,5 másodperc – ez adatbázis eléréssel 1000 lekérésre nem rossz teljesítmény! Nos, Mr. Zuckerberg, érdeklődik az ultragyors like-megoldásom iránt? 🙂

A “like” gomb működése és a cookie-k

Nézzük a két részletet, amelyek az előző részből kimaradtak. Az első rész a számláló növeléséért felelős. Ezt egy sima adatbázis UPDATE végzi a DBI interfészen keresztül. Fontos, hogy a finish() metódussal felszabadítsuk az utasítást, különben “lógna” a levegőben azaz a memóriában. A cookie-zás (sütizés) az egy kliensből történő többszöri like-olás megakadályozását szolgálja és az üzenetfejlécben állítjuk illetve kérdezzük le, mégis teszt célokból jelen kódban ki van “ütve”.

 my $cookie = $r->headers_in->get('Cookie');
 my $uri = URI::Encode->new( { encode_reserved => 1 } );
 my $uri_encoded_referer = $uri->encode($http_referer);
 #print $uri_encoded_referer;
 my $wasliked = $cookie ne '' && $cookie =~ /$uri_encoded_referer/;
 if ($like && ! $wasliked ) {
  # increment counter
  my $sthupd = $dbh->prepare('UPDATE mylike SET counter = counter + 1 WHERE approved = 1 and referer = ?');
  $sthupd->execute($http_referer);
  $sthupd->finish();

  my $cookie = CGI::Cookie->new(-name  => 'mylike'.$http_referer,
                                -value => 'SET');
  $r->headers_out->set('Set-Cookie' => $cookie);
  $wasliked = 1;

  # !! TEST ONLY !! temporary turn off cookie-mechanism !! COMMMENT OUT FOLLOWING TWO LINES FOR USE !!
  $r->headers_out->set('Set-Cookie' => '');
  $wasliked = 0;
 }

A like változó értékadása az előző részben szerepel, a GET metódus révén átadott paraméterként kerül kiértékelésre. Az alábbi gomb onclick javascript eseményéből kerül meghívásra like=1 URI változóként.

my $req = CGI->new($r);
my $like = $req->param('like');

A második kimaradt részecske maga a “like” gomb megjelenítéséért felelős. Ha már like-oltuk az oldalt, akkor disabled a gomb, azaz nem lehet többször megnyomni. 🙂

 print "<input type=\"button\" onclick=\"\$('#mylike').load('http://localhost/perl/mylike-dbi.cgi?like=1');\" value=\"like\" ".($wasliked?'disabled ':'')."/>\n";

És akkor nézzük meg a kimenetet!

Ennyi! That’s all! 🙂

Megjegyzés: azért a felhasznált javascript miatt ügyelni kell arra, hogy a site-unk include-olja script forrásként a jquery lib-eket.

Az utolsó részben megnézzük, hogyan lehet letesztelni illetve a teherbírását megmérni a like modulunknak.

A like-számlálójának működése, a REFERER a HTTP fejlécből

Minden további leírás helyett ideollózom a mylike-dbi.cgi azon részeit, amelyek a like-ok számának megjelenítéséért felelősek, illetve amelyek az első bejegyzés létrehozására alkalmasak.

#!/usr/bin/perl

use strict;
use warnings;
use DBI;
use Apache2::RequestRec ();
use Apache2::RequestUtil ();
use CGI;
use CGI::Cookie;
use URI::Encode;


my $r = Apache2::RequestUtil->request;

my $req = CGI->new($r);
my $like = $req->param('like');
#print $like;
my $http_referer = $ENV{'HTTP_REFERER'};

my $dbh = DBI->connect('DBI:mysql:mylike', 'mylike', 'mylike1'
                   ) || die "Could not connect to database: $DBI::errstr";

if ($http_referer) {

 # LIKE BUTTON CODE WAS HERE ##################

 my $sth = $dbh->prepare('SELECT id FROM mylike WHERE approved = 1 and referer = ?');
 $sth->execute($http_referer);
 my @result = $sth->fetchrow_array();
 my $len = @result;
 #print "numof result $len\n";
 $sth->finish();

 if ($len == 0) {
  # insert data into the links table
  my $sql = "INSERT INTO mylike (referer) VALUES(?)";

  my $stmt = $dbh->prepare($sql);

  # execute the query
  if($stmt->execute($http_referer)) {
   # ok
   #print "Mylike $http_referer inserted successfully";
  }

  $stmt->finish();
 }

 $sth = $dbh->prepare('SELECT counter FROM mylike WHERE approved = 1 and referer = ?');
 $sth->execute($http_referer);
 @result = $sth->fetchrow_array();

 print "$result[0] like(s)<br>\n";

 # LIKE BUTTON CODE WAS HERE ##################

 $sth->finish();
 
} else {
 # test

 my $results = $dbh->selectall_hashref('SELECT * FROM mylike', 'id');
 foreach my $id (keys %$results) {
  print "!!TEST!! ID $id: referer is $results->{$id}->{'referer'}, counter is $results->{$id}->{'counter'}\n";
 }

}

$dbh->disconnect();

A kód magyarázata.

Kivesszük a fejlécből a HTTP_REFERER-t. Ha ez definiált, akkor “éles” működés, ha nem akkor tesztüzemmód. Tesztüzemben az alábbi kimenetet kapjuk.

Látható, hogy két REFERER szerepel az adatbázisunkban. Ezeket sorolja fel tesztüzemmódban a script.

Éles üzemnél megvizsgálja a programunk, hogy létezik-e adatbázis-bejegyzés adott REFERER-rel. Ha még nincs, létrehoz egyet 0-ás like-oltsággal. 🙂 Ezután egy sima print utasítással kijelzi a kedvelések számát.

A következő részben megnézhetjük magának a “like” gombnak is a működését és a mögötte álló programkódot is. Ebben a forrásban ezt a

# LIKE BUTTON CODE WAS HERE ##################

részek jelölik.

A SELECT-jeinkben az approved = 1 az adminisztrátori funkció további lehetőségét hordozza magában. Tehát le lehet “tiltani” egy-egy beágyazást.

Látható a kód elején a use kulcsszavaknál, hogy használunk egynéhány modult. 🙂 Szinte gyerekjáték a Perl programozás.

Megjegyzem, hogy ez a like megvalósítás különböző webcímű (URI) weboldalanként ad lehetőséget like-olásra. Egy webcímen belül több like nem lehet. Nem a tökély a cél, csupán a megvalósíthatóság bemutatása.

A Hello World! mod_perl DBI alkalmazás

A /etc/apache2/conf-enabled/mod_perl.conf file-t az alábbiak szerint módosítottuk.

PerlModule ModPerl::Registry

Alias /perl /var/www/perl
<Directory /var/www/perl>
   AddHandler perl-script .cgi .pl
   PerlResponseHandler ModPerl::Registry
   Options +ExecCGI
</Directory>

Lehet saját handler-t is írni, de most enm tértünk ki erre a lehetőségre. 🙂

Nézzük a hello-world-dbi.cgi programocskánkat majd a beágyazását az apartman “Képek” oldalába.

#!/usr/bin/perl

use strict;
use warnings;
use DBI;
use Apache2::RequestRec ();
use Apache2::RequestUtil ();

my $r = Apache2::RequestUtil->request;

print "The referer in your HTTP-Header is: ", $ENV{'HTTP_REFERER'}, "\n";

my $dbh = DBI->connect('DBI:mysql:mylike', 'mylike', 'mylike1'
               ) || die "Could not connect to database: $DBI::errstr";

print "<br>\n";

my $results = $dbh->selectall_hashref('SELECT * FROM mylike', 'id');
foreach my $id (keys %$results) {
 print "Value of ID $id is $results->{$id}->{'referer'}\n";
}

$dbh->disconnect();

Használjuk a DBI és az új Apache2-es Request modulokat, majd lekérdezzük a HTTP_REFERER-t illetve egy select-tel belefetchelünk az adatbázisba. 🙂

Figyelem! Nagyon fontos a helyes beállítás az apache config oldalon.

A kis appunkat beágyazzuk mint widget-et az alábbi módon.

<div id="mylike"></div>
<script>$("#mylike").load("http://localhost/perl/hello-world-dbi.cgi</script>

És akkor lássuk az eredményt.

MySQL adatbázis és a Perl DBI és DBD::mysql modulok

Az adatbázis egyszerűen egyetlen egy táblából áll. Nézzük az SQL DDL kódot!

CREATE TABLE mylike (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
referer VARCHAR(200) NOT NULL,
counter DECIMAL(9) NOT NULL DEFAULT 0,
approved TINYINT NOT NULL DEFAULT 1,
created TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

Pár különleges megjelölést használunk a séma létrehozásakor. Alapértékeket állítunk be a DEFAULT kódszóval, illetve AUTO_INCREMENT-tet használunk, tehát automatikus id generálást, sőt: UPDATE esetén az updated mező értéke az aktuális időpőponttal automatikusan frissül!

Parancssorból a mysql paranccsal “játszhatjuk be” az új adatbázisba új felhasználóval a DDL kódrészletet. Az adatbázis és a felhasználó létrehozása az alábbi módon történik, root-kéni futtatva a mysql parancsokat. Sima copy’n’paste. 🙂

CREATE DATABASE mylike CHARACTER SET utf8 COLLATE utf8_general_ci;
CREATE USER 'mylike'@'localhost' IDENTIFIED BY 'mylike1';
GRANT ALL PRIVILEGES ON mylike.* TO 'mylike'@'localhost';
FLUSH PRIVILEGES;

Szükség esetén a “use mylike” paranccsal jelöljük ki a használt adatbázist, amikor a táblát hozzuk létre. 🙂

Teszteljük le a létrehozott adatbázis táblánkat!

mysql> insert into mylike (referer) values ('http://localhost/valami');
Query OK, 1 row affected (0.01 sec)
mysql> select * from mylike;
+----+-------------------------+---------+----------+---------------------+---------------------+
| id | referer | counter | approved | created | updated |
+----+-------------------------+---------+----------+---------------------+---------------------+
| 1 | http://localhost/valami | 0 | 1 | 2021-01-06 15:00:35 | 2021-01-06 15:00:35 |
+----+-------------------------+---------+----------+---------------------+---------------------+
1 row in set (0.01 sec)
mysql>

A Perl megfelelő moduljait az adatbázis-eléréshez installálni szükséges. Ezek a Perl Database Interface illetve a megfelelő Perl DBI Database Driver (https://metacpan.org/pod/DBI::DBD) Ezt a Debian 10.7-ünk alatt az alábbi módon tehetjük meg.

root@gergo1:~# apt-get install libdbi-perl libdbd-mysql-perl
Reading package lists… Done
Building dependency tree
Reading state information… Done
The following packages were automatically installed and are no longer required:
libappindicator3-1 libindicator3-7
Use 'apt autoremove' to remove them.
Suggested packages:
libclone-perl libmldbm-perl libnet-daemon-perl libsql-statement-perl
The following NEW packages will be installed:
libdbd-mysql-perl libdbi-perl
0 upgraded, 2 newly installed, 0 to remove and 4 not upgraded.
Need to get 896 kB of archives.
After this operation, 2,464 kB of additional disk space will be used.
Get:1 http://httpredir.debian.org/debian buster/main amd64 libdbi-perl amd64 1.642-1+deb10u1 [775 kB]
Get:2 http://httpredir.debian.org/debian buster/main amd64 libdbd-mysql-perl amd64 4.050-2 [121 kB]
Fetched 896 kB in 1s (1,535 kB/s)
Selecting previously unselected package libdbi-perl:amd64.
(Reading database … 533195 files and directories currently installed.)
Preparing to unpack …/libdbi-perl_1.642-1+deb10u1_amd64.deb …
Unpacking libdbi-perl:amd64 (1.642-1+deb10u1) …
Selecting previously unselected package libdbd-mysql-perl:amd64.
Preparing to unpack …/libdbd-mysql-perl_4.050-2_amd64.deb …
Unpacking libdbd-mysql-perl:amd64 (4.050-2) …
Setting up libdbi-perl:amd64 (1.642-1+deb10u1) …
Setting up libdbd-mysql-perl:amd64 (4.050-2) …
Processing triggers for man-db (2.8.5-2) …
root@gergo1:~#

Megjegyzés: installálhatjuk a szükséges modulokat a Perl CPAN modulkezelőjén belül is a cpan paranccsal a konzolról, de akkor elveszítjuk a rendszerhez szállított előrecsomagolt szoftverek előnyeit. Különleges csomagokhoz valóban hasznos kiegészítő lehet a Perl CPAN modulja (https://www.cpan.org/).

A következő bejegyzésben megnézzük hogyan lehet mod_perl-ben adatbázist elérni web-ről egy Perl Hello World! kóddal. 🙂

A “like” alkalmazás előtt

Mit is kell tudjon egy tetszőleges oldalba beágyazható like alkalmazás? Tulajdonképpen esetünkben arról van szó, hogy egy tetszőleges oldalon a weblapról egy tetszésnyilvánítást rögzíthessünk illetve kijelezzük a tetszésnyilvánítások számát. Nyilvánvalóan adatbázisra lesz szükségünk a dinamikus működés biztosításához.

I. Először is két részre kell bontsuk a megvalósítást. Van egy számláló-kijelzés funkciónk, azaz hogy hány like volt eddig illetve egy “like” gomb, hogy a számláló értékét növelhessük. Továbbá beágyazhatóvá kell tennünk egy tetszőleges weboldalba a funkciót. Innentől like-widget-ről beszélhetünk (https://hu.wikipedia.org/wiki/Vez%C3%A9rl%C5%91elem).

II. Másodszor is hozzá kell kötnünk a like widget-ünket a megjelenés helyéhez. Ez a funkcionalitás a REFERER HTTP fejléc attribútum adatbázisban letárolásával és vizsgálatával oldható meg. Ez az attribútum a hivatkozott oldal címét tartalmazza (https://hu.wikipedia.org/wiki/Referer). Innen fogjuk tudni, hogy melyik a “hívó” webcím.

III. Harmadszor pedig meg kell vizsgálnunk, hogy a like funkciót felhasználóhoz vagy böngésző példányhoz kössük-e nyilvánvalóan azért, hogy ne lehessen egy személynek többször like-olnia ugyanazon a helyen. Ha felhasználóhoz kötjük, úgy a facebook stílusú megoldást kapjuk. Ez nekünk azonban túl bonyolult volna, így egyszerűen a böngészőben fogunk letárolni egy cookie-t, hogy az adott kliensen már történt egy like-olás azaz egy tetszésnyilvánítás adott webcímre.

A tervezési megfontolások nagyjából ennyit tesznek ki. Még foglalkozhatnánk a színes-szagos külcsínyről is, de azt most elhanyagoljuk. Egyszerűen egy félkövér szám lesz a like-ok száma, alatta pedig sortöréssel egy “like” feliratú gomb. Ha nem “szürke” azaz “disabled”, akkor adott kliens még nem like-olt, azaz lehet like-olni. Ennyi. 🙂

Mérés – CGI (bash/perl/C), FastCGI vagy mod_perl? Melyik a gyorsabb? :)

És akkor jöjjön az igazság órája! Nézzük meg, hogy az előző két fejezet kis webalkalmazásai közül melyek a leggyorsabbak. Természetesen egy komplex webalkalmazás mérése beszédesebb volna, de reményeim szerint 1000 mérésből a curl programcsomag segítségével egy egyszerű script segítségével is ki tudjuk deríteni, hogy mi a helyzet.

Kiindulás. A rendszer load-ja (leterheltsége) az utolsó 5 percben 1,19. Alacsony. Ez ahogy később kiderült, nem is válltozik. 🙂 És igen, a notebook 161 napja nem volt újraindítva. 🙂

gvamosi@gergo1:/var/www/perl$ uptime
 04:07:48 up 161 days,  5:02,  1 user,  load average: 1.68, 1.19, 1.02

Nézzük a mérést végző script-ünket!

#!/bin/bash

START=`date +%s.%N`
echo $START
for ((i=1;i<=1000;i++)); 
do 
	curl -s $1 2>&1> /dev/null
done
END=`date +%s.%N`
RUNTIME=$(echo "$END - $START" | /usr/bin/bc)
echo $END
echo $RUNTIME

Ez egy egyszerű script. Az időt méri, és a parancssorban kapott első paraméterre mint URL-re meghívja a curl parancs segítségével a webalkalmazásunkat.

MÉRÉS I. CGI, bash script. http://localhost/cgi-bin/counter.sh13,76 másodperc.

gvamosi@gergo1:~/Documents/http_meres$ ./1000times_curl.sh http://localhost/cgi-bin/counter.sh
1597372454.016087696
1597372467.780319906
13.764232210

MÉRÉS II. CGI, perl. http://localhost/cgi-bin/counter.pl16,75 másodperc.

gvamosi@gergo1:~/Documents/http_meres$ ./1000times_curl.sh http://localhost/cgi-bin/counter.pl
1597372699.883363458
1597372716.637874845
16.754511387

MÉRÉS III. CGI, natív C. http://localhost/cgi-bin/counter.cgi12,37 másodperc.

gvamosi@gergo1:~/Documents/http_meres$ ./1000times_curl.sh http://localhost/cgi-bin/counter.cgi
1597372832.246672103
1597372844.623081288
12.376409185

MÉRÉS IV. FastCGI, perl. http://localhost/cgi-bin/counter.pl.fcgi10,19 másodperc.

gvamosi@gergo1:~/Documents/http_meres$ ./1000times_curl.sh http://localhost/cgi-bin/counter.pl.fcgi
1597372942.444961716
1597372952.635298535
10.190336819

MÉRÉS V. mod_perl. http://localhost/perl/counter-mod_perl.cgi10,51 másodperc.

gvamosi@gergo1:~/Documents/http_meres$ ./1000times_curl.sh http://localhost/perl/counter-mod_perl.cgi
1597373028.237804238
1597373038.753424598
10.515620360

Látható, hogy bár igen egyszerű a webalkalmazás, csupán pár sor, a várható mérési eredményeket hozta. A leglassabb a sima perl CGI, gyorsabb a bash script illetve a natív C, míg a FastCGI illetve a mod_perl a leggyorsabbak. 🙂

És akkor egy (viszonylag régi) összehasonlító mérés a net-ről (https://stackoverflow.com/questions/382798/mod-perl-vs-mod-fastcgi). 🙂

cgi - 1200+ requests per minute
mod_perl - 6000+ requests per minute (ModPerl::PerlRun only)
fast_cgi - 6000+ requests per minute
mod_perl - 6000+ requests per minute (ModPerl::Registry)
servlets - 2438 requests per minute.

Megjegyzés: ez egy egyszerű tesztelési eljárás alapbeállításokkal (Apache) szerveroldalon, helyi gépen, 8GB memóriával, 4 magos processzorral és SSD háttértárral. Lehetne több szerverpéldányt indítani, párhuzamosítani, azaz egyszerre több kliensről tesztelni. Alapvetően idő és tárkorlátos minden számítástechnikai eljárás, szoftver, illetve program. Tehát CPU és memória/háttértár. A webes tesztekbe ezen kívül a hálózati sebesség is beleszámít(hat).

Egyszerű számláló FastCGI perl és mod_perl implementációban

A FastCGI abban különbözik a CGI-től, hogy amíg a CGI egy kérésre egy folyamatot hoz létre a szerveren, a FastCGI újbóli futásra képes, újbóli betöltődés nélkül, ezáltal több kérést tud kiszolgálni egységnyi idő alatt, tehát nagyobb teljesítményt lehet vele elérni. Részletek itt https://en.wikipedia.org/wiki/FastCGI.

Ha ilyet szeretnénk fejleszteni, először is installánunk kell az alapból nem telepített FastCGI Apache modul-t. Modul leírás itt https://httpd.apache.org/mod_fcgid/mod/mod_fcgid.html. Ez az alábbi módon lehetséges.

root@gergo1:~# apt-get install libapache2-mod-fcgid

Majd nézzük a /etc/apache2/mods-enabled/fcgid.conf configurációs file tartlamát.

<IfModule mod_fcgid.c>
  FcgidConnectTimeout 20

  <IfModule mod_mime.c>
    AddHandler fcgid-script .fcgi
  </IfModule>
</IfModule>

Ezután hozzuk létre az első, perl nyelvű counter.pl.fcgi programocskát a cgi-bin könyvtárunkban.

#!/usr/bin/perl

use FCGI;

my $count = 0;
my $request = FCGI::Request();

while($request->Accept() >= 0) {
    print("Content-type: text/html\r\n\r\n", ++$count);
}

Látható, hogy az FCGI kérés többször lefuthat. Ha ezt az oldalt pl. hétszer frissítjük böngészőben a http://localhost/cgi-bin/counter.pl.fcgi cím alatt, akkor egy 7-es számot fog kiírni. 🙂

Megjegyzem, ha nincs az FCGI modul telepítve a perl-en belül, akkor a cpan paranccsal tudjuk ezt megtenni. A CPAN a perl csomagkezelője https://www.cpan.org/.

Most nézzük a mod_perl implementációt is! Először is szükségünk lesz az Apache modulra.

root@gergo1:~# apt-get -y install libapache2-mod-perl

Ezután nézzük meg a /etc/apache2/conf-available/mod_perl.conf konfigurációs állományt.

# create new
# for example, set PerlRun mode under the "/var/www/perl"
PerlSwitches -w
PerlSwitches -T

Alias /perl /var/www/perl
<Directory /var/www/perl>
    AddHandler perl-script .cgi .pl
    PerlResponseHandler ModPerl::PerlRun
    PerlOptions +ParseHeaders
    Options +ExecCGI
</Directory>

<Location /perl-status>
    SetHandler perl-script
    PerlResponseHandler Apache2::Status
    Require ip 127.0.0.1 10.0.0.0/24
</Location>

Majd az “élesítő” parancsokat.

root@gergo1:~# a2enconf mod_perl
Enabling conf mod_perl.
To activate the new configuration, you need to run:
  systemctl reload apache2
root@gergo1:~# systemctl reload apache2

Végül írjuk meg a perl programunkat counter-mod_perl.cgi néven.

#!/usr/bin/perl

use strict;
use warnings;

print "Content-type: text/html\n\n";

my $a = 0;
&number($a);

sub number {
    my($a) = @_;
    $a++;
    print "number \$a = $a";
}

Ez a kis programocska a http://localhost/perl/counter-mod_perl.cgi cím alatt annyit ír ki, hogy number $a = 1. 🙂

A dokumentáció szerint (https://perl.apache.org/start/index.html) egy pici változtatással 100x-os sebességnövekedést érhetünk el a script-nél (https://perl.apache.org/start/tips/registry.html). Ezt meg is fogjuk mérni a következő fejezetben! 🙂

Alias /perl /var/www/perl
<Directory /var/www/perl>
    AddHandler perl-script .cgi .pl
    # comment out PerlRun mode and add Registry mode like follows
    #PerlResponseHandler ModPerl::PerlRun
    PerlResponseHandler ModPerl::Registry
    PerlOptions +ParseHeaders
    Options +ExecCGI
</Directory>

A mod_perl implementáció esetén az Apache szerverünk a kódot a memóriában tartja, és ott “fordítja”, ezért gyorsabb.

Egyszerű CGI számláló bash, perl és C nyelveken

Először is engedélyezzük a cgi modul-t az Apache webszerverünkben. Ez pár sor Debian 10.5 alatt root-ként (rendszergazdaként).

root@gergo1:/etc/apache2/mods-enabled# ln -s ../mods-available/cgi.load
root@gergo1:/etc/apache2/mods-enabled# ls -l
..
lrwxrwxrwx 1 root root 26 Aug 12 02:24 cgi.load -> ../mods-available/cgi.load
root@gergo1:/etc/apache2/mods-enabled# service apache2 reload

root@gergo1:/etc/apache2/conf-enabled# ls -l
..
lrwxrwxrwx 1 root root 36 Aug 28  2017 serve-cgi-bin.conf -> ../conf-available/serve-cgi-bin.conf

Nézzük a serve-cgi-bin.conf konfigurációs file tartalmát!

<IfModule mod_alias.c>
        <IfModule mod_cgi.c>
                Define ENABLE_USR_LIB_CGI_BIN
        </IfModule>

        <IfModule mod_cgid.c>
                Define ENABLE_USR_LIB_CGI_BIN
        </IfModule>

        <IfDefine ENABLE_USR_LIB_CGI_BIN>
                ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/
                <Directory "/usr/lib/cgi-bin">
                        AllowOverride None
                        Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
                        Require all granted
                </Directory>
        </IfDefine>
</IfModule>

Látható, hogy a /usr/lib/cgi-bin könyvtárban kell létrehozzuk a számlálóinkat bash, perl és C nyelveken. Nézzük az elsőt bash shell script nyelven!

#!/bin/bash

printf "Content-type: text/html\n\n"
let COUNTER++
printf "$COUNTER\n"

És akkor teszteljük le parancssorból, majd web-en!

gvamosi@gergo1:/usr/lib/cgi-bin$ chmod +x counter.sh 
gvamosi@gergo1:/usr/lib/cgi-bin$ ./counter.sh 
Content-type: text/html

1

Látható, hogy 1-et ír ki, ha parancssorban futtatjuk.

Ha megnézzük weben is a http://localhost/cgi-bin/counter.sh cím alatt, ugyanez az eredmény. Látható, hogy elég a Content-type beállítása a válasz fejlécében, majd kettő soremelés után bármit kiírathatunk. 🙂

A Content-type egyébként az ún. media type (régebben MIME type) értéket tartalmazza. Részletek: https://en.wikipedia.org/wiki/Media_type.

És akkor nézzük ugyanezt perl nyelven!

#!/usr/bin/perl

use strict;
use warnings;

print "Content-type: text/html\n\n";

my $a = 0;
&number();

sub number {
  $a++;
  print "number \$a = $a\n";
}

A parancs kimenete hasonlatos mind parancssorban, mind a http://localhost/cgi-bin/counter.pl webcím alatt.

gvamosi@gergo1:/usr/lib/cgi-bin$ chmod +x counter.pl 
gvamosi@gergo1:/usr/lib/cgi-bin$ ./counter.pl 
Content-type: text/html

number $a = 1

Végül nézzük meg C nyelven. Megjegyzem: a C nyelvű scriptet a gcc programcsomag segítségével le kell fordítanunk. 🙂

#include <stdio.h>

int i=0;

int incrementcount()
{

  i++;
  return i;
}

int main()
{
  printf("Content-type: text/html\n\n");
  printf("The current count is: ");
  printf("%d\n", incrementcount());
  return 0;
}

És akkor nézzük meg a fordítást majd a kimenetet. Ugyanez lesz látható a http://localhost/cgi-bin/counter.cgi webcím alatt is.

gvamosi@gergo1:/usr/lib/cgi-bin$ gcc counter.c -o counter.cgi
gvamosi@gergo1:/usr/lib/cgi-bin$ ls -l
total 32
-rw-r--r-- 1 gvamosi gvamosi   216 Aug 12 03:05 counter.c
-rwxr-xr-x 1 gvamosi gvamosi 16720 Aug 12 03:05 counter.cgi
-rwxr-xr-x 1 gvamosi gvamosi   158 Aug 12 02:47 counter.pl
-rwxr-xr-x 1 gvamosi gvamosi    84 Aug 12 02:37 counter.sh
gvamosi@gergo1:/usr/lib/cgi-bin$ ./counter.cgi 
Content-type: text/html

The current count is: 1

Bámulatos, nem? 🙂 Hogy mire lesz jó ez az egész három különböző nyelvű, méghozzá benne egy natív C implementáció is? A következő fejezetekben kiderül!

Megjegyzés: natívan, bár gyorsabb a végeredmény, azért nem fejlesztünk többnyire, mert rendkívül bonyoluttá válik összetett oldalak esetén a programkód. Továbbá elég egy pici hiba, és már is “vulnerable”, azaz sérülékeny lehet a weboldalunk, amit a hacker-ek ún. exploit-tal ki tudnak használni, azaz adott esetben fel is törthetik az oldalainkon keresztül a szervereinket.

Apache config – a webszerver beállításának (konfigurációjának) alapjai, hasznos modulok

Ebben a fejezetben megnézzük, hogy mik a főbb konfigurációs direktívái az Apache webszerverünknek, és hogy milyen főbb beépülő modulokkal rendelkezik, és ezek hogyan konfigurálhatóak, ti. moduláris a felépítése.

Van egy kisebb bemutató szócikk a magyar wikipédián, ez a https://hu.wikipedia.org/wiki/Apache_HTTP_Server cím alatt érhető el. Amit kiemelnék ez alapján az angol nyelvű szócikkből az, hogy a világ webes kiszolgálóinak majd egyharmadát 2020-ban az Apache 2-es szerver adta, míg a top 10 milló website 40%-át. Ez igen komoly részesedés. Továbbá azt is érdemes megemlíteni, hogy a magyarországi Internet hőskorában a 90-es években gyakorlatilag Apache volt vagy semmi. Mondjuk anno írtunk 20 soros saját webszervert is perl-ben. 🙂

Mindenféle Apache dokumentáció kiindulásául szolgáljon a hivatalos angol nyelvű weblap. E cikk keletkezésének idején az aktuális verzió a 2.4-es, webcíme a http://httpd.apache.org/docs/2.4/.

A konfiguráció rövid bemutatása a http://httpd.apache.org/docs/2.4/configuring.html cím alatt olvasható. A teljes fordítás nélkül beszéljünk pár részletről.

A konfigurációs állaományt httpd.conf-nak hívják, és saját fordításnál a /usr/local/apache2/conf könyvár alatt található. Az előreinstallált különféle disztribúciós csomagokban viszont a rendszer /etc könyvtárában találhatóak a különböző konfigurációs állományok. Nálam, Debian 10.5 alatt történetesen a /etc/apache2 könyvtár alatt rejlik minden beállítás. Ez egyébként nem is standard megoldású konfiguráció, de könnyen ki lehet találni a működését. Az Include direktíva (http://httpd.apache.org/docs/2.4/mod/core.html#include) teszi lehetővé a konfiguráció több állományra osztását, illetve beillesztését végsősoron egy konfigurációs file-ba.

Amennyiben a beálításban lehetőséget adunk az AllowOverride direktívával (http://httpd.apache.org/docs/2.4/mod/core.html#allowoverride), akkor az ún. .htaccess file-okban a különböző könyvtárainkban az ún. DocumentRoot könyvtár, azaz a webes gyökér könyvtárunk alatt felülírhatunk bizonyos beállításokat. Ez főleg <VirtualHost> direktívával történő konfiguráció esetén (http://httpd.apache.org/docs/2.4/mod/core.html#virtualhost), tehát virtuális szerverek, magyarul egy fizikai gépen több webcím üzemeltetése esetén hasznosak igazán – végső soron masszív többfelhasználós módban. Pláne szükségszerű, ha továbbértékesítjük a virtuális webhelyeket, hiszen így – bizonyos főbb biztonsági megfontolások mellett – minden ügyfelünk saját maga állíthatja be bizonyos dolgokat. Példa: .pl kiterjesztésű – többnyire perl – programkód ún. CGI-ként (https://hu.wikipedia.org/wiki/Common_Gateway_Interface) vagy akár beépülő perl modulként fusson. Ez mindenkor beállítás kérdése, mert hívhatnám akár .kutyafule-nek is ezeket az állományokat. Természetesen ezek a dinamikus tartalmat generáló programocskák pont így jelennek meg az URL-ben is a böngészőablakban kliensoldalon. 🙂

Nézzük meg a <Directory> direktívát egy konkrét példában.

<Directory "/usr/local/httpd/htdocs">
  Options Indexes FollowSymLinks
</Directory>

Ez azt jelenti, hogy a /usr/local/httpd/htdocs könyvtár – általában ServerRoot, ami az “alap” hoszt DocumentRoot könyvtára – mutasson könytár indexeket és “kövesse” a szimbolikus linkeket (részletek: https://gvamosi.wordpress.com/2019/09/08/jogosutsagkezeles-filerendszer-szinten/). Sok kis trükk és beállítási lehetőség, nem célom mindet bemutatni.

Az egész konfiguráció lényege tulajdonképpen az, hogy filerendszerben lévő könyvtárak, programocskák és állományok milyen módon kerülnek a webre, azaz a böngésző címsorába.

A modulokról is ejtsünk néhány szót. Betöltésük, ha jelen vannak a rendszerben, a LoadModule direktívával történik (http://httpd.apache.org/docs/2.4/mod/mod_so.html#loadmodule). Konfigurációjuk feltételes az <IfModule> direktíva segítségével történik (http://httpd.apache.org/docs/2.4/mod/core.html#ifmodule). Nyilván azért, hogy csak akkor hajtótdjon végre a beállítás, ha lehetséges, tehát a modul betöltött. Ez amúgy sima Linux dinamikus könytárként van megvalósítva. 🙂

A modulok listája az alábbi oldalon elérhető http://httpd.apache.org/docs/2.4/mod/. Nem teljes, vannak alprojektek is, mint például a FastCGI modul (http://httpd.apache.org/mod_fcgid/), amely segítségével a CGI-k folyton kiszolgálják a kéréseket – tehát a memóriában maradnak. Így gyorsabban futnak, mivel nem kell újra és újra “elindulniuk”. Ez a teljesítménybeli kérdéseknél lesz majd fontos.

Hasznos modulok a mod_cgi – CGI-k végrehajtása, a mod_cache – mindenféle gyorsítótárazás, a mod_proxy – proxy-szerver, azaz “előtét” (https://hu.wikipedia.org/wiki/Proxyszerver), a mod_rewrite – URL-ek szabály alapú átírása (így tűntethető el az a bizonyos index.php rész a CodeIgniter URL-ekből :)) vagy a mod_ssl – HTTPS biztonság.