Skip to content

Commit 0c9b88f

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

File tree

1 file changed

+108
-112
lines changed

1 file changed

+108
-112
lines changed

en/01-NginxVariables05.tut

Lines changed: 108 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,37 @@
11
= Nginx Variables (05) =
22

3-
In L<vartut/ (02)> we learnt that variable life cycle is bounded with
4-
the request, but i own you a formal definition of "request". Without further
5-
explaining, you might have assumed that "request" refers to the HTTP request
6-
initiated by the client. But, there are actually two types of "request"
7-
in Nginx
8-
world. One is called "main request", the other "subrequest". Now let's
9-
wire them in.
10-
11-
The "main request" is the request initiated by HTTP client from outside
12-
Nginx.
13-
All requests we've been discussed are "main request", including the internal
14-
"jump"
15-
introduced in L<vartut/ 02), which uses command L<ngx_echo/echo_exec> and
16-
L<ngx_rewrite/rewrite>
17-
18-
Whereas the "subrequest" is a type of cascading request issued by Nginx
19-
from within
20-
its internals. "Subrequest" is encapsulated just like HTTP request, but
21-
it has absolutely
22-
nothing to do with HTTP protocol and networking. Subrequest is a useful
23-
abstraction in
24-
Nginx design, to ease the decomposition of main request into a few "internal
25-
requests" with
26-
finer granularity. Subrequest can be issued to multiple C<location> interfaces
27-
sequentially
28-
or in parallel, and handles the main request by collaborating their respective
29-
outcomes.
30-
Certainly, subrequest can be further decomposed into other subrequests,
31-
subrequest can even
32-
form a recursive pattern by cascade the request to itself. When a request
33-
issues its subrequests,
34-
it is called "parent request" in Nginx terminology. As a footnote, for
35-
those who had wet themselves
36-
with Apache, subrequest is defined equivalently and not totally a stranger.
37-
38-
Let's check a subrequest example:
3+
== Variables in Subrequests ==
4+
5+
=== A Detour to Subrequests ===
6+
7+
We have seen earlier that the lifetime of variable containers is bound to the
8+
request, but I own you a formal definition of "requests" there. You might have
9+
assumed that the "requests" in that context are just those HTTP requests
10+
initiated from the client side. In fact, there are two kinds of "requests" in
11+
the Nginx world. One is called "main requests", and the other is called
12+
"subrequests".
13+
14+
Main requests are those initiated externally by HTTP clients. All the examples
15+
that we have seen so far involve main requests only, including those doing
16+
"internal redirections" via the L<ngx_echo/echo_exec> or L<ngx_rewrite/rewrite>
17+
directive.
18+
19+
Whereas subrequests are a special kind of requests initiated from within the
20+
Nginx core. But please do not confuse subrequests with those HTTP requests
21+
created by the L<ngx_proxy> modules! Subrequests may look very much like an
22+
HTTP request in appearance, their implementation, however, has nothing to do
23+
with neither the HTTP protocol nor any kind of socket communication. A
24+
subrequest is an abstract invocation for decomposing the task of the main
25+
request into smaller "internal requests" that can be served independently by
26+
multiple different C<location> blocks, either in series or in parallel.
27+
"Subrequests" can also be recursive: any subrequest can initiate more
28+
sub-subrequests, targeting other C<location> blocks or even the current
29+
C<location> itself. According to Nginx's terminology, if request A initiates a
30+
subrequest B, then A is called the "parent request" of B. It is worth
31+
mentioning that the Apache web server also has the concept of subrequests for
32+
long, so readers coming from that world should be no stranger to this.
33+
34+
Let's check out an example using subrequests:
3935

4036
:nginx
4137
location /main {
@@ -51,37 +47,37 @@ Let's check a subrequest example:
5147
echo bar;
5248
}
5349

