@@ -125,33 +125,49 @@ impl Resolver {
125
125
/// The set of addresses may not be deterministic, because the
126
126
/// implementation of the resolver may race multiple DNS requests.
127
127
pub async fn resolve ( & self , name : & ngx_str_t , pool : & Pool ) -> Res {
128
- let ctx = unsafe {
129
- NonNull :: new ( ngx_resolve_start (
130
- self . resolver . as_ptr ( ) ,
131
- core:: ptr:: null_mut ( ) ,
132
- ) )
133
- . ok_or ( Error :: AllocationFailed ) ?
134
- } ;
135
-
136
- let mut resolver = Resolution :: new ( name, pool, self . timeout , ctx) ?;
128
+ let mut resolver = Resolution :: new ( name, pool, self . resolver , self . timeout ) ?;
137
129
resolver. as_mut ( ) . await
138
130
}
139
131
}
140
132
141
133
struct Resolution < ' a > {
134
+ // Storage for the result of the resolution `Res`. Populated by the
135
+ // callback handler, and taken by the Future::poll impl.
142
136
complete : Option < Res > ,
137
+ // Storage for a pending Waker. Populated by the Future::poll impl,
138
+ // and taken by the callback handler.
143
139
waker : Option < Waker > ,
140
+ // Pool used for allocating `Vec<ngx_addr_t>` contents in `Res`. Read by
141
+ // the callback handler.
144
142
pool : & ' a Pool ,
143
+ // Pointer to the ngx_resolver_ctx_t. Resolution constructs this with
144
+ // ngx_resolver_name_start in the constructor, and is responsible for
145
+ // freeing it, with ngx_resolver_name_done, once it is no longer needed -
146
+ // this happens in either the callback handler, or the drop impl. Calling
147
+ // ngx_resolver_name_done before the callback fires ensure nginx does not
148
+ // ever call the callback.
145
149
ctx : Option < NonNull < ngx_resolver_ctx_t > > ,
146
150
}
147
151
148
152
impl < ' a > Resolution < ' a > {
149
153
fn new (
150
154
name : & ngx_str_t ,
151
155
pool : & ' a Pool ,
156
+ resolver : NonNull < ngx_resolver_t > ,
152
157
timeout : ngx_msec_t ,
153
- mut ctx : NonNull < ngx_resolver_ctx_t > ,
154
158
) -> Result < Pin < Box < Self > > , Error > {
159
+ let ctx = unsafe {
160
+ // Start a new resolver context. This implementation currently
161
+ // passes a null for the second argument `temp`. A non-null `temp`
162
+ // provides a fast, non-callback-based path for immediately
163
+ // returning an addr iff `temp` contains a name which is textual
164
+ // form of an addr.
165
+ let ctx = ngx_resolve_start ( resolver. as_ptr ( ) , core:: ptr:: null_mut ( ) ) ;
166
+ NonNull :: new ( ctx) . ok_or ( Error :: AllocationFailed ) ?
167
+ } ;
168
+
169
+ // Create a pinned Resolution on the heap, so that we can make
170
+ // a stable pointer to the Resolution struct.
155
171
let mut this = Pin :: new ( Box :: new ( Resolution {
156
172
complete : None ,
157
173
waker : None ,
@@ -160,15 +176,26 @@ impl<'a> Resolution<'a> {
160
176
} ) ) ;
161
177
162
178
{
179
+ // Set up the ctx with everything the resolver needs to resolve a
180
+ // name, and the handler callback which is called on completion.
163
181
let ctx: & mut ngx_resolver_ctx_t = unsafe { ctx. as_mut ( ) } ;
164
182
ctx. name = * name;
165
183
ctx. timeout = timeout;
166
184
ctx. set_cancelable ( 1 ) ;
167
185
ctx. handler = Some ( Self :: handler) ;
186
+ // Safety: Self::handler, Future::poll, and Drop::drop will have
187
+ // access to &mut Resolution. Nginx is single-threaded and we are
188
+ // assured only one of those is on the stack at a time, except if
189
+ // Self::handler wakes a task which polls or drops the Future,
190
+ // which it only does after use of &mut Resolution is complete.
168
191
let ptr: & mut Resolution = unsafe { Pin :: into_inner_unchecked ( this. as_mut ( ) ) } ;
169
192
ctx. data = ptr as * mut Resolution as * mut c_void ;
170
193
}
171
194
195
+ // Start name resolution using the ctx. If the name is in the dns
196
+ // cache, the handler may get called from this stack. Otherwise, it
197
+ // will be called later by nginx when it gets a dns response or a
198
+ // timeout.
172
199
let ret = unsafe { ngx_resolve_name ( ctx. as_ptr ( ) ) } ;
173
200
if ret != 0 {
174
201
return Err ( Error :: Resolver (
@@ -180,18 +207,26 @@ impl<'a> Resolution<'a> {
180
207
Ok ( this)
181
208
}
182
209
210
+ // Nginx will call this handler when name resolution completes. If the
211
+ // result is cached, this could be
183
212
unsafe extern "C" fn handler ( ctx : * mut ngx_resolver_ctx_t ) {
184
213
let mut data = unsafe { NonNull :: new_unchecked ( ( * ctx) . data as * mut Resolution ) } ;
185
214
let this: & mut Resolution = unsafe { data. as_mut ( ) } ;
186
215
this. complete = Some ( Self :: resolve_result ( ctx, this. pool ) ) ;
187
- this. ctx . take ( ) ;
216
+
217
+ let mut ctx = this. ctx . take ( ) . expect ( "ctx must be present" ) ;
218
+ unsafe { nginx_sys:: ngx_resolve_name_done ( ctx. as_mut ( ) ) } ;
219
+
220
+ // Wake last, after all use of &mut Resolution, because wake may
221
+ // poll Resolution future on current stack.
188
222
if let Some ( waker) = this. waker . take ( ) {
189
- // Wake last, after all use of &mut Resolution, because wake may
190
- // poll Resolution future on current stack.
191
223
waker. wake ( ) ;
192
224
}
193
225
}
194
226
227
+ /// Take the results in a ctx and make an owned copy as a
228
+ /// Result<Vec<ngx_addr_t>, Error>, where both the Vec and internals of
229
+ /// the ngx_addr_t are allocated on the given Pool
195
230
fn resolve_result ( ctx : * mut ngx_resolver_ctx_t , pool : & Pool ) -> Res {
196
231
let ctx = unsafe { ctx. as_ref ( ) . unwrap ( ) } ;
197
232
let s = ctx. state ;
@@ -211,6 +246,8 @@ impl<'a> Resolution<'a> {
211
246
Ok ( out)
212
247
}
213
248
249
+ /// Take the contents of an ngx_resolver_addr_t and make an owned copy as
250
+ /// an ngx_addr_t, using the Pool for allocation of the internals.
214
251
fn copy_resolved_addr (
215
252
addr : * mut nginx_sys:: ngx_resolver_addr_t ,
216
253
pool : & Pool ,
@@ -247,9 +284,12 @@ impl<'a> core::future::Future for Resolution<'a> {
247
284
type Output = Result < Vec < ngx_addr_t > , Error > ;
248
285
fn poll ( mut self : Pin < & mut Self > , cx : & mut Context < ' _ > ) -> Poll < Self :: Output > {
249
286
let mut this = self . as_mut ( ) ;
287
+ // The handler populates this.complete, and we consume it here:
250
288
match this. complete . take ( ) {
251
289
Some ( res) => Poll :: Ready ( res) ,
252
290
None => {
291
+ // If the handler has not yet fired, populate the waker field,
292
+ // which the handler will consume:
253
293
match & mut self . waker {
254
294
None => {
255
295
self . waker = Some ( cx. waker ( ) . clone ( ) ) ;
@@ -264,6 +304,9 @@ impl<'a> core::future::Future for Resolution<'a> {
264
304
265
305
impl < ' a > Drop for Resolution < ' a > {
266
306
fn drop ( & mut self ) {
307
+ // ctx is taken and freed if the Resolution reaches the handler
308
+ // callback, but if dropped before that callback, this will cancel any
309
+ // ongoing work as well as free the ctx memory.
267
310
if let Some ( mut ctx) = self . ctx . take ( ) {
268
311
unsafe {
269
312
nginx_sys:: ngx_resolve_name_done ( ctx. as_mut ( ) ) ;
0 commit comments