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_str_t loc; 77 nxt_http_return_ctx_t *ctx; 78 nxt_http_return_conf_t *conf; 79 80 conf = action->u.conf; 81 82 if (conf->location == NULL) { 83 nxt_str_null(&loc); 84 85 } else { 86 nxt_var_raw(conf->location, &loc); 87 } 88 89 nxt_debug(task, "http return: %d (loc: \"%V\")", conf->status, &loc); 90 91 if (conf->status >= NXT_HTTP_BAD_REQUEST 92 && conf->status <= NXT_HTTP_SERVER_ERROR_MAX) 93 { 94 nxt_http_request_error(task, r, conf->status); 95 return NULL; 96 } 97 98 if (conf->location == NULL) { 99 ctx = NULL; 100 101 } else { 102 ctx = nxt_mp_zget(r->mem_pool, sizeof(nxt_http_return_ctx_t)); 103 if (nxt_slow_path(ctx == NULL)) { 104 goto fail; 105 } 106 } 107 108 r->status = conf->status; 109 r->resp.content_length_n = 0; 110 111 if (ctx == NULL || nxt_var_is_const(conf->location)) { 112 if (ctx != NULL) { 113 ctx->encoded = conf->encoded; 114 } 115 116 nxt_http_return_send_ready(task, r, ctx); 117 118 } else { 119 ret = nxt_var_query_init(&r->var_query, r, r->mem_pool); 120 if (nxt_slow_path(ret != NXT_OK)) { 121 goto fail; 122 } 123 124 nxt_var_query(task, r->var_query, conf->location, &ctx->location); 125 126 nxt_var_query_resolve(task, r->var_query, ctx, 127 nxt_http_return_send_ready, 128 nxt_http_return_var_error); 129 } 130 131 return NULL; 132 133 fail: 134 135 nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR); 136 return NULL; 137 } 138 139 140 static nxt_int_t 141 nxt_http_return_encode(nxt_mp_t *mp, nxt_str_t *encoded, 142 const nxt_str_t *location) 143 { 144 nxt_uint_t encode; 145 146 if (nxt_is_complex_uri_encoded(location->start, location->length)) { 147 *encoded = *location; 148 149 return NXT_OK; 150 } 151 152 encode = nxt_encode_complex_uri(NULL, location->start, location->length); 153 encoded->length = location->length + encode * 2; 154 155 encoded->start = nxt_mp_nget(mp, encoded->length); 156 if (nxt_slow_path(encoded->start == NULL)) { 157 return NXT_ERROR; 158 } 159 160 nxt_encode_complex_uri(encoded->start, location->start, location->length); 161 162 return NXT_OK; 163 } 164 165 166 static void 167 nxt_http_return_send_ready(nxt_task_t *task, void *obj, void *data) 168 { 169 nxt_int_t ret; 170 nxt_http_field_t *field; 171 nxt_http_request_t *r; 172 nxt_http_return_ctx_t *ctx; 173 174 r = obj; 175 ctx = data; 176 177 if (ctx != NULL) { 178 if (ctx->location.length > 0) { 179 ret = nxt_http_return_encode(r->mem_pool, &ctx->encoded, 180 &ctx->location); 181 if (nxt_slow_path(ret == NXT_ERROR)) { 182 goto fail; 183 } 184 } 185 186 field = nxt_list_zero_add(r->resp.fields); 187 if (nxt_slow_path(field == NULL)) { 188 goto fail; 189 } 190 191 nxt_http_field_name_set(field, "Location"); 192 193 field->value = ctx->encoded.start; 194 field->value_length = ctx->encoded.length; 195 } 196 197 r->state = &nxt_http_return_send_state; 198 199 nxt_http_request_header_send(task, r, NULL, NULL); 200 201 return; 202 203 fail: 204 205 nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR); 206 } 207 208 209 static void 210 nxt_http_return_var_error(nxt_task_t *task, void *obj, void *data) 211 { 212 nxt_http_request_t *r; 213 214 r = obj; 215 216 nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR); 217 } 218 219 220 static const nxt_http_request_state_t nxt_http_return_send_state 221 nxt_aligned(64) = 222 { 223 .error_handler = nxt_http_request_error_handler, 224 }; 225