summaryrefslogtreecommitdiff
path: root/lib/TUWF/Response.pod
blob: 20d8261537847e545eacf190e2a1584a08f73676 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
=head1 NAME

TUWF::Response - Response generation methods for TUWF

=head1 DESCRIPTION

This module is used to generate a HTTP response. It is automatically loaded by
TUWF and its methods can be used without requiring any special work.

This module can not be used outside of the TUWF framework.

=head1 METHODS

The following methods are added to the main TUWF object:

=head2 resInit()

Initializes or resets the response data. All headers, cookies and content will
be reset to their defaults. This method is called at the start of each request
by TUWF, but it can also be useful in your own code:

  # create a response, setting headers and content, etc...
  tuwf->resCookie(userid => $user);
  print { tuwf->resFd() } "Content!\n";
  
  # when you later decide that you actually wanted to create a totally
  # different response:
  tuwf->resInit();
  # ...after which you can start again

=head2 resHeader(name, value, add)

When only the I<name> argument is present, returns the list of values of the
named header in list context, or the first value in scalar context. An empty
list or undef is returned if the named response header has not been set.

When I<name> is given and I<value> is C<undef>, all set headers with that name
will be removed from the response.

When I<name> and I<value> are both given, the named header will be set to the
value, possibly overwriting an earlier header of the same name. When I<add> is
also given and set to a true value, the header will be I<added>, without
potentially overwriting a previous value.

The default header (as set by C<reqInit()>) is:

  Content-Type: text/html; charset=UTF-8

A C<Content-Encoding> header may be set by C<resBuffer()>. A C<Content-Length>
header is added automatically when the response is sent. C<Set-Cookie> headers
set by C<resCookie()> are not manipulated through this function.

TIP: When setting the C<Content-Type> header to a textual type, do not forget
to indicate that the charset is UTF-8.

=head2 resCookie(name, value, %options)

Add and manipulate C<Set-Cookie> response headers. When only I<name> is given
or when I<value> is C<undef>, the named cookie will be removed. That is, there
will be a C<Set-Cookie> header telling the browser to remove the cookie. When
I<value> is also defined, the cookie will be set to that value, possibly
overwriting a previous value. The following additional options are accepted:

=over

=item expires

A UNIX timestamp indicating when this cookie should expire. A value before the
current C<time()> means the cookie should be immediately removed, which is
equivalent to setting I<value> to undef.

=item domain

The domain name for which this cookie should be used.

=item path

The absolute path for which this cookie should be present.

=item secure

Set to true to add the I<secure> flag to the cookie. This means that the cookie
will only be present if the client is connected through HTTPS.

=item httponly

Set to true to only allow the cookie to be read using HTTP. That is, disallow
the cookie to be used within Javascript.

=back

It is possible to set defaults for these options with the
L<cookie_defaults|TUWF/cookie_defaults> setting.


=head2 resBuffer(action)

Reads or manipulates the output buffer and its encoding.  I<action> can be one
of the following:

=over

=item undef / empty string

Returns the currently used output encoding, which can be one of 'none', 'gzip'
or 'deflate'.

=item clear

Clears the output buffer, but does not modify the output encoding.

=item none

Clears the output buffer and disables any output encoding. All data will be
sent to the client as-is.

=item gzip

Clears the output buffer and enables gzip output compression. Requires
C<PerlIO::gzip>.

=item deflate

Same as gzip, but does not include the gzip header. (More correctly called
'zlib'). Requires C<PerlIO::gzip>.

=item auto

Automatically detect which output encoding to use. If C<PerlIO::gzip> is not
installed, it will use 'none'. If it is, it will choose whatever the browser
requested with the C<Accept-Encoding> request header.

=back

The C<Content-Encoding> response header is automatically set to the correct
value if compression is used.  C<reqInit()> automatically calls C<reqBuffer()>.
The default encoding can be set with the
L<content_encoding|TUWF/content_encoding> setting.

Note that the C<:utf8> IO filter is always enabled, regardless of the encoding.


=head2 resFd()

