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