Модераторы: korob2001, ginnie
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Разбор запроса http 
:(
    Опции темы
justauser
Дата 22.9.2014, 12:31 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 7
Регистрация: 22.9.2014

Репутация: нет
Всего: нет



Начал разбираться с Perl (я не программист) и для тренировки решил написать простейший http сервер без использования спец. модулей. Пока получилось сделать сетевую чась (минимально). Сервер слушает порт 80, принимает подключения, форкается и выдает прописанный в скрипте index.html не разбирая запрос. Это работает в браузере, я проверил. Но запрос может быть на разные страницы поэтому его нужно разобрать на отдельные переменные. Пока я его просто печатаю в консоли
GET / HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:32.0) Gecko/20100101 Firefox/32.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
а нужно все параметры разделить по отдельным переменным, но не соображу как. Можете написать пример?
Запрос получаю
$count = sysread(FH, $data, 1400);
вот $data и нужно разобрать.

p.s.
Заодно проверьте пожалуйста на ошибки то что я уже сделал
Код

#!/usr/bin/perl
use strict;
use warnings;
use Socket;
use POSIX;
my $host = "localhost";
my $port = "80";
my $length = 10;
my $pif;
my $sin = sockaddr_in($port,inet_aton($host));

  socket(F, PF_INET, SOCK_STREAM, getprotobyname('tcp')) || die $!;
  setsockopt(F, SOL_SOCKET, SO_REUSEADDR, 1); 
  bind(F,$sin)  || die $!;
  listen(F, $length) || die $!;
  
print "PID: $$\n\n";  
while (accept(FH,F) || die $!) {

$SIG{CHLD} = &REAPER;

#ветвление процесса
defined($pif=fork()) || die $!; 

if ($pif){
#родитель
 close(FH);
}
else
{    
#потомок
 print "Child-PID: $$\n\n";
 child();
}
}

close(F);
exit();

sub child{
    
 close(F);
my $fh;
my @fhn;
my $data;
my $count;

 $count = sysread(FH, $data, 1400);
 
 open($fh, "<", "/var/www/index.html");
 @fhn=<$fh>;
 print FH "HTTP/1.0 200 Ok\r\nContent-Type: text/html\r\nContent-Length: 82\r\nConnection: close\r\n\r\n";
 print FH @fhn;
 print $data;
 close($fh);
 close(FH);
 exit();
 }

sub REAPER {    
 while ((my $waitedpid = waitpid(-1,WNOHANG)) > 0) {
 $SIG{CHLD} = &REAPER;}
 }


PM MAIL   Вверх
arto
Дата 22.9.2014, 12:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1495
Регистрация: 31.10.2004

Репутация: 38
Всего: 40



# perl -MData::Dumper -le 'print Dumper do { local$/="";my%a=map{split":? ",$_,2}split"\r?\n",<STDIN>;\%a}'
$VAR1 = {
          'User-Agent' => 'Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:32.0) Gecko/20100101 Firefox/32.0',
          'Accept-Language' => 'en-US,en;q=0.5',
          'Accept-Encoding' => 'gzip, deflate',
          'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
          'GET' => '/ HTTP/1.1',
          'Connection' => 'keep-alive',
          'Host' => 'localhost'
        };
#
PM MAIL ICQ   Вверх
justauser
Дата 22.9.2014, 12:56 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 7
Регистрация: 22.9.2014

Репутация: нет
Всего: нет



Спасибо но мне без доп модулей нужно. А  Data::Dumper отдельный модуль. Как можно $data разобрать стандартными средствами? 
PM MAIL   Вверх
arto
Дата 22.9.2014, 13:11 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1495
Регистрация: 31.10.2004

Репутация: 38
Всего: 40



оно разбирает стандартными средствами, а Dumper выводит в удобоваримом виде.
PM MAIL ICQ   Вверх
justauser
Дата 22.9.2014, 17:45 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 7
Регистрация: 22.9.2014

Репутация: нет
Всего: нет



По вашему предложению сделал такой тестовый скрипт для разбора, вроде работает. Наверняка можно проще.

Код

#!/usr/bin/perl 
local$/="";
my @a=map{split":? ",$_,2}split"\r?\n",<STDIN>;
my $n=@a/2;
print "Найдено строк: ".$n."\n";
for ($x=0; $x < @a; ++$x){
    if ($a[$x] eq "GET"){
    $get=$a[++$x];
    } elsif ($a[$x] eq "Host"){
    $host=$a[++$x];
    } elsif ($a[$x] eq "User-Agent"){
    $agent=$a[++$x];
    } elsif ($a[$x] eq "Accept"){
    $acc=$a[++$x];
    } elsif ($a[$x] eq "Accept-Language"){
    $lang=$a[++$x];
    } elsif ($a[$x] eq "Accept-Encoding"){
    $enc=$a[++$x];
    } elsif ($a[$x] eq "Connection"){
    $connection=$a[++$x];
    } 
 }
print "GET: ".$get."\n";
print "Host: ".$host."\n";
print "User-Agent: ".$agent."\n";
print "Accept: ".$acc."\n";
print "Accept-Language: ".$lang."\n";
print "Accept-Encoding: ".$enc."\n";
print "Connection: ".$connection."\n";


