xref: /unit/src/nxt_http_return.c (revision 2081:c68e6afffb84)
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     uint8_t            loc_is_const;
15 } nxt_http_return_conf_t;
16 
17 
18 typedef struct {
19     nxt_http_action_t  *action;
20     nxt_str_t          location;
21     nxt_str_t          encoded;
22 } nxt_http_return_ctx_t;
23 
24 
25 static nxt_http_action_t *nxt_http_return(nxt_task_t *task,
26     nxt_http_request_t *r, nxt_http_action_t *action);
27 static nxt_int_t nxt_http_return_encode(nxt_mp_t *mp, nxt_str_t *encoded,
28     const nxt_str_t *location);
29 static void nxt_http_return_send_ready(nxt_task_t *task, void *obj, void *data);
30 static void nxt_http_return_var_error(nxt_task_t *task, void *obj, void *data);
31 
32 
33 static const nxt_http_request_state_t  nxt_http_return_send_state;
34 
35 
36 nxt_int_t
37 nxt_http_return_init(nxt_mp_t *mp, nxt_http_action_t *action,
38     nxt_http_action_conf_t *acf)
39 {
40     nxt_str_t               str;
41     nxt_var_t               *var;
42     nxt_http_return_conf_t  *conf;
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.length == 0) {
55         conf->loc_is_const = 1;
56         return NXT_OK;
57     }
58 
59     var = nxt_var_compile(&acf->location, mp, 0);
60     if (nxt_slow_path(var == NULL)) {
61         return NXT_ERROR;
62     }
63 
64     conf->location = var;
65     conf->loc_is_const = nxt_var_is_const(var);
66 
67     if (conf->loc_is_const) {
68         nxt_var_raw(conf->location, &str);
69         return nxt_http_return_encode(mp, &conf->encoded, &str);
70     }
71 
72     return NXT_OK;
73 }
74 
75 
76 nxt_http_action_t *
77 nxt_http_return(nxt_task_t *task, nxt_http_request_t *r,
78     nxt_http_action_t *action)
79 {
80     nxt_int_t               ret;
81     nxt_str_t               loc;
82     nxt_http_return_ctx_t   *ctx;
83     nxt_http_return_conf_t  *conf;
84 
85     conf = action->u.conf;
86 
87     if (conf->location == NULL) {
88         nxt_str_null(&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 
96     if (conf->status >= NXT_HTTP_BAD_REQUEST
97         && conf->status <= NXT_HTTP_SERVER_ERROR_MAX)
98     {
99         nxt_http_request_error(task, r, conf->status);
100         return NULL;
101     }
102 
103     ctx = nxt_mp_zget(r->mem_pool, sizeof(nxt_http_return_ctx_t));
104     if (nxt_slow_path(ctx == NULL)) {
105         goto fail;
106     }
107 
108     ctx->action = action;
109     r->status = conf->status;
110     r->resp.content_length_n = 0;
111 
112     if (conf->loc_is_const) {
113         ctx->encoded = conf->encoded;
114 
115         nxt_http_return_send_ready(task, r, ctx);
116 
117     } else {
118         ret = nxt_var_query_init(&r->var_query, r, r->mem_pool);
119         if (nxt_slow_path(ret != NXT_OK)) {
120             goto fail;
121         }
122 
123         nxt_var_query(task, r->var_query, conf->location, &ctx->location);
124 
125         nxt_var_query_resolve(task, r->var_query, ctx,
126                               nxt_http_return_send_ready,
127                               nxt_http_return_var_error);
128     }
129 
130     return NULL;
131 
132 fail:
133 
134     nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR);
135     return NULL;
136 }
137 
138 
139 static nxt_int_t
140 nxt_http_return_encode(nxt_mp_t *mp, nxt_str_t *encoded,
141     const nxt_str_t *location)
142 {
143     nxt_uint_t  encode;
144 
145     if (nxt_is_complex_uri_encoded(location->start, location->length)) {
146         *encoded = *location;
147 
148         return NXT_OK;
149     }
150 
151     encode = nxt_encode_complex_uri(NULL, location->start, location->length);
152     encoded->length = location->length + encode * 2;
153 
154     encoded->start = nxt_mp_nget(mp, encoded->length);
155     if (nxt_slow_path(encoded->start == NULL)) {
156         return NXT_ERROR;
157     }
158 
159     nxt_encode_complex_uri(encoded->start, location->start, location->length);
160 
161     return NXT_OK;
162 }
163 
164 
165 static void
166 nxt_http_return_send_ready(nxt_task_t *task, void *obj, void *data)
167 {
168     nxt_int_t               ret;
169     nxt_http_field_t        *field;
170     nxt_http_action_t       *action;
171     nxt_http_request_t      *r;
172     nxt_http_return_ctx_t   *ctx;
173     nxt_http_return_conf_t  *conf;
174 
175     r = obj;
176     ctx = data;
177     action = ctx->action;
178     conf = action->u.conf;
179 
180     if (!conf->loc_is_const) {
181         ret = nxt_http_return_encode(r->mem_pool, &ctx->encoded,
182                                      &ctx->location);
183         if (nxt_slow_path(ret == NXT_ERROR)) {
184             goto fail;
185         }
186     }
187 
188     if (ctx->encoded.length > 0) {
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
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