54-
Now in C<location /main>, two subrequests are sent to C</foo> and C</bar>
55-
via 3rd party module L<ngx_echo> and its command L<ngx_echo/echo_location>.
56-
The subrequests are of type HTTP C<GET>. When they are sent by L<ngx_echo/echo_location>
57-
the requests are executed sequentially, in the order of their writing,
58-
which
59-
means, C</bar> is sub-requested only when C</foo> has finished its part.
60-
And
61-
outcome from the subrequests will be concatenated as the final response
62-
of C</main>
50+
Here in C<location /main>, we use the L<ngx_echo/echo_location> directive from
51+
the L<ngx_echo> module to initiate two C<GET>-typed subrequests targeting
52+
C</foo> and C</bar>, respectively. The subrequests initiated by
53+
L<ngx_echo/echo_location> are always running sequentially according to their
54+
literal order in the configuration file. Therefore, the second C</bar> request
55+
will not be fired until the first C</foo> request completes processing. The
56+
response body of these two subrequests get concatenated together according to
57+
their running order, to form the final response body of their parent request
58+
(for C</main>):
6359

6460
:bash
6561
$ curl 'http://localhost:8080/main'
6662
foo
6763
bar
6864

69-
As we can tell, subrequest is issued within a virtual server, i.e. subrequest
70-
handling is implemented as a few C API calls, without any networking nor
71-
UNIX
72-
socket, therefore subrequest execution is extremely fast.
73-
74-
Back to the very initial subject about Nginx variable life cycle, we can
75-
still
76-
safely conclude that it is bounded with current request and every request
77-
has
78-
an isolated copy of variables. However, the request can be "main request"
79-
as
80-
well as "subrequest". Even if "parent request" and "subrequest" share the
81-
same
82-
variable name, they are virtually different variables. We can verify it
83-
with
84-
following test:
65+
It should be noted that the communication of C<location> blocks via subrequests
66+
is limited within the same C<server> block (i.e., the same virtual server
67+
configuration), so when the Nginx core processes a subrequest, it just calls a
68+
few C functions behind the scene, without doing any kind of network or UNIX
69+
domain socket communication. For this reason, subrequests are extremely
70+
efficient.
71+
72+
=== Independent Variable Containers in Subrequests ===
73+
74+
Back to our earlier discussion for the lifetime of Nginx variable containers,
75+
now we can still state that the lifetime is bound to the current request, and
76+
every request does have its own copy of all the variable containers. It is just
77+
that the "request" here can be either a main request, or a subrequest.
78+
Variables with the same name between a parent request and a subrequest will
79+
generally not interfere with each other. Let's do a small experiment to confirm
80+
this:
8581

8682
:nginx
8783
location /main {
@@ -103,31 +99,30 @@ following test:
10399
echo "bar: $var";
104100
}
105101

106-
In this test, the same variable C<$var> is declared in C<location /main>,
107-
C<location /foo> and C<location /bar> but is initialized with different
108-
values.
109-
Further more, we print the value of variable C<$var> within C</main> after
110-
two subrequests are handled. Then we issue a request to C</main>:
102+
In this sample, we assign different values to the variable C<$var> in three
103+
C<location> blocks, C</main>, C</foo>, and C</bar>, and output the value of
104+
C<$var> in all these locations. In particular, we intentionally output the
105+
value of C<$var> in C<location /main> I<after> calling the two subrequests, so
106+
if value changes of C<$var> in the subrequests can affect their parent request,
107+
we should see a new value output in location C</main>. The result of requesting
108+
C</main> is as follows:
111109

112110
:bash
113111
$ curl 'http://localhost:8080/main'
114112
foo: foo
115113
bar: bar
116114
main: main
117115

118-
Apparently, when the subrequests are handled in C</foo> and C</bar>,
119-
they both have no impact on the value of C<$var> and the main requesting
120-
handling
121-
in C</main>, again subrequests themselves have no impact on each other.
122-
So we
123-
have asserted that "main request" and every other subrequest each has its
124-
own
125-
copies of C<$var>.
116+
Apparently, the assignments to variable C<$var> in those two subrequests do not
117+
affect the main request C</main> at all. This successfully verifies that both
118+
the main request and its subrequests do own different copies of variable
119+
containers.
120+
121+
=== Shared Variable Containers among Requests ===
126122

127-
Unfortunately, there exists exceptions. Some Nginx module might issue
128-
subrequest which references the same copy of variables of their parent
129-
request. The
130-
3rd party module L<ngx_auth_request> is one of them:
123+
Unfortunately, subrequests initiated by certain Nginx modules do share variable
124+
containers with their parent requests, like those initiated by the 3rd-party
125+
module L<ngx_auth_request>. Below is such an example:
131126

