Mercurial > pub > Impl
annotate Lib/IMPL/Web/Application/Response.pm @ 217:bfce101e0a5a
Слияние
| author | cin |
|---|---|
| date | Mon, 20 Aug 2012 10:27:20 +0400 |
| parents | d6e2ea24af08 |
| children |
| rev | line source |
|---|---|
|
59
0f3e369553bd
Rewritten property implementation (probably become slower but more flexible)
wizard
parents:
58
diff
changeset
|
1 package IMPL::Web::Application::Response; |
| 57 | 2 use strict; |
| 3 | |
| 166 | 4 use parent qw(IMPL::Object IMPL::Object::Autofill); |
| 57 | 5 |
| 6 require IMPL::Exception; | |
| 7 require CGI; | |
| 8 require CGI::Cookie; | |
| 9 | |
| 58 | 10 use Carp; |
|
63
76b878ad6596
Added serialization support for the IMPL::Object::List
wizard
parents:
59
diff
changeset
|
11 use Encode; |
| 57 | 12 use IMPL::Class::Property; |
| 13 | |
|
63
76b878ad6596
Added serialization support for the IMPL::Object::List
wizard
parents:
59
diff
changeset
|
14 #todo: add binary method to set a binary encoding, set it automatic when type isn't a text |
|
76b878ad6596
Added serialization support for the IMPL::Object::List
wizard
parents:
59
diff
changeset
|
15 |
| 57 | 16 BEGIN { |
| 194 | 17 # автозаполнение буде происходить в порядке объявления |
| 18 public property query => prop_get | owner_set; # cgi query | |
| 19 public property status => prop_all, { validator => \&_checkHeaderPrinted }; | |
| 20 public property contentType => prop_all, { validator => \&_checkHeaderPrinted }; # String | |
| 21 public property charset => { get => \&_charset, set => \&_charset }, { validator => \&_checkHeaderPrinted }; | |
| 22 public property expires => prop_all, { validator => \&_checkHeaderPrinted }; | |
| 23 public property cookies => prop_all, { validator => \&_checkHeaderPrinted }; # Hash | |
| 24 | |
| 25 public property buffered => prop_all, { validator => \&_canChangeBuffer }; # Boolean | |
| 26 public property streamOut => prop_get | owner_set; # stream | |
| 27 public property streamBody => {get => \&getStreamBody }; # stream | |
| 28 public property isHeaderPrinted => prop_get | owner_set; # Boolean | |
| 29 | |
| 30 private property _bufferBody => prop_all; | |
| 31 private property _streamBody => prop_all; | |
| 57 | 32 } |
| 33 | |
| 34 __PACKAGE__->PassThroughArgs; | |
| 35 | |
| 67 | 36 our %CTOR = ( |
| 194 | 37 'IMPL::Object::Autofill' => sub { |
| 38 my %args = @_; | |
| 39 | |
| 40 $args{query} = CGI->new($args{query} || {}); | |
| 41 | |
| 42 %args; | |
| 43 } | |
| 67 | 44 ); |
| 45 | |
| 57 | 46 sub CTOR { |
| 194 | 47 my ($this,%args) = @_; |
| 48 | |
| 207 | 49 if ($this->streamOut and lc $this->streamOut eq 'memory') { |
| 194 | 50 my $dummy = ''; |
| 51 open my $hout, '>:encoding(utf8)', \$dummy or die new IMPL::Exception("Failed to create memory stream",$!); | |
| 52 $this->streamOut($hout); | |
| 53 } elsif (not $this->streamOut) { | |
| 54 $this->streamOut(*STDOUT); | |
| 55 } else { | |
| 56 die new IMPL::InvalidArgumentException("Invalid parameter value",$this->streamOut); | |
| 57 } | |
| 58 | |
| 59 $this->buffered(1) unless defined $this->buffered; | |
| 60 binmode $this->streamOut, ":encoding(".$this->charset.")"; | |
| 57 | 61 } |
| 62 | |
| 63 sub _checkHeaderPrinted { | |
| 194 | 64 my ($this,$value) = @_; |
| 65 | |
| 66 die new IMPL::InvalidOperationException() if $this->isHeaderPrinted; | |
| 57 | 67 } |
| 68 | |
| 58 | 69 sub _canChangeBuffer { |
| 194 | 70 my ($this,$value) = @_; |
| 71 | |
| 72 die new IMPL::InvalidOperationException() if $this->isHeaderPrinted or $this->_streamBody; | |
| 58 | 73 } |
| 74 | |
| 75 sub _charset { | |
| 194 | 76 my $this = shift; |
| 77 | |
| 78 if (@_) { | |
| 79 my $charset = $this->query->charset(@_); | |
| 80 | |
| 81 my $hout = $this->streamOut; | |
| 82 | |
| 83 binmode $hout; | |
| 84 binmode $hout, ":encoding($charset)"; | |
| 85 | |
| 86 return $charset; | |
| 87 } else { | |
| 88 return $this->query->charset; | |
| 89 } | |
| 58 | 90 } |
| 91 | |
| 57 | 92 sub _PrintHeader { |
| 194 | 93 my ($this) = @_; |
| 94 | |
| 95 unless ($this->isHeaderPrinted) { | |
| 96 $this->isHeaderPrinted(1); | |
| 97 | |
| 98 my %opt; | |
| 99 | |
| 100 $opt{-type} = $this->contentType if $this->contentType; | |
| 101 $opt{-status} = $this->status if $this->status; | |
| 102 $opt{-expires} = $this->expires if $this->expires; | |
| 103 | |
| 104 my $refCookies = $this->cookies; | |
| 105 $opt{-cookie} = [map _createCookie($_,$refCookies->{$_}), keys %$refCookies] if $refCookies; | |
| 106 | |
| 107 my $hOut = $this->streamOut; | |
| 108 | |
| 109 print $hOut $this->query->header( | |
| 110 %opt | |
| 111 ); | |
| 112 } | |
| 57 | 113 } |
| 114 | |
| 97 | 115 sub _createCookie { |
| 194 | 116 return UNIVERSAL::isa($_[1], 'CGI::Cookie') ? $_[1] : CGI::Cookie->new(-name => $_[0], -value => $_[1] ); |
| 97 | 117 } |
| 118 | |
|
95
67eb8eaec3d4
Added a security authority property to the Context and Security classes
wizard
parents:
67
diff
changeset
|
119 sub setCookie { |
| 194 | 120 my ($this,$name,$value) = @_; |
| 121 | |
| 122 unless ($this->cookies) { | |
| 123 $this->cookies({$name,$value}); | |
| 124 } else { | |
| 125 $this->_checkHeaderPrinted(); | |
| 126 $this->cookies->{$name} = $value; | |
| 127 } | |
| 128 return $value; | |
|
95
67eb8eaec3d4
Added a security authority property to the Context and Security classes
wizard
parents:
67
diff
changeset
|
129 } |
|
67eb8eaec3d4
Added a security authority property to the Context and Security classes
wizard
parents:
67
diff
changeset
|
130 |
| 57 | 131 sub getStreamBody { |
| 194 | 132 my ($this) = @_; |
| 133 | |
| 134 return undef unless $this->streamOut; | |
| 135 | |
| 136 unless ($this->_streamBody) { | |
| 137 if ($this->buffered) { | |
| 138 my $buffer = ""; | |
| 139 | |
| 140 $this->_bufferBody(\$buffer); | |
| 141 | |
| 142 open my $hBody, ">:encoding(utf-8)", \$buffer or die new IMPL::Exception("Failed to create buffer",$!); | |
| 143 | |
| 144 Encode::_utf8_on($buffer); | |
| 145 | |
| 146 $this->_streamBody($hBody); | |
| 147 } else { | |
| 148 $this->_PrintHeader(); | |
| 149 $this->_streamBody($this->streamOut); | |
| 150 } | |
| 151 } | |
| 152 | |
| 153 return $this->_streamBody; | |
| 57 | 154 } |
| 155 | |
| 156 sub Complete { | |
| 194 | 157 my ($this) = @_; |
| 158 | |
| 159 return 0 unless $this->streamOut; | |
| 160 | |
| 161 my $hOut = $this->streamOut; | |
| 162 | |
| 163 $this->_PrintHeader(); | |
| 97 | 164 |
| 194 | 165 close $this->_streamBody(); |
| 166 | |
| 167 if ($this->buffered) { | |
| 168 print $hOut ${$this->_bufferBody}; | |
| 169 } | |
| 170 | |
| 171 $this->_bufferBody(undef); | |
| 172 $this->streamOut(undef); | |
| 173 | |
| 174 return 1; | |
| 57 | 175 } |
| 176 | |
| 177 sub Discard { | |
| 194 | 178 my ($this) = @_; |
| 179 | |
| 180 carp "Discarding sent response" if $this->isHeaderPrinted; | |
| 181 | |
| 182 $this->_streamBody(undef); | |
| 183 $this->_bufferBody(undef); | |
| 184 $this->streamOut(undef); | |
| 57 | 185 } |
| 186 | |
| 187 1; | |
| 188 | |
| 189 __END__ | |
| 190 | |
| 191 =pod | |
| 192 | |
| 67 | 193 =head1 NAME |
| 194 | |
| 180 | 195 C<IMPL::Web::Application::Response> - Ответ веб сервера непосредственно клиенту. |
| 67 | 196 |
| 58 | 197 =head1 DESCRIPTION |
| 57 | 198 |
| 67 | 199 C<[Infrastructure]> |
| 200 | |
| 180 | 201 Позволяет сформировать основные свойства заголовка и тело ответа. |
| 67 | 202 |
| 180 | 203 Создается объектом C<IMPL::Web::Application::Action> в процессе обработки запроса. |
| 67 | 204 |
| 180 | 205 Может использоваться обработчиками C<IMPL::Web::QueryHandler> в процессе выполнения запроса. |
| 58 | 206 |
| 180 | 207 Объект позволяет буфферизировать вывод в тело ответа, что позволяет отменить или изменить |
| 208 ответ в последний момент. Свойство C< isHeaderPrinted > используется для определения факта | |
| 209 отправлки данных клиенту. | |
| 58 | 210 |
| 211 =head1 PROPERTIES | |
| 212 | |
| 213 =head2 HTTP Header | |
| 214 | |
| 180 | 215 Свойства отвечающие за заголовок HTTP ответа. Эти своства могут быть изменены до тех пор пока |
| 216 не будет отправлен заголовок. В противном случае выдается исключение C< IMPL::InvalidOperationException >. | |
| 58 | 217 |
| 218 =over | |
| 219 | |
| 67 | 220 =item C< [get] query > |
| 58 | 221 |
| 180 | 222 CGI запрос, который используется для вывода данных, заголовка и пр. Существует всегда. |
| 58 | 223 |
| 67 | 224 =item C< [get,set] status > |
| 58 | 225 |
| 213 | 226 Код HTTP. Например, '200 OK'. По умолчанию не установлен, при отправке клиенту бедт отправлен '200 ОК'. |
| 58 | 227 |
| 67 | 228 =item C< [get,set] contentType > |
| 58 | 229 |
| 180 | 230 Тип MIME. По умолчанию не установлен, подразумивается 'text/html'. |
| 58 | 231 |
| 67 | 232 =item C< [get,set] charset > |
| 58 | 233 |
| 180 | 234 Кодировка, синоним свойства query->charset. |
| 58 | 235 |
| 67 | 236 =item C< [get,set] expires > |
| 58 | 237 |
| 180 | 238 Определяет время жизни контента, например '+10m'. По умолчанию не задано и не передается. |
| 58 | 239 |
| 67 | 240 =item C< [get,set] cookies > |
| 58 | 241 |
| 180 | 242 Хеш массив с cookies, например C< { cart => ['foo','bar'], display => 'list' } >. |
| 58 | 243 |
| 244 =back | |
| 245 | |
| 246 =head2 Response behaviour | |
| 247 | |
| 180 | 248 Свойства отвечающие за поведение ответа. |
| 58 | 249 |
| 250 =over | |
| 251 | |
| 67 | 252 =item C< [get,set] buffered > |
| 58 | 253 |
| 180 | 254 C< True > - то тело ответа пишется в буффер и будет отправлено при вызове метода C< Complete >, |
| 255 заголовок также будет отправлен после вызова метода C< Complete >. | |
| 58 | 256 |
| 180 | 257 C< False > - тело ответа пишется непосредственно в поток к клиенту, при этом заголовок |
| 258 будет отправлен при первом обращении к свойству C< streamBody > | |
| 58 | 259 |
| 180 | 260 Это свойство можно менять до первого обращения к потоку для записи в тело ответа. |
| 58 | 261 |
| 67 | 262 =item C< [get] streamOut > |
| 58 | 263 |
| 180 | 264 Стандартный вывод CGI приложения. |
| 58 | 265 |
| 67 | 266 =item C< [get] streamBody > |
| 58 | 267 |
| 180 | 268 Поток для записи в тело ответа. |
| 58 | 269 |
| 173 | 270 =item C< [get] isHeaderPrinted > |
| 58 | 271 |
| 180 | 272 Признак того, что заголовок уже был отправлен клиенту. |
| 58 | 273 |
| 274 =back | |
| 275 | |
| 276 =head1 METHODS | |
| 277 | |
| 278 =over | |
| 279 | |
| 280 =item C< Complete > | |
| 281 | |
| 180 | 282 Завершает отправку ответа. |
| 58 | 283 |
| 284 =item C< Discard > | |
| 285 | |
| 180 | 286 Отменяет отправку ответа, при этом если часть данных (например, заголовок) |
| 287 уже была отправлена, выдает предупреждение в STDERR. | |
| 58 | 288 |
| 289 =back | |
| 57 | 290 |
| 67 | 291 =head1 REMARKS |
| 292 | |
| 180 | 293 Данный объект является автозаполняемым, т.е. все его свойства можно задать через |
| 294 именованные параметры конструктора. | |
| 67 | 295 |
| 180 | 296 =cut |
