Skip to content

Commit 3388362

Browse files
committed
[en] massive wording improvements in "Nginx Variables (06)".
1 parent 0c9b88f commit 3388362

File tree

1 file changed

+122
-124
lines changed

1 file changed

+122
-124
lines changed

en/01-NginxVariables06.tut

Lines changed: 122 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,21 @@
11
= Nginx Variables (06) =
22

3-
There are a few subtleties with Nginx builtin variable in the context of
4-
subrequest.
5-
6-
We have learnt from L<vartut/ (03)>, many builtin variables are not simple
7-
value
8-
containers, they become special because the variables are hooked with "
9-
get/set handler".
10-
Even if a value container can be opt-in, it is used as cache to avoid repeated
11-
calculations.
12-
L<ngx_core/$args> is such a variable, it uses "get/set handler" to calculate
13-
the URL
14-
parameter string for the current request. When it is a subrequest, L<ngx_core/$args>
15-
should be calculated accordingly and return the subrequest's URL parameter
16-
string. We
17-
can check it with following example:
3+
== Built-in Variables in Subrequests ==
4+
5+
There are some subtleties involved in using Nginx built-in variables in the
6+
context of a subrequest. We will discuss the details in this section.
7+
8+
=== Built-in Variables Sensitive to the Subrequest Context ===
9+
10+
We already know that most built-in variables are not simple value containers.
11+
They behave differently than user variables by registering "get
12+
handlers" and/or "set handlers". Even when they do own a value container, they
13+
usually just use the container as a result cache for their "get handlers". The
14+
L<ngx_core/$args> variable we discussed earlier, for example, just uses its
15+
"get handler" to return the URL query string for the current request. The
16+
current request here can also be a subrequest, so when reading
17+
L<ngx_core/$args> in a subrequest, its "get handler" should naturally return
18+
the query string for the subrequest. Let's see such an example:
1819

1920
:nginx
2021
location /main {
@@ -26,30 +27,28 @@ can check it with following example:
2627
echo "sub args: $args";
2728
}
2829

29-
We print the value of variable L<ngx_core/$args> in C<location /main> by
30-
command
31-
L<ngx_echo/echo>, then we issue a subrequest to C<location /sub> by command
32-
L<ngx_echo/echo_location>.
33-
You might have noticed that the subrequest is given a second parameter,
34-
which designates
35-
the URL parameters (C<a=1&b=2>). Again in C<location /sub> directive, the
36-
value of L<ngx_core/$args>
37-
is printed.
30+
Here in the C</main> interface, we first echo out the value of
31+
L<ngx_core/$args> for the current request, and then use
32+
L<ngx_echo/echo_location> to initiate a subrequest to C</sub>. It should be
33+
noted that here we give a second argument to the L<ngx_echo/echo_location>
34+
directive, to specify the URL query string for the subrequest being fired (the
35+
first argument is the URI for the subrequest, as we already know). Finally, we
36+
define the C</sub> interface and print out the value of L<ngx_core/$args> in
37+
there. Querying the C</main> interface gives
3838

3939
:bash
4040
$ curl 'http://localhost:8080/main?c=3'
4141
main args: c=3
4242
sub args: a=1&b=2
4343

44-
Clearly, L<ngx_core/$args> prints C<c=3> in C<location /main>, which is
45-
the URL parameter for the
46-
main request. And it prints C<a=1&b=2> in C<location /sub>, which is the
47-
URL parameter for the subrequest.
48-
These are exactly what we were expecting.
44+
It is clear that when L<ngx_core/$args> is read in the main request (to
45+
C</main>), its value is the URL query string of the main request; whereas when
46+
in the subrequest (to C</foo>), it is the query string of the subrequest,
47+
C<a=1&b=2>. This behavior indeed matches our intuition.
4948

50-
Just like L<ngx_core/$args>, builtin variable L<ngx_core/$uri> is calculated
51-
as subrequest's URI in
52-
subrequest context.
49+
Just like L<ngx_core/$args>, when the built-in variable L<ngx_core/$uri> is
50+
used in a subrequest, its "get handler" also returns the (decoded) URI of the
51+
current subrequest:
5352