Returns the filehandle of the output buffer. The C<:utf8> filter is enabled on
this filehandle, meaning that everything you write to it should be in Perls
native unicode format. It is a bad idea to use this filehandle for anything
other than writing. It is important to remember that this filehandle writes to
a B<buffer>, and its contents will not be flushed before the request has been
fully processed. Since C<resBuffer()> creates a new filehandle each time it is
called, the return value of C<resFd()> is invalid after a call to
C<resBuffer()>.

You rarely have to use this filehandle directly.  L<TUWF::XML|TUWF::XML>
provides a more convenient set of functions, including C<lit()>, which can be
used to output raw text.

=head2 resStatus(code)

Gets or sets the numeric HTTP response status code.

=head2 resRedirect(location, type)

Generate a HTTP redirect to I<location>, which should be a path relative to the
domain, including leading a slash. If I<type> is not defined, a 301 (Moved
Permanently) is used. If I<type> is 'temp', a 307 (Temporary Redirect) or if
I<type> is 'post' a 303 (See Other) status code is used. The latter is
recommended for use as response to a POST request, as it explicitely tells the
browser to use a GET request for the redirect.

B<Compatibility note>: TUWF 1.1 and earlier called C<resInit()> in
C<resRedirect()>, so if you wished to send any additional headers or cookies,
you had to set these B<after> calling C<resRedirect()>. In TUWF versions after
1.1, you can also set headers and cookies set before calling C<resRedirect()>.

=head2 resJSON(data)

Sets the content type to C<application/json> and sends the encoded JSON object
to the client.

=head2 resBinary(data)

Send binary data to the client. This method throws away any data currently
written to the buffer. This method also disables output compression to prevent
additional copies of the data. Make sure to set the right content type as well,
for example:

  tuwf->resHeader('Content-Type' => 'image/jpeg');
  tuwf->resBinary($image_data);

=head2 resFile(path, filename)

Send the contents of the given file in the given path to the client. This
method returns 1 if the file exists, 0 if it does not. This method ensures that
only files within I<path> are served to the client, preventing unwanted path
traversal.

The I<Content-Type> header is set according to the file extension of
I<filename> and the L<mime_types|TUWF/mime_types> and
L<mime_default|TUWF/mime_types> settings. You can always override this by
calling C<resHeader()> after C<resFile()>.

Examples:

  # Serve 'GET /static/x' requests from /webroot/public
  TUWF::get qr{/static/(.+)} => sub {
    tuwf->resFile('/webroot/public', tuwf->captures(1)) || tuwf->resNotFound;
  };

  # Serve a file in '/webroot/public' if it exists,
  # otherwise handle the request as usual.
  TUWF::hook before => sub {
    return 0 if tuwf->resFile('/webroot/public', tuwf->reqPath);
    return 1;
  };

You might also want to set proper caching headers if the static files don't
change much:

  tuwf->resHeader('Cache-Control' => 'max-age=31536000');

C<resFile()> uses C<resBinary()> internally, so output compression will be
disabled for these files. Furthermore, this method is not really suitable for
sending large files, as the entire files contents will be copied into RAM.
Range requests, I<If-Modified-Since>, I<ETag> and such are also not supported.
If any of this is a concern, it's recommended to configure your webserver to
serve static files directly - without involvement of TUWF.

B<WARNING>: The I<path> argument should not be dependent on any input data from
the client, or you might still make yourself vulnerable to path traversal!

B<WARNING#2>: This method will follow symlinks, so you have to ensure that
there are no malicious symlinks inside I<path>. If, in the above example, there
is a symlink from I</webroot/public/malicious> to I</>, then anyone will be
able to retrieve any file on your system!

=head2 resNotFound()

Return a 404 Page Not Found response. The response will be exactly the same as
the 404 response used when the path did not match any of the registered URI
handles. This means the callback configured with
L<error_404_handler|TUWF/error_404_handler> will be called.

B<Important:> Do B<NOT> call this function from your C<error_404_handler>
function, this will cause infinite recursion!


=head1 SEE ALSO

L<TUWF>


=head1 COPYRIGHT

Copyright (c) 2008-2017 Yoran Heling.

This module is part of the TUWF framework and is free software available under
the liberal MIT license. See the COPYING file in the TUWF distribution for the
details.


=head1 AUTHOR

Yoran Heling <projects@yorhel.nl>

=cut