-
Notifications
You must be signed in to change notification settings - Fork 437
/
Copy path01-NginxVariables06.tut
241 lines (192 loc) · 8.99 KB
/
01-NginxVariables06.tut
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
= Nginx Variables (06) =
== Built-in Variables in Subrequests ==
There are some subtleties involved in using Nginx built-in variables in the
context of a subrequest. We will discuss the details in this section.
=== Built-in Variables Sensitive to the Subrequest Context ===
We already know that most built-in variables are not simple value containers.
They behave differently than user variables by registering "get
handlers" and/or "set handlers". Even when they do own a value container, they
usually just use the container as a result cache for their "get handlers". The
L<ngx_core/$args> variable we discussed earlier, for example, just uses its
"get handler" to return the URL query string for the current request. The
current request here can also be a subrequest, so when reading
L<ngx_core/$args> in a subrequest, its "get handler" should naturally return
the query string for the subrequest. Let's see such an example:
:nginx
location /main {
echo "main args: $args";
echo_location /sub "a=1&b=2";
}
location /sub {
echo "sub args: $args";
}
Here in the C</main> interface, we first echo out the value of
L<ngx_core/$args> for the current request, and then use
L<ngx_echo/echo_location> to initiate a subrequest to C</sub>. It should be
noted that here we give a second argument to the L<ngx_echo/echo_location>
directive, to specify the URL query string for the subrequest being fired (the
first argument is the URI for the subrequest, as we already know). Finally, we
define the C</sub> interface and print out the value of L<ngx_core/$args> in
there. Querying the C</main> interface gives
:bash
$ curl 'http://localhost:8080/main?c=3'
main args: c=3
sub args: a=1&b=2
It is clear that when L<ngx_core/$args> is read in the main request (to
C</main>), its value is the URL query string of the main request; whereas when
in the subrequest (to C</foo>), it is the query string of the subrequest,
C<a=1&b=2>. This behavior indeed matches our intuition.
Just like L<ngx_core/$args>, when the built-in variable L<ngx_core/$uri> is
used in a subrequest, its "get handler" also returns the (decoded) URI of the
current subrequest:
:nginx
location /main {
echo "main uri: $uri";
echo_location /sub;
}
location /sub {
echo "sub uri: $uri";
}
Below is the result of querying C</main>:
:bash
$ curl 'http://localhost:8080/main'
main uri: /main
sub uri: /sub
The output is what we would expect.
=== Built-in Variables for Main Requests Only ===
Unfortunately, not all built-in variables are sensitive to the context of
subrequests. Several built-in variables always act on the main request even
when they are used in a subrequest. The built-in variable
L<ngx_core/$request_method> is such an exception.
Whenever L<ngx_core/$request_method> is read, we always get the request method
name (such as C<GET> and C<POST>) for the main request, no matter whether the
current request is a
subrequest or not. Let's test it out:
:nginx
location /main {
echo "main method: $request_method";
echo_location /sub;
}
location /sub {
echo "sub method: $request_method";
}
In this example, the C</main> and C</sub> interfaces both output the value of
L<ngx_core/$request_method>. Meanwhile, we initiate a C<GET> subrequest to
C</sub> via the L<ngx_echo/echo_location> directive in C</main>. Now let's do a
C<POST> request to C</main>:
:bash
$ curl --data hello 'http://localhost:8080/main'
main method: POST
sub method: POST
Here we use the C<--data> option of the C<curl> utility to specify our POST
request body, also this option makes C<curl> use the C<POST>
method for the request. The test result turns out as we predicted:
the variable L<ngx_core/$request_method> is evaluated to the main request's
method name, C<POST>, despite its use in a C<GET> subrequest.
Some readers might challenge our conclusion here by pointing out that we did
not rule out the possibility that the value of L<ngx_core/$request_method> got
cached at its first reading in the main request and what we were seeing in the
subrequest was actually the cached value that was evaluated earlier in the main
request. This concern is unnecessary, however, because we have also learned
that the variable container required by data caching (if any) is always bound
to the
current request, also the subrequests initiated by the L<ngx_echo> module
always disable variable container sharing with their parent requests.
Back to the previous example, even if the built-in variable
L<ngx_core/$request_method> in the main request used the value container as the
data cache (actually it does not), it cannot affect the subrequest by any means.
To further address the concern of these readers, let's slightly modify the
previous example by putting the L<ngx_echo/echo> statement for
L<ngx_core/$request_method> in C</main> I<after> the L<ngx_echo/echo_location>
directive that runs the subrequest:
:nginx
location /main {
echo_location /sub;
echo "main method: $request_method";
}
location /sub {
echo "sub method: $request_method";
}
Let's test it again:
:bash
$ curl --data hello 'http://localhost:8080/main'
sub method: POST
main method: POST
No change in the output can be observed, except that the two output lines
reversed the order (since we exchange the order of those two L<ngx_echo>
module's directives).
Consequently, we cannot obtain the method name of a subrequest by reading the
L<ngx_core/$request_method> variable. This is a common pitfall for freshmen
when dealing with method names of subrequests. To overcome this limitation, we
need to turn to the built-in variable L<ngx_echo/$echo_request_method> provided
by the L<ngx_echo> module:
:nginx
location /main {
echo "main method: $echo_request_method";
echo_location /sub;
}
location /sub {
echo "sub method: $echo_request_method";
}
We are finally getting what we want:
:bash
$ curl --data hello 'http://localhost:8080/main'
main method: POST
sub method: GET
Now within the subrequest, we get its own method name, C<GET>, as expected, and
the main request method remains C<POST>.
Similar to L<ngx_core/$request_method>, the built-in variable
L<ngx_core/$request_uri> also always returns the (non-decoded) URL for the main
request. This is more understandable, however, because subrequests are
essentially faked requests inside Nginx, which do not really take a non-decoded
raw URL.
=== Variable Container Sharing and Value Caching Together ===
In the previous section, some of the readers were worried about the case that
variable container sharing in subrequests and value caching for variable's "get
handlers" were working together. If it were indeed the case, then it would be a
nightmare because it would be really really hard to predict what is going on by
just looking at the configuration file. In previous sections, we already
learned that the subrequests initiated by the L<ngx_auth_request> module are
sharing the same variable containers with their parents, so we can maliciously
construct such a horrible example:
:nginx
map $uri $tag {
default 0;
/main 1;
/sub 2;
}
server {
listen 8080;
location /main {
auth_request /sub;
echo "main tag: $tag";
}
location /sub {
echo "sub tag: $tag";
}
}
Here we use our old friend, the L<ngx_map/map> directive, to map the value of
the built-in variable L<ngx_core/$uri> to our user variable C<$tag>. When
L<ngx_core/$uri> takes the value C</main>, the value C<1> is assigned to
C<$tag>; when L<ngx_core/$uri> takes the value C</sub>, the value C<2> is
assigned instead to C<$tag>; under all the other conditions, C<0> is assigned.
Next, in C</main>, we first initiate a subrequest to C</sub> by using the
L<ngx_auth_request/auth_request> directive, and then output the value of
C<$tag>. And within C</sub>, we directly output the value of C<$tag>. Guess
what we will get when we access C</main>?
$ curl 'http://localhost:8080/main'
main tag: 2
Ouch! Didn't we map the value C</main> to C<1>? Why the actual output for
C</main> is the value, C<2>, for C</sub>? What is going on here?
Actually it worked like this: our C<$tag> variable was first read in the
subrequest to C</sub>, and the "get handler" registered by L<ngx_map/map>
computed the value C<2> for C<$tag> in that context (because L<ngx_core/$uri>
was C</sub> in the subrequest) and the value C<2> got cached in the value
container of C<$tag> from then on. Because the parent request shared the same
container as the subrequest created by L<ngx_auth_request/auth_request>, when
the parent request read C<$tag> later (after the subrequest was finished), the
cached value C<2> was directly returned! Such results can indeed be very
surprising at first glance.
From this example, we can conclude again that it can hardly be a good idea to
enable
variable container sharing in subrequests.