1 /*
2 * Copyright (c) 2010, FRiCKLE Piotr Sikora <info@frickle.com>
3 * Copyright (c) 2009-2010, Xiaozhe Wang <chaoslawful@gmail.com>
4 * Copyright (c) 2009-2010, Yichun Zhang <agentzh@gmail.com>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #ifndef DDEBUG
30 #define DDEBUG 0
31 #endif
32
33 #include "ngx_postgres_ddebug.h"
34 #include "ngx_postgres_handler.h"
35 #include "ngx_postgres_module.h"
36 #include "ngx_postgres_output.h"
37 #include "ngx_postgres_processor.h"
38 #include "ngx_postgres_util.h"
39
40
41 ngx_int_t
ngx_postgres_handler(ngx_http_request_t * r)42 ngx_postgres_handler(ngx_http_request_t *r)
43 {
44 ngx_postgres_loc_conf_t *pglcf;
45 ngx_postgres_ctx_t *pgctx;
46 ngx_http_core_loc_conf_t *clcf;
47 ngx_http_upstream_t *u;
48 ngx_connection_t *c;
49 ngx_str_t host;
50 ngx_url_t url;
51 ngx_int_t rc;
52
53 dd("entering");
54
55 if (r->subrequest_in_memory) {
56 /* TODO: add support for subrequest in memory by
57 * emitting output into u->buffer instead */
58
59 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
60 "postgres: ngx_postgres module does not support"
61 " subrequests in memory");
62
63 dd("returning NGX_HTTP_INTERNAL_SERVER_ERROR");
64 return NGX_HTTP_INTERNAL_SERVER_ERROR;
65 }
66
67 pglcf = ngx_http_get_module_loc_conf(r, ngx_postgres_module);
68
69 if ((pglcf->query.def == NULL) && !(pglcf->query.methods_set & r->method)) {
70 if (pglcf->query.methods_set != 0) {
71 dd("returning NGX_HTTP_NOT_ALLOWED");
72 return NGX_HTTP_NOT_ALLOWED;
73 }
74
75 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
76
77 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
78 "postgres: missing \"postgres_query\" in location \"%V\"",
79 &clcf->name);
80
81 dd("returning NGX_HTTP_INTERNAL_SERVER_ERROR");
82 return NGX_HTTP_INTERNAL_SERVER_ERROR;
83 }
84
85 rc = ngx_http_discard_request_body(r);
86 if (rc != NGX_OK) {
87 dd("returning rc:%d", (int) rc);
88 return rc;
89 }
90
91 #if defined(nginx_version) \
92 && (((nginx_version >= 7063) && (nginx_version < 8000)) \
93 || (nginx_version >= 8007))
94
95 if (ngx_http_upstream_create(r) != NGX_OK) {
96 dd("returning NGX_HTTP_INTERNAL_SERVER_ERROR");
97 return NGX_HTTP_INTERNAL_SERVER_ERROR;
98 }
99
100 u = r->upstream;
101
102 #else /* 0.7.x < 0.7.63, 0.8.x < 0.8.7 */
103
104 u = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_t));
105 if (u == NULL) {
106 dd("returning NGX_HTTP_INTERNAL_SERVER_ERROR");
107 return NGX_HTTP_INTERNAL_SERVER_ERROR;
108 }
109
110 u->peer.log = r->connection->log;
111 u->peer.log_error = NGX_ERROR_ERR;
112 # if (NGX_THREADS)
113 u->peer.lock = &r->connection->lock;
114 # endif
115 r->upstream = u;
116 #endif
117
118 if (pglcf->upstream_cv) {
119 /* use complex value */
120 if (ngx_http_complex_value(r, pglcf->upstream_cv, &host) != NGX_OK) {
121 dd("returning NGX_HTTP_INTERNAL_SERVER_ERROR");
122 return NGX_HTTP_INTERNAL_SERVER_ERROR;
123 }
124
125 if (host.len == 0) {
126 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
127
128 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
129 "postgres: empty \"postgres_pass\" (was: \"%V\")"
130 " in location \"%V\"", &pglcf->upstream_cv->value,
131 &clcf->name);
132
133 dd("returning NGX_HTTP_INTERNAL_SERVER_ERROR");
134 return NGX_HTTP_INTERNAL_SERVER_ERROR;
135 }
136
137 ngx_memzero(&url, sizeof(ngx_url_t));
138
139 url.host = host;
140 url.no_resolve = 1;
141
142 pglcf->upstream.upstream = ngx_postgres_find_upstream(r, &url);
143 if (pglcf->upstream.upstream == NULL) {
144 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
145 "postgres: upstream name \"%V\" not found", &host);
146
147 dd("returning NGX_ERROR");
148 return NGX_HTTP_INTERNAL_SERVER_ERROR;
149 }
150 }
151
152 pgctx = ngx_pcalloc(r->pool, sizeof(ngx_postgres_ctx_t));
153 if (pgctx == NULL) {
154 dd("returning NGX_HTTP_INTERNAL_SERVER_ERROR");
155 return NGX_HTTP_INTERNAL_SERVER_ERROR;
156 }
157
158 /*
159 * set by ngx_pcalloc():
160 *
161 * pgctx->response = NULL
162 * pgctx->var_query = { 0, NULL }
163 * pgctx->variables = NULL
164 * pgctx->status = 0
165 */
166
167 pgctx->var_cols = NGX_ERROR;
168 pgctx->var_rows = NGX_ERROR;
169 pgctx->var_affected = NGX_ERROR;
170
171 if (pglcf->variables != NULL) {
172 pgctx->variables = ngx_array_create(r->pool, pglcf->variables->nelts,
173 sizeof(ngx_str_t));
174 if (pgctx->variables == NULL) {
175 dd("returning NGX_HTTP_INTERNAL_SERVER_ERROR");
176 return NGX_HTTP_INTERNAL_SERVER_ERROR;
177 }
178
179 /* fake ngx_array_push'ing */
180 pgctx->variables->nelts = pglcf->variables->nelts;
181
182 ngx_memzero(pgctx->variables->elts,
183 pgctx->variables->nelts * pgctx->variables->size);
184 }
185
186 ngx_http_set_ctx(r, pgctx, ngx_postgres_module);
187
188 u->schema.len = sizeof("postgres://") - 1;
189 u->schema.data = (u_char *) "postgres://";
190
191 u->output.tag = (ngx_buf_tag_t) &ngx_postgres_module;
192
193 u->conf = &pglcf->upstream;
194
195 u->create_request = ngx_postgres_create_request;
196 u->reinit_request = ngx_postgres_reinit_request;
197 u->process_header = ngx_postgres_process_header;
198 u->abort_request = ngx_postgres_abort_request;
199 u->finalize_request = ngx_postgres_finalize_request;
200
201 /* we bypass the upstream input filter mechanism in
202 * ngx_http_upstream_process_headers */
203
204 u->input_filter_init = ngx_postgres_input_filter_init;
205 u->input_filter = ngx_postgres_input_filter;
206 u->input_filter_ctx = NULL;
207
208 #if defined(nginx_version) && (nginx_version >= 8011)
209 r->main->count++;
210 #endif
211
212 ngx_http_upstream_init(r);
213
214 /* override the read/write event handler to our own */
215 u->write_event_handler = ngx_postgres_wev_handler;
216 u->read_event_handler = ngx_postgres_rev_handler;
217
218 /* a bit hack-ish way to return error response (clean-up part) */
219 if ((u->peer.connection) && (u->peer.connection->fd == 0)) {
220 c = u->peer.connection;
221 u->peer.connection = NULL;
222
223 if (c->write->timer_set) {
224 ngx_del_timer(c->write);
225 }
226
227 #if defined(nginx_version) && (nginx_version >= 1001004)
228 if (c->pool) {
229 ngx_destroy_pool(c->pool);
230 }
231 #endif
232
233 ngx_free_connection(c);
234
235 ngx_postgres_upstream_finalize_request(r, u,
236 #if defined(nginx_version) && (nginx_version >= 8017)
237 NGX_HTTP_SERVICE_UNAVAILABLE);
238 #else
239 pgctx->status ? pgctx->status : NGX_HTTP_INTERNAL_SERVER_ERROR);
240 #endif
241 }
242
243 dd("returning NGX_DONE");
244 return NGX_DONE;
245 }
246
247 void
ngx_postgres_wev_handler(ngx_http_request_t * r,ngx_http_upstream_t * u)248 ngx_postgres_wev_handler(ngx_http_request_t *r, ngx_http_upstream_t *u)
249 {
250 ngx_connection_t *pgxc;
251
252 dd("entering");
253
254 /* just to ensure u->reinit_request always gets called for
255 * upstream_next */
256 u->request_sent = 1;
257
258 pgxc = u->peer.connection;
259
260 if (pgxc->write->timedout) {
261 dd("postgres connection write timeout");
262
263 ngx_postgres_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_TIMEOUT);
264
265 dd("returning");
266 return;
267 }
268
269 if (ngx_postgres_upstream_test_connect(pgxc) != NGX_OK) {
270 dd("postgres connection is broken");
271
272 ngx_postgres_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);
273
274 dd("returning");
275 return;
276 }
277
278 ngx_postgres_process_events(r);
279
280 dd("returning");
281 }
282
283 void
ngx_postgres_rev_handler(ngx_http_request_t * r,ngx_http_upstream_t * u)284 ngx_postgres_rev_handler(ngx_http_request_t *r, ngx_http_upstream_t *u)
285 {
286 ngx_connection_t *pgxc;
287
288 dd("entering");
289
290 /* just to ensure u->reinit_request always gets called for
291 * upstream_next */
292 u->request_sent = 1;
293
294 pgxc = u->peer.connection;
295
296 if (pgxc->read->timedout) {
297 dd("postgres connection read timeout");
298
299 ngx_postgres_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_TIMEOUT);
300
301 dd("returning");
302 return;
303 }
304
305 if (ngx_postgres_upstream_test_connect(pgxc) != NGX_OK) {
306 dd("postgres connection is broken");
307
308 ngx_postgres_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);
309
310 dd("returning");
311 return;
312 }
313
314 ngx_postgres_process_events(r);
315
316 dd("returning");
317 }
318
319 ngx_int_t
ngx_postgres_create_request(ngx_http_request_t * r)320 ngx_postgres_create_request(ngx_http_request_t *r)
321 {
322 dd("entering");
323
324 r->upstream->request_bufs = NULL;
325
326 dd("returning NGX_OK");
327 return NGX_OK;
328 }
329
330 ngx_int_t
ngx_postgres_reinit_request(ngx_http_request_t * r)331 ngx_postgres_reinit_request(ngx_http_request_t *r)
332 {
333 ngx_http_upstream_t *u;
334
335 dd("entering");
336
337 u = r->upstream;
338
339 /* override the read/write event handler to our own */
340 u->write_event_handler = ngx_postgres_wev_handler;
341 u->read_event_handler = ngx_postgres_rev_handler;
342
343 dd("returning NGX_OK");
344 return NGX_OK;
345 }
346
347 void
ngx_postgres_abort_request(ngx_http_request_t * r)348 ngx_postgres_abort_request(ngx_http_request_t *r)
349 {
350 dd("entering & returning (dummy function)");
351 }
352
353 void
ngx_postgres_finalize_request(ngx_http_request_t * r,ngx_int_t rc)354 ngx_postgres_finalize_request(ngx_http_request_t *r, ngx_int_t rc)
355 {
356 ngx_postgres_ctx_t *pgctx;
357
358 dd("entering");
359
360 if (rc == NGX_OK) {
361 pgctx = ngx_http_get_module_ctx(r, ngx_postgres_module);
362
363 ngx_postgres_output_chain(r, pgctx->response);
364 }
365
366 dd("returning");
367 }
368
369 ngx_int_t
ngx_postgres_process_header(ngx_http_request_t * r)370 ngx_postgres_process_header(ngx_http_request_t *r)
371 {
372 dd("entering");
373
374 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
375 "postgres: ngx_postgres_process_header should not"
376 " be called by the upstream");
377
378 dd("returning NGX_ERROR");
379 return NGX_ERROR;
380 }
381
382 ngx_int_t
ngx_postgres_input_filter_init(void * data)383 ngx_postgres_input_filter_init(void *data)
384 {
385 ngx_http_request_t *r = data;
386
387 dd("entering");
388
389 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
390 "postgres: ngx_postgres_input_filter_init should not"
391 " be called by the upstream");
392
393 dd("returning NGX_ERROR");
394 return NGX_ERROR;
395 }
396
397 ngx_int_t
ngx_postgres_input_filter(void * data,ssize_t bytes)398 ngx_postgres_input_filter(void *data, ssize_t bytes)
399 {
400 ngx_http_request_t *r = data;
401
402 dd("entering");
403
404 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
405 "postgres: ngx_postgres_input_filter should not"
406 " be called by the upstream");
407
408 dd("returning NGX_ERROR");
409 return NGX_ERROR;
410 }
411