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