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 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 * 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 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 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 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