PM MAIL   Вверх
noize
Дата 22.9.2014, 21:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


5.18.2
*


Профиль
Группа: Участник
Сообщений: 89
Регистрация: 10.7.2010
Где: СПб

Репутация: 3
Всего: 3



1. Добавьте пробелов в строки, иначе всё сливается в ###код
2. Используйте прагмы strict и warnings, не стоит начинать изучение языка с хардкора
Код

my @a=map{split":? ",$_,2}split"\r?\n",<STDIN>;

возьмите \r в скобки - 
Код

split "(\r)?\n", <STDIN>

PM MAIL   Вверх
arto
Дата 22.9.2014, 23:51 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1495
Регистрация: 31.10.2004

Репутация: 38
Всего: 40



а для чего (\r) ?
PM MAIL ICQ   Вверх
noize
Дата 23.9.2014, 00:17 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


5.18.2
*


Профиль
Группа: Участник
Сообщений: 89
Регистрация: 10.7.2010
Где: СПб

Репутация: 3
Всего: 3



думал, что интерпретатор может воспринять "\r" как 2 символа - "\" и "r" и тогда условие \r? проверяло бы на существование только 'r'. Сейчас проверил у себя локально - воспринимает как единый символ, так что скобка там в принципе не обязатальна, да.
PM MAIL   Вверх
arto
Дата 23.9.2014, 07:28 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1495
Регистрация: 31.10.2004

Репутация: 38
Всего: 40



оно ещё и неверно работает:

# print "aa\r\nbb\r\n" | perl -0777 -lne 'print join "+", split "(\r)?\n"'
+bb+
# print "aa\r\nbb\r\n" | perl -0777 -lne 'print join "+", split "\r?\n"'
aa+bb
#
PM MAIL ICQ   Вверх
justauser
Дата 23.9.2014, 14:13 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 7
Регистрация: 22.9.2014

Репутация: нет
Всего: нет



Сделал вариант с хэшем тоже работает.
Код

#!/usr/bin/perl

use strict;
use warnings;

local$/="";

my    ($get,$host,$agent,$acc,$lang,$enc,$connect);

my %a=map{split":? ",$_,2}split"\r?\n",<STDIN>;

    $get=$a{'GET'};

    $host=$a{'Host'};

    $agent=$a{'User-Agent'};

    $acc=$a{'Accept'};

    $lang=$a{'Accept-Language'};

    $enc=$a{'Accept-Encoding'};

    $connect=$a{'Connection'};

print "GET: ".$get."\n";
print "Host: ".$host."\n";
print "User-Agent: ".$agent."\n";
print "Accept: ".$acc."\n";
print "Accept-Language: ".$lang."\n";
print "Accept-Encoding: ".$enc."\n";
print "Connection: ".$connect."\n";


Но при доп. тестировании выявилась проблема с разбором если параметр объявлен но его значения в запросе нет. 

GET / HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:32.0) Gecko/20100101 Firefox/32.0
Accept:
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive

Оба скрипта тогда неправильно работают. Получается нужно при разделении строки на поля проверять чтоб было обязательно два поля и если значение не задано нужно удалять всю строку или подставлять что-то свое типа "Undefined!"  Не соображу как, может кто подскажет.
PM MAIL   Вверх
noize
Дата 23.9.2014, 14:49 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


5.18.2
*


Профиль
Группа: Участник
Сообщений: 89
Регистрация: 10.7.2010
Где: СПб

Репутация: 3
Всего: 3



Строки с 11 по 32 можно заменить так:
Код

for my $key ( keys %a ) {
    print "$key: " . ( $a{ $key } ? $a{ $key } : "Undef" ) . "\n";
}


тернарный оператор
Код

$a{ $key } ? $a{ $key } : "Undef" 

проверяет наличие в массиве значения для ключа $key и печатает это значение. Если значения нет, печатает "Undef"

Это сообщение отредактировал(а) noize - 23.9.2014, 14:53
PM MAIL   Вверх
hobo1mts
Дата 24.9.2014, 06:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 4
Регистрация: 21.6.2013

Репутация: нет
Всего: 1



Это ж классика жанра
Код

my %headers;
while (my ($k, $v) = /(.*?):\s*(.*)/ =~ <STDIN>) {
  $headers{$k} = $v;
}

Тут ещё нада добавить проверку длинных строк, которые продолжаются на следующей строке, начинаясь с пробельного символа.

PM MAIL   Вверх
DProf
Дата 25.9.2014, 16:43 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 39
Регистрация: 28.9.2012

Репутация: 1
Всего: 1



Вообще для "не программиста" начинать изучение языка с написания http сервера, пусть и простого, странно, ИМХО. Возьмите лучше книжку с Ламой и сделайте задания-примеры ко всем главам. А потом ее вторую часть.
PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Perl"
korob2001
sharq
  • В этом разделе обсуждаются общие вопросы по языку Perl
  • Если ваш вопрос относится к системному программированию, задавайте его здесь
  • Если ваш вопрос относится к CGI программированию, задавайте его здесь
  • Интерпретатор Perl можно скачать здесь ActiveState, O'REILLY, The source for Perl
  • Справочное руководство "Установка perl-модулей", можно скачать здесь


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, korob2001, sharq.

 
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Perl: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.0975 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.