132127
:nginx
133128
location /main {
@@ -141,43 +136,44 @@ request. The
141136
echo "sub: $var";
142137
}
143138

144-
Variable C<$var> is declared and initialized with value C<main> in C<location
145-
/main>
146-
then a subrequest is issued via module L<ngx_auth_request> and its command
147-
L<ngx_auth_request/auth_request>, finally we print variable C<$var> using
148-
command L<ngx_echo/echo>.
149-
Now we reset variable C<$var> as C<sub> in C<location /sub> and check what
150-
our test says:
139+
Here in C<location /main>, we first assign the initial value C<main> to
140+
variable C<$var>, then fire a subrequest to C</sub> via the
141+
L<ngx_auth_request/auth_request> directive from the L<ngx_auth_request> module,
142+
and finally output the value of C<$var>. Note that in C<location /sub> we
143+
intentionally overwrite the value of C<$var> to C<sub>. When accessing
144+
C</main>, we get
151145

152146
:bash
153147
$ curl 'http://localhost:8080/main'
154148
main: sub
155149

156-
It says, C<$var> has become C<sub> back in its main request to C<location
157-
/main>, which means
158-
the subrequest issued by L<ngx_auth_request> module shares exactly the
159-
same copy of variables
160-
with its parent request.
161-
162-
Wait a minute, you might exclaim, why the print within subrequest to C<location
163-
/sub> is missing?
164-
good gocha and the answer is simple, command L<ngx_auth_request/auth_request>
165-
ignores the responses
166-
from its subrequest, instead all it cares is the status code from the subrequest
167-
(subrequest is
168-
ecapsulated as HTTP request). When status code is C<2XX>, the handling
169-
of main request continues,
170-
otherwise the handling gets aborted and error is returned. In our example,
171-
subrequest only executes
172-
a print by L<ngx_echo/echo>, which implicitly returns status code C<200>.
173-
174-
Surely it's always easier to share variables in between parent request
175-
and subrequest, like module
176-
L<ngx_auth_request> does. Do expect the side effect and haunted bugs it
177-
consequences when configuration
178-
scales and becomes complicated. It's too hard to trace a variable when
179-
its value get overwritten in
180-
some subrequests. To keep our sanity, modules like L<ngx_echo>, L<ngx_lua>
181-
and L<ngx_srcache> and many
182-
other 3rd party module chooses not to share variables in between requests.
150+
Obviously, the value change of C<$var> in the subrequest to C</sub> does affect
151+
the main request to C</main>. Thus the variable container of C<$var> is indeed
152+
shared between the main request and the subrequest created by the
153+
L<ngx_auth_request> module.
154+
155+
For the previous example, some readers might ask: "why doesn't the response
156+
body of the subrequest appear in the final output?" The answer is simple: it is
157+
just because the L<ngx_auth_request/auth_request> directive discards the
158+
response
159+
body of the subrequest it manages, and only checks the response status code of
160+
the subrequest. When the status code looks good, like C<200>,
161+
L<ngx_auth_request/auth_request> will just allow Nginx continue processing the
162+
main request; otherwise it will immediately abort the main request by
163+
returning a C<403> error page, for example. In our example, the subrequest to
164+
C</sub>
165+
just return a C<200> response implicitly created by the L<ngx_echo/echo>
166+
directive in C<location /sub>.
167+
168+
Even though sharing variable containers among the main request and all its
169+
subrequests could make bidirectional data exchange easier, it could also lead
170+
to unexpected subtle issues that are hard to debug in real-world
171+
configurations. Because users often forget that a variable with the same name
172+
is actually used in some deeply embedded subrequest and just use it for
173+
something else in the main request, this variable could get unexpectedly
174+
modified during processing. Such bad side effects make many 3rd-party modules
175+
like L<ngx_echo>, L<ngx_lua> and
176+
L<ngx_srcache> choose to disable the variable sharing behavior for subrequests
177+
by
178+
default.
183179

0 commit comments

Comments
 (0)