5453
:nginx
5554
location /main {
@@ -61,27 +60,26 @@ subrequest context.
6160
echo "sub uri: $uri";
6261
}
6362

64-
Sending request to C<location /main> we have:
63+
Below is the result of querying C</main>:
6564

6665
:bash
6766
$ curl 'http://localhost:8080/main'
6867
main uri: /main
6968
sub uri: /sub
7069

71-
The result is what we'd expected.
70+
The output is what we would expect.
7271

73-
Reality is imperfect, not all the builtin variable is calculated from current
74-
request, a minority of builtin variables only calculate their values from
75-
the
76-
main request. Builtin variable L<ngx_core/$request_method>, provided by
77-
module
78-
L<ngx_http_core>, is one of them.
72+
=== Built-in Variables for Main Requests Only ===
7973

80-
When variable L<ngx_core/$request_method> is devalued, the HTTP method
81-
of "main
82-
request" is always obtained. The HTTP method can be C<GET>, C<POST> etc,
83-
let's
84-
test it:
74+
Unfortunately, not all built-in variables are sensitive to the context of
75+
subrequests. Several built-in variables always act on the main request even
76+
when they are used in a subrequest. The built-in variable
77+
L<ngx_core/$request_method> is such an exception.
78+
79+
Whenever L<ngx_core/$request_method> is read, we always get the request method
80+
name (such as C<GET> and C<POST>) for the main request, no matter whether the
81+
current request is a
82+
subrequest or not. Let's test it out:
8583

8684
:nginx
8785
location /main {
@@ -93,39 +91,39 @@ test it:
9391
echo "sub method: $request_method";
9492
}
9593

96-
For this case, variable L<ngx_core/$request_method> is printed in both
97-
C<location /main> and C<location /sub>. Subrequest is initiated to C<location
98-
/sub>
99-
from within C<location /main> by L<ngx_echo/echo_location>. Again we use
100-
the C<curl> utility to send a C<POST> request to C<location /main>
94+
In this example, the C</main> and C</sub> interfaces both output the value of
95+
L<ngx_core/$request_method>. Meanwhile, we initiate a C<GET> subrequest to
96+
C</sub> via the L<ngx_echo/echo_location> directive in C</main>. Now let's do a
97+
C<POST> request to C</main>:
10198

10299
:bash
103100
$ curl --data hello 'http://localhost:8080/main'
104101
main method: POST
105102
sub method: POST
106103

107-
Command C<curl> has a C<--data> option, which designates the request data,
108-
meanwhile it lets the request use HTTP method C<POST>. The test shows no
109-
surprises, no matter where L<ngx_core/$request_method> is devalued, the
110-
HTTP method of main request is obtained: C<POST>.
111-
112-
Hey, would it be the case where variables are devalued and cached for variable
113-
L<ngx_core/$request_method> ? so that it is calculated in "main request"
114-
and
115-
referenced again in the "subrequest" ? Think again. We have learnt back
116-
in
117-
L<vartut/ (05)>, each request has its own copies of variables. Module L<ngx_echo>
118-
fully respects this rule and the subrequest it initiates forbids the variable
119-
referencing its counterpart in parent request. So back to our example,
120-
even if
121-
the calculation has been cached (in fact nothing is cached here), it shall
122-
have
123-
no impact on the variables on subrequest to C<location /sub>.
124-
125-
We can adjust our example a little bit, i.e. to print L<ngx_core/$request_method>
126-
after the subrequest has been initiated in C<location /main>. This helps
127-
answering
128-
above question.
104+
Here we use the C<--data> option of the C<curl> utility to specify our POST
105+
request body, also this option makes C<curl> use the C<POST>
106+
method for the request. The test result turns out as we predicted:
107+
the variable L<ngx_core/$request_method> is evaluated to the main request's
108+
method name, C<POST>, despite its use in a C<GET> subrequest.
109+
110+
Some readers might challenge our conclusion here by pointing out that we did
111+
not rule out the possibility that the value of L<ngx_core/$request_method> got
112+
cached at its first reading in the main request and what we were seeing in the
113+
subrequest was actually the cached value that was evaluated earlier in the main
114+
request. This concern is unnecessary, however, because we have also learned
115+
that the variable container required by data caching (if any) is always bound
116+
to the
117+
current request, also the subrequests initiated by the L<ngx_echo> module
118+
always disable variable container sharing with their parent requests.
119+
Back to the previous example, even if the built-in variable
120+
L<ngx_core/$request_method> in the main request used the value container as the
121+
data cache (actually it does not), it cannot affect the subrequest by any means.
122+
123+
To further address the concern of these readers, let's slightly modify the
124+
previous example by putting the L<ngx_echo/echo> statement for
125+
L<ngx_core/$request_method> in C</main> I<after> the L<ngx_echo/echo_location>
126+
directive that runs the subrequest:
129127

