1 /*
2  * Copyright (c) 2010, FRiCKLE Piotr Sikora <info@frickle.com>
3  * Copyright (c) 2009-2010, Yichun Zhang <agentzh@gmail.com>
4  * Copyright (C) 2002-2010, Igor Sysoev
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 #ifndef DDEBUG
29 #define DDEBUG 0
30 #endif
31 
32 #include <nginx.h>
33 
34 #include "ngx_postgres_ddebug.h"
35 #include "ngx_postgres_util.h"
36 
37 
38 /*
39  * All functions in this file are copied directly from ngx_http_upstream.c,
40  * beacuse they are declared as static there.
41  */
42 
43 
44 void
ngx_postgres_upstream_finalize_request(ngx_http_request_t * r,ngx_http_upstream_t * u,ngx_int_t rc)45 ngx_postgres_upstream_finalize_request(ngx_http_request_t *r,
46     ngx_http_upstream_t *u, ngx_int_t rc)
47 {
48 #if defined(nginx_version) && (nginx_version < 1009001)
49     ngx_time_t  *tp;
50 #endif
51 
52     dd("entering");
53 
54     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
55                    "finalize http upstream request: %i", rc);
56 
57     if (u->cleanup) {
58         *u->cleanup = NULL;
59     }
60 
61     if (u->resolved && u->resolved->ctx) {
62         ngx_resolve_name_done(u->resolved->ctx);
63         u->resolved->ctx = NULL;
64     }
65 
66 #if defined(nginx_version) && (nginx_version >= 1009001)
67     if (u->state && u->state->response_time) {
68         u->state->response_time = ngx_current_msec - u->state->response_time;
69 #else
70     if (u->state && u->state->response_sec) {
71         tp = ngx_timeofday();
72         u->state->response_sec = tp->sec - u->state->response_sec;
73         u->state->response_msec = tp->msec - u->state->response_msec;
74 #endif
75 
76         if (u->pipe) {
77             u->state->response_length = u->pipe->read_length;
78         }
79     }
80 
81     if (u->finalize_request) {
82         u->finalize_request(r, rc);
83     }
84 
85     if (u->peer.free) {
86         u->peer.free(&u->peer, u->peer.data, 0);
87     }
88 
89     if (u->peer.connection) {
90 
91 #if 0 /* we don't support SSL at this time, was: (NGX_HTTP_SSL) */
92 
93         /* TODO: do not shutdown persistent connection */
94 
95         if (u->peer.connection->ssl) {
96 
97             /*
98              * We send the "close notify" shutdown alert to the upstream only
99              * and do not wait its "close notify" shutdown alert.
100              * It is acceptable according to the TLS standard.
101              */
102 
103             u->peer.connection->ssl->no_wait_shutdown = 1;
104 
105             (void) ngx_ssl_shutdown(u->peer.connection);
106         }
107 #endif
108 
109         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
110                        "close http upstream connection: %d",
111                        u->peer.connection->fd);
112 
113 #if defined(nginx_version) && (nginx_version >= 1001004)
114         if (u->peer.connection->pool) {
115             ngx_destroy_pool(u->peer.connection->pool);
116         }
117 #endif
118 
119         ngx_close_connection(u->peer.connection);
120     }
121 
122     u->peer.connection = NULL;
123 
124     if (u->pipe && u->pipe->temp_file) {
125         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
126                        "http upstream temp fd: %d",
127                        u->pipe->temp_file->file.fd);
128     }
129 
130     if (u->header_sent
131         && (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE))
132     {
133         rc = 0;
134     }
135 
136     if (rc == NGX_DECLINED) {
137         dd("returning");
138         return;
139     }
140 
141     r->connection->log->action = "sending to client";
142 
143     if (rc == 0) {
144         rc = ngx_http_send_special(r, NGX_HTTP_LAST);
145     }
146 
147     ngx_http_finalize_request(r, rc);
148 
149     dd("returning");
150 }
151 
152 void
153 ngx_postgres_upstream_next(ngx_http_request_t *r,
154     ngx_http_upstream_t *u, ngx_int_t ft_type)
155 {
156     ngx_uint_t  status, state;
157 
158     dd("entering");
159 
160     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
161                    "http next upstream, %xi", ft_type);
162 
163 #if 0
164     ngx_http_busy_unlock(u->conf->busy_lock, &u->busy_lock);
165 #endif
166 
167     if (ft_type == NGX_HTTP_UPSTREAM_FT_HTTP_404) {
168         state = NGX_PEER_NEXT;
169     } else {
170         state = NGX_PEER_FAILED;
171     }
172 
173     if (ft_type != NGX_HTTP_UPSTREAM_FT_NOLIVE) {
174         u->peer.free(&u->peer, u->peer.data, state);
175     }
176 
177     if (ft_type == NGX_HTTP_UPSTREAM_FT_TIMEOUT) {
178         ngx_log_error(NGX_LOG_ERR, r->connection->log, NGX_ETIMEDOUT,
179                       "upstream timed out");
180     }
181 
182     if (u->peer.cached && ft_type == NGX_HTTP_UPSTREAM_FT_ERROR) {
183         status = 0;
184 
185     } else {
186         switch(ft_type) {
187 
188         case NGX_HTTP_UPSTREAM_FT_TIMEOUT:
189             status = NGX_HTTP_GATEWAY_TIME_OUT;
190             break;
191 
192         case NGX_HTTP_UPSTREAM_FT_HTTP_500:
193             status = NGX_HTTP_INTERNAL_SERVER_ERROR;
194             break;
195 
196         case NGX_HTTP_UPSTREAM_FT_HTTP_404:
197             status = NGX_HTTP_NOT_FOUND;
198             break;
199 
200         /*
201          * NGX_HTTP_UPSTREAM_FT_BUSY_LOCK and NGX_HTTP_UPSTREAM_FT_MAX_WAITING
202          * never reach here
203          */
204 
205         default:
206             status = NGX_HTTP_BAD_GATEWAY;
207         }
208     }
209 
210     if (r->connection->error) {
211         ngx_postgres_upstream_finalize_request(r, u,
212                                                NGX_HTTP_CLIENT_CLOSED_REQUEST);
213 
214         dd("returning");
215         return;
216     }
217 
218     if (status) {
219         u->state->status = status;
220 
221         if (u->peer.tries == 0 || !(u->conf->next_upstream & ft_type)) {
222             ngx_postgres_upstream_finalize_request(r, u, status);
223 
224             dd("returning");
225             return;
226         }
227     }
228 
229     if (u->peer.connection) {
230         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
231                        "close http upstream connection: %d",
232                        u->peer.connection->fd);
233 
234 #if 0 /* we don't support SSL at this time, was: (NGX_HTTP_SSL) */
235 
236         if (u->peer.connection->ssl) {
237             u->peer.connection->ssl->no_wait_shutdown = 1;
238             u->peer.connection->ssl->no_send_shutdown = 1;
239 
240             (void) ngx_ssl_shutdown(u->peer.connection);
241         }
242 #endif
243 
244 #if defined(nginx_version) && (nginx_version >= 1001004)
245         if (u->peer.connection->pool) {
246             ngx_destroy_pool(u->peer.connection->pool);
247         }
248 #endif
249 
250         ngx_close_connection(u->peer.connection);
251     }
252 
253 #if 0
254     if (u->conf->busy_lock && !u->busy_locked) {
255         ngx_http_upstream_busy_lock(p);
256         return;
257     }
258 #endif
259 
260     /* TODO: ngx_http_upstream_connect(r, u); */
261     if (status == 0) {
262         status = NGX_HTTP_INTERNAL_SERVER_ERROR;
263     }
264 
265     dd("returning");
266     return ngx_postgres_upstream_finalize_request(r, u, status);
267 }
268 
269 ngx_int_t
270 ngx_postgres_upstream_test_connect(ngx_connection_t *c)
271 {
272     int        err;
273     socklen_t  len;
274 
275     dd("entering");
276 
277 #if (NGX_HAVE_KQUEUE)
278 
279     if (ngx_event_flags & NGX_USE_KQUEUE_EVENT)  {
280         if (c->write->pending_eof) {
281             c->log->action = "connecting to upstream";
282             (void) ngx_connection_error(c, c->write->kq_errno,
283                        "kevent() reported that connect() failed");
284 
285             dd("returning NGX_ERROR");
286             return NGX_ERROR;
287         }
288 
289     } else
290 #endif
291     {
292         err = 0;
293         len = sizeof(int);
294 
295         /*
296          * BSDs and Linux return 0 and set a pending error in err
297          * Solaris returns -1 and sets errno
298          */
299 
300         if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, (void *) &err, &len) == -1)
301         {
302             err = ngx_errno;
303         }
304 
305         if (err) {
306             c->log->action = "connecting to upstream";
307             (void) ngx_connection_error(c, err, "connect() failed");
308 
309             dd("returning NGX_ERROR");
310             return NGX_ERROR;
311         }
312     }
313 
314     dd("returning NGX_OK");
315     return NGX_OK;
316 }
317 
318 ngx_int_t
319 ngx_postgres_rewrite_var(ngx_http_request_t *r, ngx_http_variable_value_t *v,
320     uintptr_t data)
321 {
322     ngx_http_variable_t              *var;
323     ngx_http_core_main_conf_t        *cmcf;
324     ngx_postgres_rewrite_loc_conf_t  *rlcf;
325 
326     rlcf = ngx_http_get_module_loc_conf(r, ngx_http_rewrite_module);
327 
328     if (rlcf->uninitialized_variable_warn == 0) {
329         *v = ngx_http_variable_null_value;
330         return NGX_OK;
331     }
332 
333     cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
334 
335     var = cmcf->variables.elts;
336 
337     /*
338      * the ngx_http_rewrite_module sets variables directly in r->variables,
339      * and they should be handled by ngx_http_get_indexed_variable(),
340      * so the handler is called only if the variable is not initialized
341      */
342 
343     ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,
344                   "using uninitialized \"%V\" variable", &var[data].name);
345 
346     *v = ngx_http_variable_null_value;
347 
348     return NGX_OK;
349 }
350 
351 char *
352 ngx_postgres_rewrite_value(ngx_conf_t *cf, ngx_postgres_rewrite_loc_conf_t *lcf,
353     ngx_str_t *value)
354 {
355     ngx_int_t                              n;
356     ngx_http_script_compile_t              sc;
357     ngx_http_script_value_code_t          *val;
358     ngx_http_script_complex_value_code_t  *complex;
359 
360     n = ngx_http_script_variables_count(value);
361 
362     if (n == 0) {
363         val = ngx_http_script_start_code(cf->pool, &lcf->codes,
364                                          sizeof(ngx_http_script_value_code_t));
365         if (val == NULL) {
366             return NGX_CONF_ERROR;
367         }
368 
369         n = ngx_atoi(value->data, value->len);
370 
371         if (n == NGX_ERROR) {
372             n = 0;
373         }
374 
375         val->code = ngx_http_script_value_code;
376         val->value = (uintptr_t) n;
377         val->text_len = (uintptr_t) value->len;
378         val->text_data = (uintptr_t) value->data;
379 
380         return NGX_CONF_OK;
381     }
382 
383     complex = ngx_http_script_start_code(cf->pool, &lcf->codes,
384                                  sizeof(ngx_http_script_complex_value_code_t));
385     if (complex == NULL) {
386         return NGX_CONF_ERROR;
387     }
388 
389     complex->code = ngx_http_script_complex_value_code;
390     complex->lengths = NULL;
391 
392     ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
393 
394     sc.cf = cf;
395     sc.source = value;
396     sc.lengths = &complex->lengths;
397     sc.values = &lcf->codes;
398     sc.variables = n;
399     sc.complete_lengths = 1;
400 
401     if (ngx_http_script_compile(&sc) != NGX_OK) {
402         return NGX_CONF_ERROR;
403     }
404 
405     return NGX_CONF_OK;
406 }
407