В 1998 году казнет насчитывал несколько тысяч пользователей - в основном это были сотрудники государственных структур, крупных компаний и университетов в Алматы. Подключение: dial-up через модем на 28-33 кбит/с. Браузер: Internet Explorer 4 или Netscape Navigator 4. Хостинг: физически в Москве, Берлине или Амстердаме, потому что в самом Казахстане надёжных дата-центров почти не было.
Тот, кто хотел создать не просто брошюру в HTML, а настоящий динамический сайт - с формой обратной связи, гостевой книгой, новостной лентой из базы данных - должен был освоить CGI. И самым распространённым языком для CGI в 1998 году был Perl.
Гостевая книга - визитная карточка сайта 1998 года
Гостевая книга была стандартным требованием к «серьёзному» сайту. В Алматы тогда только появлялись первые веб-студии, и умение развернуть гостевую книгу на Perl считалось квалификационным минимумом.
#!/usr/bin/perl
# guestbook.pl - гостевая книга для казахстанского сайта, 1998 год
# Кодировка: windows-1251 (стандарт того времени)
use strict;
use warnings;
use CGI;
use POSIX qw(strftime);
my $GUESTBOOK_FILE = '/home/mysite/data/gb.txt';
my $MAX_ENTRIES = 50;
my $cgi = new CGI;
my $action = $cgi->param('action') || 'view';
if ($action eq 'post') {
handle_post($cgi);
} else {
show_guestbook($cgi);
}
sub handle_post {
my ($cgi) = @_;
my $name = $cgi->param('name') || '';
my $city = $cgi->param('city') || '';
my $message = $cgi->param('message') || '';
for my $var ($name, $city, $message) {
$var =~ s/[<>&"']//g;
$var =~ s/\n/ /g;
}
if (length($name) < 2 || length($message) < 5) {
print_header();
print "<p><font color='red'><b>Ошибка:</b> Введите имя и сообщение.</font></p>\n";
print "<p><a href='guestbook.pl'>Назад</a></p>\n";
print_footer();
return;
}
# Форматируем дату по-казахстански: день.месяц.год
my $timestamp = strftime('%d.%m.%Y %H:%M', localtime);
my @entries = read_entries();
unshift @entries, {
name => $name,
city => $city,
message => $message,
timestamp => $timestamp,
};
@entries = @entries[0 .. $MAX_ENTRIES - 1] if @entries > $MAX_ENTRIES;
write_entries(\@entries);
print "Content-Type: text/html; charset=windows-1251\n\n";
print "<html><head><meta http-equiv='refresh' content='0;url=guestbook.pl'>\n";
print "</head><body>Спасибо! <a href='guestbook.pl'>Вернуться</a></body></html>\n";
}
sub show_guestbook {
my ($cgi) = @_;
print_header();
print <<'FORM';
<h2>Оставить запись</h2>
<form method="POST" action="guestbook.pl">
<input type="hidden" name="action" value="post">
<table border="0" cellpadding="4">
<tr>
<td><b>Имя:</b> *</td>
<td><input type="text" name="name" size="25" maxlength="50"></td>
</tr>
<tr>
<td><b>Город:</b></td>
<td><input type="text" name="city" size="25" maxlength="50"></td>
</tr>
<tr>
<td valign="top"><b>Сообщение:</b> *</td>
<td><textarea name="message" rows="4" cols="40"></textarea></td>
</tr>
<tr>
<td></td>
<td><input type="submit" value="Оставить запись"></td>
</tr>
</table>
</form>
<hr>
<h2>Записи посетителей</h2>
FORM
my @entries = read_entries();
if (!@entries) {
print "<p><i>Будьте первым!</i></p>\n";
print_footer();
return;
}
for my $entry (@entries) {
print "<p>\n";
print "<b>" . $entry->{name} . "</b>";
print " (" . $entry->{city} . ")" if $entry->{city};
print " — " . $entry->{timestamp} . "<br>\n";
print $entry->{message} . "\n";
print "</p><hr size='1'>\n";
}
print_footer();
}
sub read_entries {
my @entries;
return @entries unless -f $GUESTBOOK_FILE;
open(my $fh, '<', $GUESTBOOK_FILE) or return @entries;
my $e = {};
while (my $line = <$fh>) {
chomp $line;
if ($line =~ /^N:(.*)$/) { $e->{name} = $1 }
elsif ($line =~ /^C:(.*)$/) { $e->{city} = $1 }
elsif ($line =~ /^M:(.*)$/) { $e->{message} = $1 }
elsif ($line =~ /^T:(.*)$/) { $e->{timestamp} = $1 }
elsif ($line eq '=') {
push @entries, $e if $e->{name};
$e = {};
}
}
close($fh);
return @entries;
}
sub write_entries {
my ($entries) = @_;
open(my $fh, '>', $GUESTBOOK_FILE) or die "Ошибка записи: $!";
for my $e (@$entries) {
print $fh "N:" . $e->{name} . "\n";
print $fh "C:" . $e->{city} . "\n";
print $fh "M:" . $e->{message} . "\n";
print $fh "T:" . $e->{timestamp} . "\n";
print $fh "=\n";
}
close($fh);
}
sub print_header {
print "Content-Type: text/html; charset=windows-1251\n\n";
print "<html>\n<head><title>Гостевая книга</title></head>\n";
print "<body bgcolor='#FFFFFF'>\n<h1>Гостевая книга сайта</h1>\n";
}
sub print_footer {
print "<hr><small>Алматы, 1998 • Perl CGI / Apache</small>\n";
print "</body></html>\n";
}
Реалии казахстанского хостинга в 1998 году
В 1998 году хостить сайт в Казахстане технически было очень сложно. Большинство казахстанских компаний размещали сайты:
- На российских хостингах (Cityline.ru, РТС/Demos, Zenon)
- На немецких (Strato, 1&1 - тогда они уже работали)
- У американских провайдеров, принимавших оплату через банковский перевод
Это создавало практическую проблему для CGI-скриптов: серверы были за рубежом, любая отладка занимала часы из-за разницы во времени с техподдержкой. Самые распространённые действия при деплое:
# Подключение по FTP (SSH был редкостью на shared-хостинге 1998 года)
# FileZilla ещё не было - использовали WS_FTP LE или Total Commander
# Загружали файл в cgi-bin/
# Подключение по Telnet для настройки прав
telnet myhost.de
# Логин, пароль
chmod 755 guestbook.pl
chmod 777 data/
ls -la
# Проверка: открыть в Netscape Navigator 4
# http://www.mojacompany.kz/cgi-bin/guestbook.pl
Ошибка 500 Internal Server Error при неверном пути к Perl:
#!/usr/bin/perl # путь на большинстве Linux-серверов
#!/usr/local/bin/perl # путь на FreeBSD - типичный для немецких хостингов
#!/usr/bin/env perl # универсальный вариант, работавший везде
Кодировка: головная боль 1998 года
Если в Рунете шла война windows-1251 vs KOI8-R, то в казнете была отдельная сложность: казахские буквы (Ә, Ғ, Қ, Ң, Ө, Ұ, Ү, І) просто не входили ни в одну стандартную кодировку того времени. В 1998 году на казахстанских сайтах текст на казахском языке встречался крайне редко - не было технического решения.
# В 1998 году для CGI на казнете: только windows-1251
# Казахский текст: или транслитерация, или просто русский
print "Content-Type: text/html; charset=windows-1251\n\n";
# KOI8-R встречался на Unix-серверах у технических сайтов
# Конвертация если нужно:
use Encode;
my $text = Encode::encode('koi8-r',
Encode::decode('cp1251', $win1251_text));
Полноценная поддержка казахского языка в вебе появится только с переходом на UTF-8 - примерно к 2005-2008 годам.
Почему Perl уступил PHP в казнете
К 2000-2001 году PHP 3/4 захватили рынок по той же причине, что и в Рунете - простота деплоя. Но в казахстанском контексте было ещё одно измерение: доступность обучающих материалов на русском языке.
Документация PHP была переведена на русский быстро и полно. Российские хостинги (Cityline, Zenon) активно рекламировали поддержку PHP. Учебников и статей по PHP на русском было в несколько раз больше, чем по Perl CGI - это решало всё для казахстанских разработчиков, которые работали в русскоязычной технической среде.
Perl остался инструментом системных администраторов и тех, кто пришёл из Unix-культуры. Веб-разработка в казнете к 2001 году почти полностью перешла на PHP.