diff options
author | Valentin Bartenev <vbart@nginx.com> | 2017-08-11 19:54:40 +0300 |
---|---|---|
committer | Valentin Bartenev <vbart@nginx.com> | 2017-08-11 19:54:40 +0300 |
commit | 80deee3903e70287f18e0d296d53caae077e6f99 (patch) | |
tree | df447d1ea0e9da893de291a22d4dc10ea7e0475c /src/nxt_controller.c | |
parent | 8bb88aaf5186721db20dd07e1976d52a1bc8332a (diff) | |
download | unit-80deee3903e70287f18e0d296d53caae077e6f99.tar.gz unit-80deee3903e70287f18e0d296d53caae077e6f99.tar.bz2 |
Controller: more HTTP headers and detailed JSON parsing errors.
Diffstat (limited to 'src/nxt_controller.c')
-rw-r--r-- | src/nxt_controller.c | 354 |
1 files changed, 238 insertions, 116 deletions
diff --git a/src/nxt_controller.c b/src/nxt_controller.c index 221393eb..876c037f 100644 --- a/src/nxt_controller.c +++ b/src/nxt_controller.c @@ -27,9 +27,14 @@ typedef struct { typedef struct { - nxt_str_t status_line; + nxt_uint_t status; nxt_conf_value_t *conf; - nxt_str_t json; + + u_char *title; + u_char *detail; + ssize_t offset; + nxt_uint_t line; + nxt_uint_t column; } nxt_controller_response_t; @@ -63,8 +68,8 @@ static nxt_int_t nxt_controller_conf_pass(nxt_task_t *task, nxt_conf_value_t *conf); static void nxt_controller_response(nxt_task_t *task, nxt_controller_request_t *req, nxt_controller_response_t *resp); -static nxt_buf_t *nxt_controller_response_body(nxt_controller_response_t *resp, - nxt_mp_t *pool); +static u_char *nxt_controller_date(u_char *buf, nxt_realtime_t *now, + struct tm *tm, size_t size, const char *format); static nxt_http_fields_hash_entry_t nxt_controller_request_fields[] = { @@ -561,10 +566,10 @@ nxt_controller_process_request(nxt_task_t *task, nxt_controller_request_t *req) nxt_int_t rc; nxt_str_t path; nxt_conn_t *c; - nxt_uint_t status; nxt_buf_mem_t *mbuf; nxt_conf_op_t *ops; nxt_conf_value_t *value; + nxt_conf_json_error_t error; nxt_controller_response_t resp; static const nxt_str_t empty_obj = nxt_string("{}"); @@ -583,14 +588,14 @@ nxt_controller_process_request(nxt_task_t *task, nxt_controller_request_t *req) value = nxt_conf_get_path(nxt_controller_conf.root, &path); if (value == NULL) { - status = 404; - goto done; + goto not_found; } + resp.status = 200; resp.conf = value; - status = 200; - goto done; + nxt_controller_response(task, req, &resp); + return; } if (nxt_str_eq(&req->parser.method, "PUT", 3)) { @@ -598,19 +603,32 @@ nxt_controller_process_request(nxt_task_t *task, nxt_controller_request_t *req) mp = nxt_mp_create(1024, 128, 256, 32); if (nxt_slow_path(mp == NULL)) { - status = 500; - goto done; + goto alloc_fail; } mbuf = &c->read->mem; - value = nxt_conf_json_parse(mp, mbuf->pos, mbuf->free); + nxt_memzero(&error, sizeof(nxt_conf_json_error_t)); + + value = nxt_conf_json_parse(mp, mbuf->pos, mbuf->free, &error); if (value == NULL) { nxt_mp_destroy(mp); - status = 400; - nxt_str_set(&resp.json, "{ \"error\": \"Invalid JSON.\" }"); - goto done; + + if (error.pos == NULL) { + goto alloc_fail; + } + + resp.status = 400; + resp.title = (u_char *) "Invalid JSON."; + resp.detail = error.detail; + resp.offset = error.pos - mbuf->pos; + + nxt_conf_json_position(mbuf->pos, error.pos, + &resp.line, &resp.column); + + nxt_controller_response(task, req, &resp); + return; } if (path.length != 1) { @@ -620,29 +638,23 @@ nxt_controller_process_request(nxt_task_t *task, nxt_controller_request_t *req) if (rc != NXT_OK) { if (rc == NXT_DECLINED) { - status = 404; - goto done; + goto not_found; } - status = 500; - goto done; + goto alloc_fail; } value = nxt_conf_clone(mp, ops, nxt_controller_conf.root); if (nxt_slow_path(value == NULL)) { nxt_mp_destroy(mp); - status = 500; - goto done; + goto alloc_fail; } } if (nxt_slow_path(nxt_conf_validate(value) != NXT_OK)) { nxt_mp_destroy(mp); - status = 400; - nxt_str_set(&resp.json, - "{ \"error\": \"Invalid configuration.\" }"); - goto done; + goto invalid_conf; } req->conf.root = value; @@ -650,8 +662,7 @@ nxt_controller_process_request(nxt_task_t *task, nxt_controller_request_t *req) if (nxt_controller_conf_apply(task, req) != NXT_OK) { nxt_mp_destroy(mp); - status = 500; - goto done; + goto alloc_fail; } return; @@ -663,8 +674,7 @@ nxt_controller_process_request(nxt_task_t *task, nxt_controller_request_t *req) mp = nxt_mp_create(1024, 128, 256, 32); if (nxt_slow_path(mp == NULL)) { - status = 500; - goto done; + goto alloc_fail; } value = nxt_conf_json_parse_str(mp, &empty_obj); @@ -676,19 +686,16 @@ nxt_controller_process_request(nxt_task_t *task, nxt_controller_request_t *req) if (rc != NXT_OK) { if (rc == NXT_DECLINED) { - status = 404; - goto done; + goto not_found; } - status = 500; - goto done; + goto alloc_fail; } mp = nxt_mp_create(1024, 128, 256, 32); if (nxt_slow_path(mp == NULL)) { - status = 500; - goto done; + goto alloc_fail; } value = nxt_conf_clone(mp, ops, nxt_controller_conf.root); @@ -696,16 +703,12 @@ nxt_controller_process_request(nxt_task_t *task, nxt_controller_request_t *req) if (nxt_slow_path(value == NULL)) { nxt_mp_destroy(mp); - status = 500; - goto done; + goto alloc_fail; } if (nxt_slow_path(nxt_conf_validate(value) != NXT_OK)) { nxt_mp_destroy(mp); - status = 400; - nxt_str_set(&resp.json, - "{ \"error\": \"Invalid configuration.\" }"); - goto done; + goto invalid_conf; } req->conf.root = value; @@ -713,44 +716,45 @@ nxt_controller_process_request(nxt_task_t *task, nxt_controller_request_t *req) if (nxt_controller_conf_apply(task, req) != NXT_OK) { nxt_mp_destroy(mp); - status = 500; - goto done; + goto alloc_fail; } return; } - status = 405; + resp.status = 405; + resp.title = (u_char *) "Invalid method."; + resp.offset = -1; -done: + nxt_controller_response(task, req, &resp); + return; - switch (status) { +alloc_fail: - case 200: - nxt_str_set(&resp.status_line, "200 OK"); - break; + resp.status = 500; + resp.title = (u_char *) "Memory allocation failed."; + resp.offset = -1; - case 400: - nxt_str_set(&resp.status_line, "400 Bad Request"); - break; + nxt_controller_response(task, req, &resp); + return; - case 404: - nxt_str_set(&resp.status_line, "404 Not Found"); - nxt_str_set(&resp.json, "{ \"error\": \"Value doesn't exist.\" }"); - break; +not_found: - case 405: - nxt_str_set(&resp.status_line, "405 Method Not Allowed"); - nxt_str_set(&resp.json, "{ \"error\": \"Invalid method.\" }"); - break; + resp.status = 404; + resp.title = (u_char *) "Value doesn't exist."; + resp.offset = -1; - case 500: - nxt_str_set(&resp.status_line, "500 Internal Server Error"); - nxt_str_set(&resp.json, "{ \"error\": \"Memory allocation failed.\" }"); - break; - } + nxt_controller_response(task, req, &resp); + return; + +invalid_conf: + + resp.status = 400; + resp.title = (u_char *) "Invalid configuration."; + resp.offset = -1; nxt_controller_response(task, req, &resp); + return; } @@ -796,15 +800,15 @@ nxt_controller_conf_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg, nxt_controller_conf = req->conf; - nxt_str_set(&resp.status_line, "200 OK"); - nxt_str_set(&resp.json, "{ \"success\": \"Reconfiguration done.\" }"); + resp.status = 200; + resp.title = (u_char *) "Reconfiguration done."; } else { nxt_mp_destroy(req->conf.pool); - nxt_str_set(&resp.status_line, "500 Internal Server Error"); - nxt_str_set(&resp.json, - "{ \"error\": \"Failed to apply new configuration.\" }"); + resp.status = 500; + resp.title = (u_char *) "Failed to apply new configuration."; + resp.offset = -1; } nxt_controller_response(task, req, &resp); @@ -830,9 +834,11 @@ nxt_controller_process_waiting(nxt_task_t *task) nxt_mp_destroy(req->conf.pool); - nxt_str_set(&resp.status_line, "500 Internal Server Error"); - nxt_str_set(&resp.json, - "{ \"error\": \"Memory allocation failed.\" }"); + nxt_memzero(&resp, sizeof(nxt_controller_response_t)); + + resp.status = 500; + resp.title = (u_char *) "Memory allocation failed."; + resp.offset = -1; nxt_controller_response(task, req, &resp); @@ -877,61 +883,111 @@ nxt_controller_conf_pass(nxt_task_t *task, nxt_conf_value_t *conf) } - static void nxt_controller_response(nxt_task_t *task, nxt_controller_request_t *req, nxt_controller_response_t *resp) { - size_t size; - nxt_buf_t *b; - nxt_conn_t *c; + size_t size; + nxt_str_t status_line, str; + nxt_buf_t *b, *body; + nxt_conn_t *c; + nxt_uint_t n; + nxt_conf_value_t *value, *location; + nxt_conf_json_pretty_t pretty; - c = req->conn; + static nxt_str_t success_str = nxt_string("success"); + static nxt_str_t error_str = nxt_string("error"); + static nxt_str_t detail_str = nxt_string("detail"); + static nxt_str_t location_str = nxt_string("location"); + static nxt_str_t offset_str = nxt_string("offset"); + static nxt_str_t line_str = nxt_string("line"); + static nxt_str_t column_str = nxt_string("column"); + + static nxt_time_string_t date_cache = { + (nxt_atomic_uint_t) -1, + nxt_controller_date, + "%s, %02d %s %4d %02d:%02d:%02d GMT", + sizeof("Wed, 31 Dec 1986 16:40:00 GMT") - 1, + NXT_THREAD_TIME_GMT, + NXT_THREAD_TIME_SEC, + }; + + switch (resp->status) { - size = sizeof("HTTP/1.0 " "\r\n\r\n") - 1 + resp->status_line.length; + case 200: + nxt_str_set(&status_line, "200 OK"); + break; - b = nxt_buf_mem_alloc(c->mem_pool, size, 0); - if (nxt_slow_path(b == NULL)) { - nxt_controller_conn_close(task, c, req); - return; + case 400: + nxt_str_set(&status_line, "400 Bad Request"); + break; + + case 404: + nxt_str_set(&status_line, "404 Not Found"); + break; + + case 405: + nxt_str_set(&status_line, "405 Method Not Allowed"); + break; + + case 500: + nxt_str_set(&status_line, "500 Internal Server Error"); + break; } - b->mem.free = nxt_cpymem(b->mem.free, "HTTP/1.0 ", sizeof("HTTP/1.0 ") - 1); - b->mem.free = nxt_cpymem(b->mem.free, resp->status_line.start, - resp->status_line.length); + c = req->conn; + value = resp->conf; - b->mem.free = nxt_cpymem(b->mem.free, "\r\n\r\n", sizeof("\r\n\r\n") - 1); + if (value == NULL) { + n = 1 + + (resp->detail != NULL) + + (resp->status >= 400 && resp->offset != -1); - b->next = nxt_controller_response_body(resp, c->mem_pool); + value = nxt_conf_create_object(c->mem_pool, n); - if (nxt_slow_path(b->next == NULL)) { - nxt_controller_conn_close(task, c, req); - return; - } + if (nxt_slow_path(value == NULL)) { + nxt_controller_conn_close(task, c, req); + return; + } - c->write = b; - c->write_state = &nxt_controller_conn_write_state; + str.length = nxt_strlen(resp->title); + str.start = resp->title; - nxt_conn_write(task->thread->engine, c); -} + if (resp->status < 400) { + nxt_conf_set_member_string(value, &success_str, &str, 0); + } else { + nxt_conf_set_member_string(value, &error_str, &str, 0); + } -static nxt_buf_t * -nxt_controller_response_body(nxt_controller_response_t *resp, nxt_mp_t *pool) -{ - size_t size; - nxt_buf_t *b; - nxt_conf_value_t *value; - nxt_conf_json_pretty_t pretty; + n = 0; - if (resp->conf) { - value = resp->conf; + if (resp->detail != NULL) { + str.length = nxt_strlen(resp->detail); + str.start = resp->detail; - } else { - value = nxt_conf_json_parse_str(pool, &resp->json); + n++; - if (nxt_slow_path(value == NULL)) { - return NULL; + nxt_conf_set_member_string(value, &detail_str, &str, n); + } + + if (resp->status >= 400 && resp->offset != -1) { + n++; + + location = nxt_conf_create_object(c->mem_pool, + resp->line != 0 ? 3 : 1); + + nxt_conf_set_member(value, &location_str, location, n); + + nxt_conf_set_member_integer(location, &offset_str, resp->offset, 0); + + if (resp->line != 0) { + nxt_conf_set_member_integer(location, &line_str, + resp->line, 1); + + nxt_conf_set_member_integer(location, &column_str, + resp->column, 2); + } } } @@ -939,17 +995,83 @@ nxt_controller_response_body(nxt_controller_response_t *resp, nxt_mp_t *pool) size = nxt_conf_json_length(value, &pretty) + 2; - b = nxt_buf_mem_alloc(pool, size, 0); - if (nxt_slow_path(b == NULL)) { - return NULL; + body = nxt_buf_mem_alloc(c->mem_pool, size, 0); + if (nxt_slow_path(body == NULL)) { + nxt_controller_conn_close(task, c, req); + return; } nxt_memzero(&pretty, sizeof(nxt_conf_json_pretty_t)); - b->mem.free = nxt_conf_json_print(b->mem.free, value, &pretty); + body->mem.free = nxt_conf_json_print(body->mem.free, value, &pretty); + + body->mem.free = nxt_cpymem(body->mem.free, "\r\n", 2); + + size = sizeof("HTTP/1.1 " "\r\n") - 1 + status_line.length + + sizeof("Server: nginext/0.1\r\n") - 1 + + sizeof("Date: Wed, 31 Dec 1986 16:40:00 GMT\r\n") - 1 + + sizeof("Content-Type: application/json\r\n") - 1 + + sizeof("Content-Length: " "\r\n") - 1 + NXT_SIZE_T_LEN + + sizeof("Connection: close\r\n") - 1 + + sizeof("\r\n") - 1; + + b = nxt_buf_mem_alloc(c->mem_pool, size, 0); + if (nxt_slow_path(b == NULL)) { + nxt_controller_conn_close(task, c, req); + return; + } + + b->next = body; + + nxt_str_set(&str, "HTTP/1.1 "); + + b->mem.free = nxt_cpymem(b->mem.free, str.start, str.length); + b->mem.free = nxt_cpymem(b->mem.free, status_line.start, + status_line.length); + + nxt_str_set(&str, "\r\n" + "Server: nginext/0.1\r\n" + "Date: "); + + b->mem.free = nxt_cpymem(b->mem.free, str.start, str.length); + + b->mem.free = nxt_thread_time_string(task->thread, &date_cache, + b->mem.free); + + nxt_str_set(&str, "\r\n" + "Content-Type: application/json\r\n" + "Content-Length: "); + + b->mem.free = nxt_cpymem(b->mem.free, str.start, str.length); + + b->mem.free = nxt_sprintf(b->mem.free, b->mem.end, "%uz", + nxt_buf_mem_used_size(&body->mem)); + + nxt_str_set(&str, "\r\n" + "Connection: close\r\n" + "\r\n"); + + b->mem.free = nxt_cpymem(b->mem.free, str.start, str.length); + + c->write = b; + c->write_state = &nxt_controller_conn_write_state; + + nxt_conn_write(task->thread->engine, c); +} + + +static u_char * +nxt_controller_date(u_char *buf, nxt_realtime_t *now, struct tm *tm, + size_t size, const char *format) +{ + static const char *week[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", + "Sat" }; - *b->mem.free++ = '\r'; - *b->mem.free++ = '\n'; + static const char *month[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; - return b; + return nxt_sprintf(buf, buf + size, format, + week[tm->tm_wday], tm->tm_mday, + month[tm->tm_mon], tm->tm_year + 1900, + tm->tm_hour, tm->tm_min, tm->tm_sec); } |