xref: /unit/src/nxt_http_return.c (revision 2147:7bf58b1b18c4)
1 
2 /*
3  * Copyright (C) NGINX, Inc.
4  */
5 
6 #include <nxt_router.h>
7 #include <nxt_http.h>
8 
9 
10 typedef struct {
11     nxt_http_status_t  status;
12     nxt_var_t          *location;
13     nxt_str_t          encoded;
14 } nxt_http_return_conf_t;
15 
16 
17 typedef struct {
18     nxt_str_t          location;
19     nxt_str_t          encoded;
20 } nxt_http_return_ctx_t;
21 
22 
23 static nxt_http_action_t *nxt_http_return(nxt_task_t *task,
24     nxt_http_request_t *r, nxt_http_action_t *action);
25 static nxt_int_t nxt_http_return_encode(nxt_mp_t *mp, nxt_str_t *encoded,
26     const nxt_str_t *location);
27 static void nxt_http_return_send_ready(nxt_task_t *task, void *obj, void *data);
28 static void nxt_http_return_var_error(nxt_task_t *task, void *obj, void *data);
29 
30 
31 static const nxt_http_request_state_t  nxt_http_return_send_state;
32 
33 
34 nxt_int_t
nxt_http_return_init(nxt_router_conf_t * rtcf,nxt_http_action_t * action,nxt_http_action_conf_t * acf)35 nxt_http_return_init(nxt_router_conf_t *rtcf, nxt_http_action_t *action,
36     nxt_http_action_conf_t *acf)
37 {
38     nxt_mp_t                *mp;
39     nxt_str_t               str;
40     nxt_http_return_conf_t  *conf;
41 
42     mp = rtcf->mem_pool;
43 
44     conf = nxt_mp_zget(mp, sizeof(nxt_http_return_conf_t));
45     if (nxt_slow_path(conf == NULL)) {
46         return NXT_ERROR;
47     }
48 
49     action->handler = nxt_http_return;
50     action->u.conf = conf;
51 
52     conf->status = nxt_conf_get_number(acf->ret);
53 
54     if (acf->location == NULL) {
55         return NXT_OK;
56     }
57 
58     nxt_conf_get_string(acf->location, &str);
59 
60     conf->location = nxt_var_compile(&str, mp, rtcf->var_fields, 0);
61     if (nxt_slow_path(conf->location == NULL)) {
62         return NXT_ERROR;
63     }
64 
65     if (nxt_var_is_const(conf->location)) {
66         nxt_var_raw(conf->location, &str);
67         return nxt_http_return_encode(mp, &conf->encoded, &str);
68     }
69 
70     return NXT_OK;
71 }
72 
73 
74 nxt_http_action_t *
nxt_http_return(nxt_task_t * task,nxt_http_request_t * r,nxt_http_action_t * action)75 nxt_http_return(nxt_task_t *task, nxt_http_request_t *r,
76     nxt_http_action_t *action)
77 {
78     nxt_int_t               ret;
79     nxt_http_return_ctx_t   *ctx;
80     nxt_http_return_conf_t  *conf;
81 
82     conf = action->u.conf;
83 
84 #if (NXT_DEBUG)
85     nxt_str_t  loc;
86 
87     if (conf->location == NULL) {
88         nxt_str_set(&loc, "");
89 
90     } else {
91         nxt_var_raw(conf->location, &loc);
92     }
93 
94     nxt_debug(task, "http return: %d (loc: \"%V\")", conf->status, &loc);
95 #endif
96 
97     if (conf->status >= NXT_HTTP_BAD_REQUEST
98         && conf->status <= NXT_HTTP_SERVER_ERROR_MAX)
99     {
100         nxt_http_request_error(task, r, conf->status);
101         return NULL;
102     }
103 
104     if (conf->location == NULL) {
105         ctx = NULL;
106 
107     } else {
108         ctx = nxt_mp_zget(r->mem_pool, sizeof(nxt_http_return_ctx_t));
109         if (nxt_slow_path(ctx == NULL)) {
110             goto fail;
111         }
112     }
113 
114     r->status = conf->status;
115     r->resp.content_length_n = 0;
116 
117     if (ctx == NULL || nxt_var_is_const(conf->location)) {
118         if (ctx != NULL) {
119             ctx->encoded = conf->encoded;
120         }
121 
122         nxt_http_return_send_ready(task, r, ctx);
123 
124     } else {
125         ret = nxt_var_query_init(&r->var_query, r, r->mem_pool);
126         if (nxt_slow_path(ret != NXT_OK)) {
127             goto fail;
128         }
129 
130         nxt_var_query(task, r->var_query, conf->location, &ctx->location);
131 
132         nxt_var_query_resolve(task, r->var_query, ctx,
133                               nxt_http_return_send_ready,
134                               nxt_http_return_var_error);
135     }
136 
137     return NULL;
138 
139 fail:
140 
141     nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR);
142     return NULL;
143 }
144 
145 
146 static nxt_int_t
nxt_http_return_encode(nxt_mp_t * mp,nxt_str_t * encoded,const nxt_str_t * location)147 nxt_http_return_encode(nxt_mp_t *mp, nxt_str_t *encoded,
148     const nxt_str_t *location)
149 {
150     nxt_uint_t  encode;
151 
152     if (nxt_is_complex_uri_encoded(location->start, location->length)) {
153         *encoded = *location;
154 
155         return NXT_OK;
156     }
157 
158     encode = nxt_encode_complex_uri(NULL, location->start, location->length);
159     encoded->length = location->length + encode * 2;
160 
161     encoded->start = nxt_mp_nget(mp, encoded->length);
162     if (nxt_slow_path(encoded->start == NULL)) {
163         return NXT_ERROR;
164     }
165 
166     nxt_encode_complex_uri(encoded->start, location->start, location->length);
167 
168     return NXT_OK;
169 }
170 
171 
172 static void
nxt_http_return_send_ready(nxt_task_t * task,void * obj,void * data)173 nxt_http_return_send_ready(nxt_task_t *task, void *obj, void *data)
174 {
175     nxt_int_t               ret;
176     nxt_http_field_t        *field;
177     nxt_http_request_t      *r;
178     nxt_http_return_ctx_t   *ctx;
179 
180     r = obj;
181     ctx = data;
182 
183     if (ctx != NULL) {
184         if (ctx->location.length > 0) {
185             ret = nxt_http_return_encode(r->mem_pool, &ctx->encoded,
186                                          &ctx->location);
187             if (nxt_slow_path(ret == NXT_ERROR)) {
188                 goto fail;
189             }
190         }
191 
192         field = nxt_list_zero_add(r->resp.fields);
193         if (nxt_slow_path(field == NULL)) {
194             goto fail;
195         }
196 
197         nxt_http_field_name_set(field, "Location");
198 
199         field->value = ctx->encoded.start;
200         field->value_length = ctx->encoded.length;
201     }
202 
203     r->state = &nxt_http_return_send_state;
204 
205     nxt_http_request_header_send(task, r, NULL, NULL);
206 
207     return;
208 
209 fail:
210 
211     nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR);
212 }
213 
214 
215 static void
nxt_http_return_var_error(nxt_task_t * task,void * obj,void * data)216 nxt_http_return_var_error(nxt_task_t *task, void *obj, void *data)
217 {
218     nxt_http_request_t  *r;
219 
220     r = obj;
221 
222     nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR);
223 }
224 
225 
226 static const nxt_http_request_state_t  nxt_http_return_send_state
227     nxt_aligned(64) =
228 {
229     .error_handler = nxt_http_request_error_handler,
230 };
231