130128
:nginx
131129
location /main {
@@ -137,21 +135,22 @@ above question.
137135
echo "sub method: $request_method";
138136
}
139137

140-
Test again:
138+
Let's test it again:
141139

142140
:bash
143141
$ curl --data hello 'http://localhost:8080/main'
144142
sub method: POST
145143
main method: POST
146144

147-
The result is almost same as before, except the ordering of prints has
148-
been reversed for parent request and subrequest. (because we reversed the
149-
statements in C<location /main>.
145+
No change in the output can be observed, except that the two output lines
146+
reversed the order (since we exchange the order of those two L<ngx_echo>
147+
module's directives).
150148

151-
So we cannot correctly retrieve the HTTP method of subrequest by evaluating
152-
L<ngx_core/$request_method>, yet we can use 3rd party module L<ngx_echo>
153-
and
154-
its variable L<ngx_echo/$echo_request_method> for the purpose.
149+
Consequently, we cannot obtain the method name of a subrequest by reading the
150+
L<ngx_core/$request_method> variable. This is a common pitfall for freshmen
151+
when dealing with method names of subrequests. To overcome this limitation, we
152+
need to turn to the built-in variable L<ngx_echo/$echo_request_method> provided
153+
by the L<ngx_echo> module:
155154

156155
:nginx
157156
location /main {
@@ -163,30 +162,32 @@ its variable L<ngx_echo/$echo_request_method> for the purpose.
163162
echo "sub method: $echo_request_method";
164163
}
165164

166-
Finally this is what we'd have wanted:
165+
We are finally getting what we want:
167166

168167
:bash
169168
$ curl --data hello 'http://localhost:8080/main'
170169
main method: POST
171170
sub method: GET
172171

173-
So parent request prints C<POST>, and subrequest prints C<GET>. Each
174-
reflects its own HTTP method.
172+
Now within the subrequest, we get its own method name, C<GET>, as expected, and
173+
the main request method remains C<POST>.
174+
175+
Similar to L<ngx_core/$request_method>, the built-in variable
176+
L<ngx_core/$request_uri> also always returns the (non-decoded) URL for the main
177+
request. This is more understandable, however, because subrequests are
178+
essentially faked requests inside Nginx, which do not really take a non-decoded
179+
raw URL.
175180

176-
Builtin variable L<ngx_core/$request_uri>, like L<ngx_core/$request_method>,
177-
always returns the encoded URL of "main request" no matter the context
178-
it
179-
is evaluated. This is normal since subrequests are Nginx internal abstractions
180-
in which an encoded request URL has no specific meanings.
181+
=== Variable Container Sharing and Value Caching Together ===
181182

182-
If it were the case you would have worried, that builtin variable is cached
183-
in between parent request and subrequests, it is as bitter as hell. Since
184-
we
185-
have already learnt in L<vartut/ (05)> that module L<ngx_auth_request>
186-
allows
187-
its subrequest to share the same copy of variables with its parent request,
188-
let's
189-
have guts for the following dreadful case:
183+
In the previous section, some of the readers were worried about the case that
184+
variable container sharing in subrequests and value caching for variable's "get
185+
handlers" were working together. If it were indeed the case, then it would be a
186+
nightmare because it would be really really hard to predict what is going on by
187+
just looking at the configuration file. In previous sections, we already
188+
learned that the subrequests initiated by the L<ngx_auth_request> module are
189+
sharing the same variable containers with their parents, so we can maliciously
190+
construct such a horrible example:
190191

191192
:nginx
192193
map $uri $tag {
@@ -208,36 +209,33 @@ have guts for the following dreadful case:
208209
}
209210
}
210211

211-
Our old friend L<ngx_map/map> defines mapping rules from builtin variable
212-
L<ngx_core/$uri> to user variable C<$tag>. The rules are: C<$tag> is 1
213-
if
214-
L<ngx_core/$uri> is C</main>, 2 if it is C</sub>, 0 for all the other cases.
215-
Then subrequest is initiated to <location /sub> by using module L<ngx_auth_requst>
216-
and its command L<ngx_auth_request/auth_request>. After the subrequest
217-
is
218-
handled, variable C<$tag> is printed. Guess what happens on the output
219-
if
220-
we request to C<location /main> ?
212+
Here we use our old friend, the L<ngx_map/map> directive, to map the value of
213+
the built-in variable L<ngx_core/$uri> to our user variable C<$tag>. When
214+
L<ngx_core/$uri> takes the value C</main>, the value C<1> is assigned to
215+
C<$tags>; when L<ngx_core/$uri> takes the value C</sub>, the value C<2> is
216+
assigned instead to C<$tags>; under all the other conditions, C<0> is assigned.
217+
Next, in C</main>, we first initiate a subrequest to C</sub> by using the
218+
L<ngx_auth_request/auth_request> directive, and then output the value of
219+
C<$tag>. And within C</sub>, we directly output the value of C<$tag>. Guess
220+
what we will get when we access C</main>?
221221

222222
$ curl 'http://localhost:8080/main'
223223
main tag: 2
224224

225-
eh ? did the mapping rules says C<$tag> is 1 when L<ngx_core/$uri> is C</main>
226-
?
227-
why it is 2 as if </sub> is requested ?
228-
229-
Hold on, this is because variable C<$tag> is first devalued in subrequest
230-
to C</sub>,
231-
it is mapped as C<2> under that context (L<ngx_core/$uri> becomes C<sub>,
232-
so mapping rule
233-
deducts the value accordingly as C<2>). Mapping result is cached, even
234-
worse, the subrequest
235-
initiated by L<ngx_auth_request/auth_request> shares the same copy of variables
236-
in between
237-
parent and subrequests. So Nginx returns the cached value C<2> when variable
238-
C<$tag> is
239-
devalued back in the parent request. What a big deal.
240-
241-
Admittedly, think twice if we design to share variables in between parent
242-
request and subrequests.
225+
Ouch! Didn't we map the value C</main> to C<1>? Why the actual output for
226+
C</main> is the value, C<2>, for C</sub>? What is going on here?
227+
228+
Actually it worked like this: our C<$tag> variable was first read in the
229+
subrequest to C</sub>, and the "get handler" registered by L<ngx_map/map>
230+
computed the value C<2> for C<$tag> in that context (because L<ngx_core/$uri>
231+
was C</sub> in the subrequest) and the value C<2> got cached in the value
232+
container of C<$tag> from then on. Because the parent request shared the same
233+
container as the subrequest created by L<ngx_auth_request/auth_request>, when
234+
the parent request read C<$tag> later (after the subrequest was finished), the
235+
cached value C<2> was directly returned! Such results can indeed be very
236+
surprising at first glance.
237+
238+
From this example, we can conclude again that it can hardly be a good idea to
239+
enable
240+
variable container sharing in subrequests.
243241

0 commit comments

Comments
 (0)