1
2/*
3 * Copyright (C) Igor Sysoev
4 * Copyright (C) Valentin V. Bartenev
5 * Copyright (C) NGINX, Inc.
6 */
7
8#include <nxt_main.h>

--- 13 unchanged lines hidden (view full) ---

22 size_t length;
23 nxt_controller_conf_t conf;
24 nxt_conn_t *conn;
25 nxt_queue_link_t link;
26} nxt_controller_request_t;
27
28
29typedef struct {
30 nxt_str_t status_line;
30 nxt_uint_t status;
31 nxt_conf_value_t *conf;
32 nxt_str_t json;
32
33 u_char *title;
34 u_char *detail;
35 ssize_t offset;
36 nxt_uint_t line;
37 nxt_uint_t column;
38} nxt_controller_response_t;
39
40
41static void nxt_controller_conn_init(nxt_task_t *task, void *obj, void *data);
42static void nxt_controller_conn_read(nxt_task_t *task, void *obj, void *data);
43static nxt_msec_t nxt_controller_conn_timeout_value(nxt_conn_t *c,
44 uintptr_t data);
45static void nxt_controller_conn_read_error(nxt_task_t *task, void *obj,

--- 17 unchanged lines hidden (view full) ---

63 nxt_controller_request_t *req);
64static nxt_int_t nxt_controller_conf_apply(nxt_task_t *task,
65 nxt_controller_request_t *req);
66static void nxt_controller_process_waiting(nxt_task_t *task);
67static nxt_int_t nxt_controller_conf_pass(nxt_task_t *task,
68 nxt_conf_value_t *conf);
69static void nxt_controller_response(nxt_task_t *task,
70 nxt_controller_request_t *req, nxt_controller_response_t *resp);
66static nxt_buf_t *nxt_controller_response_body(nxt_controller_response_t *resp,
67 nxt_mp_t *pool);
71static u_char *nxt_controller_date(u_char *buf, nxt_realtime_t *now,
72 struct tm *tm, size_t size, const char *format);
73
74
75static nxt_http_fields_hash_entry_t nxt_controller_request_fields[] = {
76 { nxt_string("Content-Length"),
77 &nxt_controller_request_content_length, 0 },
78
79 { nxt_null_string, NULL, 0 }
80};

--- 480 unchanged lines hidden (view full) ---

561
562static void
563nxt_controller_process_request(nxt_task_t *task, nxt_controller_request_t *req)
564{
565 nxt_mp_t *mp;
566 nxt_int_t rc;
567 nxt_str_t path;
568 nxt_conn_t *c;
564 nxt_uint_t status;
569 nxt_buf_mem_t *mbuf;
570 nxt_conf_op_t *ops;
571 nxt_conf_value_t *value;
572 nxt_conf_json_error_t error;
573 nxt_controller_response_t resp;
574
575 static const nxt_str_t empty_obj = nxt_string("{}");
576
577 c = req->conn;
578 path = req->parser.path;
579
580 if (path.length > 1 && path.start[path.length - 1] == '/') {
581 path.length--;
582 }
583
584 nxt_memzero(&resp, sizeof(nxt_controller_response_t));
585
586 if (nxt_str_eq(&req->parser.method, "GET", 3)) {
587
588 value = nxt_conf_get_path(nxt_controller_conf.root, &path);
589
590 if (value == NULL) {
586 status = 404;
587 goto done;
591 goto not_found;
592 }
593
594 resp.status = 200;
595 resp.conf = value;
596
592 status = 200;
593 goto done;
597 nxt_controller_response(task, req, &resp);
598 return;
599 }
600
601 if (nxt_str_eq(&req->parser.method, "PUT", 3)) {
602
603 mp = nxt_mp_create(1024, 128, 256, 32);
604
605 if (nxt_slow_path(mp == NULL)) {
601 status = 500;
602 goto done;
606 goto alloc_fail;
607 }
608
609 mbuf = &c->read->mem;
610
607 value = nxt_conf_json_parse(mp, mbuf->pos, mbuf->free);
611 nxt_memzero(&error, sizeof(nxt_conf_json_error_t));
612
613 value = nxt_conf_json_parse(mp, mbuf->pos, mbuf->free, &error);
614
615 if (value == NULL) {
616 nxt_mp_destroy(mp);
611 status = 400;
612 nxt_str_set(&resp.json, "{ \"error\": \"Invalid JSON.\" }");
613 goto done;
617
618 if (error.pos == NULL) {
619 goto alloc_fail;
620 }
621
622 resp.status = 400;
623 resp.title = (u_char *) "Invalid JSON.";
624 resp.detail = error.detail;
625 resp.offset = error.pos - mbuf->pos;
626
627 nxt_conf_json_position(mbuf->pos, error.pos,
628 &resp.line, &resp.column);
629
630 nxt_controller_response(task, req, &resp);
631 return;
632 }
633
634 if (path.length != 1) {
635 rc = nxt_conf_op_compile(c->mem_pool, &ops,
636 nxt_controller_conf.root,
637 &path, value);
638
639 if (rc != NXT_OK) {
640 if (rc == NXT_DECLINED) {
623 status = 404;
624 goto done;
641 goto not_found;
642 }
643
627 status = 500;
628 goto done;
644 goto alloc_fail;
645 }
646
647 value = nxt_conf_clone(mp, ops, nxt_controller_conf.root);
648
649 if (nxt_slow_path(value == NULL)) {
650 nxt_mp_destroy(mp);
635 status = 500;
636 goto done;
651 goto alloc_fail;
652 }
653 }
654
655 if (nxt_slow_path(nxt_conf_validate(value) != NXT_OK)) {
656 nxt_mp_destroy(mp);
642 status = 400;
643 nxt_str_set(&resp.json,
644 "{ \"error\": \"Invalid configuration.\" }");
645 goto done;
657 goto invalid_conf;
658 }
659
660 req->conf.root = value;
661 req->conf.pool = mp;
662
663 if (nxt_controller_conf_apply(task, req) != NXT_OK) {
664 nxt_mp_destroy(mp);
653 status = 500;
654 goto done;
665 goto alloc_fail;
666 }
667
668 return;
669 }
670
671 if (nxt_str_eq(&req->parser.method, "DELETE", 6)) {
672
673 if (path.length == 1) {
674 mp = nxt_mp_create(1024, 128, 256, 32);
675
676 if (nxt_slow_path(mp == NULL)) {
666 status = 500;
667 goto done;
677 goto alloc_fail;
678 }
679
680 value = nxt_conf_json_parse_str(mp, &empty_obj);
681
682 } else {
683 rc = nxt_conf_op_compile(c->mem_pool, &ops,
684 nxt_controller_conf.root,
685 &path, NULL);
686
687 if (rc != NXT_OK) {
688 if (rc == NXT_DECLINED) {
679 status = 404;
680 goto done;
689 goto not_found;
690 }
691
683 status = 500;
684 goto done;
692 goto alloc_fail;
693 }
694
695 mp = nxt_mp_create(1024, 128, 256, 32);
696
697 if (nxt_slow_path(mp == NULL)) {
690 status = 500;
691 goto done;
698 goto alloc_fail;
699 }
700
701 value = nxt_conf_clone(mp, ops, nxt_controller_conf.root);
702 }
703
704 if (nxt_slow_path(value == NULL)) {
705 nxt_mp_destroy(mp);
699 status = 500;
700 goto done;
706 goto alloc_fail;
707 }
708
709 if (nxt_slow_path(nxt_conf_validate(value) != NXT_OK)) {
710 nxt_mp_destroy(mp);
705 status = 400;
706 nxt_str_set(&resp.json,
707 "{ \"error\": \"Invalid configuration.\" }");
708 goto done;
711 goto invalid_conf;
712 }
713
714 req->conf.root = value;
715 req->conf.pool = mp;
716
717 if (nxt_controller_conf_apply(task, req) != NXT_OK) {
718 nxt_mp_destroy(mp);
716 status = 500;
717 goto done;
719 goto alloc_fail;
720 }
721
722 return;
723 }
724
723 status = 405;
725 resp.status = 405;
726 resp.title = (u_char *) "Invalid method.";
727 resp.offset = -1;
728
725done:
729 nxt_controller_response(task, req, &resp);
730 return;
731
727 switch (status) {
732alloc_fail:
733
729 case 200:
730 nxt_str_set(&resp.status_line, "200 OK");
731 break;
734 resp.status = 500;
735 resp.title = (u_char *) "Memory allocation failed.";
736 resp.offset = -1;
737
733 case 400:
734 nxt_str_set(&resp.status_line, "400 Bad Request");
735 break;
738 nxt_controller_response(task, req, &resp);
739 return;
740
737 case 404:
738 nxt_str_set(&resp.status_line, "404 Not Found");
739 nxt_str_set(&resp.json, "{ \"error\": \"Value doesn't exist.\" }");
740 break;
741not_found:
742
742 case 405:
743 nxt_str_set(&resp.status_line, "405 Method Not Allowed");
744 nxt_str_set(&resp.json, "{ \"error\": \"Invalid method.\" }");
745 break;
743 resp.status = 404;
744 resp.title = (u_char *) "Value doesn't exist.";
745 resp.offset = -1;
746
747 case 500:
748 nxt_str_set(&resp.status_line, "500 Internal Server Error");
749 nxt_str_set(&resp.json, "{ \"error\": \"Memory allocation failed.\" }");
750 break;
751 }
747 nxt_controller_response(task, req, &resp);
748 return;
749
750invalid_conf:
751
752 resp.status = 400;
753 resp.title = (u_char *) "Invalid configuration.";
754 resp.offset = -1;
755
756 nxt_controller_response(task, req, &resp);
757 return;
758}
759
760
761static nxt_int_t
762nxt_controller_conf_apply(nxt_task_t *task, nxt_controller_request_t *req)
763{
764 nxt_int_t rc;
765

--- 29 unchanged lines hidden (view full) ---

795 req = nxt_controller_current_request;
796 nxt_controller_current_request = NULL;
797
798 if (msg->port_msg.type == NXT_PORT_MSG_RPC_READY) {
799 nxt_mp_destroy(nxt_controller_conf.pool);
800
801 nxt_controller_conf = req->conf;
802
799 nxt_str_set(&resp.status_line, "200 OK");
800 nxt_str_set(&resp.json, "{ \"success\": \"Reconfiguration done.\" }");
803 resp.status = 200;
804 resp.title = (u_char *) "Reconfiguration done.";
805
806 } else {
807 nxt_mp_destroy(req->conf.pool);
808
805 nxt_str_set(&resp.status_line, "500 Internal Server Error");
806 nxt_str_set(&resp.json,
807 "{ \"error\": \"Failed to apply new configuration.\" }");
809 resp.status = 500;
810 resp.title = (u_char *) "Failed to apply new configuration.";
811 resp.offset = -1;
812 }
813
814 nxt_controller_response(task, req, &resp);
815
816 nxt_controller_process_waiting(task);
817}
818
819

--- 9 unchanged lines hidden (view full) ---

829 nxt_queue_remove(&req->link);
830
831 if (nxt_fast_path(nxt_controller_conf_apply(task, req) == NXT_OK)) {
832 return;
833 }
834
835 nxt_mp_destroy(req->conf.pool);
836
833 nxt_str_set(&resp.status_line, "500 Internal Server Error");
834 nxt_str_set(&resp.json,
835 "{ \"error\": \"Memory allocation failed.\" }");
837 nxt_memzero(&resp, sizeof(nxt_controller_response_t));
838
839 resp.status = 500;
840 resp.title = (u_char *) "Memory allocation failed.";
841 resp.offset = -1;
842
843 nxt_controller_response(task, req, &resp);
844
845 } nxt_queue_loop;
846}
847
848
849static nxt_int_t
850nxt_controller_conf_pass(nxt_task_t *task, nxt_conf_value_t *conf)

--- 27 unchanged lines hidden (view full) ---

878 if (nxt_slow_path(rc != NXT_OK)) {
879 nxt_port_rpc_cancel(task, controller_port, stream);
880 }
881
882 return rc;
883}
884
885
880
886static void
887nxt_controller_response(nxt_task_t *task, nxt_controller_request_t *req,
888 nxt_controller_response_t *resp)
889{
885 size_t size;
886 nxt_buf_t *b;
887 nxt_conn_t *c;
890 size_t size;
891 nxt_str_t status_line, str;
892 nxt_buf_t *b, *body;
893 nxt_conn_t *c;
894 nxt_uint_t n;
895 nxt_conf_value_t *value, *location;
896 nxt_conf_json_pretty_t pretty;
897
889 c = req->conn;
898 static nxt_str_t success_str = nxt_string("success");
899 static nxt_str_t error_str = nxt_string("error");
900 static nxt_str_t detail_str = nxt_string("detail");
901 static nxt_str_t location_str = nxt_string("location");
902 static nxt_str_t offset_str = nxt_string("offset");
903 static nxt_str_t line_str = nxt_string("line");
904 static nxt_str_t column_str = nxt_string("column");
905
891 size = sizeof("HTTP/1.0 " "\r\n\r\n") - 1 + resp->status_line.length;
906 static nxt_time_string_t date_cache = {
907 (nxt_atomic_uint_t) -1,
908 nxt_controller_date,
909 "%s, %02d %s %4d %02d:%02d:%02d GMT",
910 sizeof("Wed, 31 Dec 1986 16:40:00 GMT") - 1,
911 NXT_THREAD_TIME_GMT,
912 NXT_THREAD_TIME_SEC,
913 };
914
893 b = nxt_buf_mem_alloc(c->mem_pool, size, 0);
894 if (nxt_slow_path(b == NULL)) {
895 nxt_controller_conn_close(task, c, req);
896 return;
897 }
915 switch (resp->status) {
916
899 b->mem.free = nxt_cpymem(b->mem.free, "HTTP/1.0 ", sizeof("HTTP/1.0 ") - 1);
900 b->mem.free = nxt_cpymem(b->mem.free, resp->status_line.start,
901 resp->status_line.length);
917 case 200:
918 nxt_str_set(&status_line, "200 OK");
919 break;
920
903 b->mem.free = nxt_cpymem(b->mem.free, "\r\n\r\n", sizeof("\r\n\r\n") - 1);
921 case 400:
922 nxt_str_set(&status_line, "400 Bad Request");
923 break;
924
905 b->next = nxt_controller_response_body(resp, c->mem_pool);
925 case 404:
926 nxt_str_set(&status_line, "404 Not Found");
927 break;
928
907 if (nxt_slow_path(b->next == NULL)) {
908 nxt_controller_conn_close(task, c, req);
909 return;
929 case 405:
930 nxt_str_set(&status_line, "405 Method Not Allowed");
931 break;
932
933 case 500:
934 nxt_str_set(&status_line, "500 Internal Server Error");
935 break;
936 }
937
912 c->write = b;
913 c->write_state = &nxt_controller_conn_write_state;
938 c = req->conn;
939 value = resp->conf;
940
915 nxt_conn_write(task->thread->engine, c);
916}
941 if (value == NULL) {
942 n = 1
943 + (resp->detail != NULL)
944 + (resp->status >= 400 && resp->offset != -1);
945
946 value = nxt_conf_create_object(c->mem_pool, n);
947
919static nxt_buf_t *
920nxt_controller_response_body(nxt_controller_response_t *resp, nxt_mp_t *pool)
921{
922 size_t size;
923 nxt_buf_t *b;
924 nxt_conf_value_t *value;
925 nxt_conf_json_pretty_t pretty;
948 if (nxt_slow_path(value == NULL)) {
949 nxt_controller_conn_close(task, c, req);
950 return;
951 }
952
927 if (resp->conf) {
928 value = resp->conf;
953 str.length = nxt_strlen(resp->title);
954 str.start = resp->title;
955
930 } else {
931 value = nxt_conf_json_parse_str(pool, &resp->json);
956 if (resp->status < 400) {
957 nxt_conf_set_member_string(value, &success_str, &str, 0);
958
933 if (nxt_slow_path(value == NULL)) {
934 return NULL;
959 } else {
960 nxt_conf_set_member_string(value, &error_str, &str, 0);
961 }
962
963 n = 0;
964
965 if (resp->detail != NULL) {
966 str.length = nxt_strlen(resp->detail);
967 str.start = resp->detail;
968
969 n++;
970
971 nxt_conf_set_member_string(value, &detail_str, &str, n);
972 }
973
974 if (resp->status >= 400 && resp->offset != -1) {
975 n++;
976
977 location = nxt_conf_create_object(c->mem_pool,
978 resp->line != 0 ? 3 : 1);
979
980 nxt_conf_set_member(value, &location_str, location, n);
981
982 nxt_conf_set_member_integer(location, &offset_str, resp->offset, 0);
983
984 if (resp->line != 0) {
985 nxt_conf_set_member_integer(location, &line_str,
986 resp->line, 1);
987
988 nxt_conf_set_member_integer(location, &column_str,
989 resp->column, 2);
990 }
991 }
992 }
993
994 nxt_memzero(&pretty, sizeof(nxt_conf_json_pretty_t));
995
996 size = nxt_conf_json_length(value, &pretty) + 2;
997
942 b = nxt_buf_mem_alloc(pool, size, 0);
943 if (nxt_slow_path(b == NULL)) {
944 return NULL;
998 body = nxt_buf_mem_alloc(c->mem_pool, size, 0);
999 if (nxt_slow_path(body == NULL)) {
1000 nxt_controller_conn_close(task, c, req);
1001 return;
1002 }
1003
1004 nxt_memzero(&pretty, sizeof(nxt_conf_json_pretty_t));
1005
949 b->mem.free = nxt_conf_json_print(b->mem.free, value, &pretty);
1006 body->mem.free = nxt_conf_json_print(body->mem.free, value, &pretty);
1007
951 *b->mem.free++ = '\r';
952 *b->mem.free++ = '\n';
1008 body->mem.free = nxt_cpymem(body->mem.free, "\r\n", 2);
1009
954 return b;
1010 size = sizeof("HTTP/1.1 " "\r\n") - 1 + status_line.length
1011 + sizeof("Server: nginext/0.1\r\n") - 1
1012 + sizeof("Date: Wed, 31 Dec 1986 16:40:00 GMT\r\n") - 1
1013 + sizeof("Content-Type: application/json\r\n") - 1
1014 + sizeof("Content-Length: " "\r\n") - 1 + NXT_SIZE_T_LEN
1015 + sizeof("Connection: close\r\n") - 1
1016 + sizeof("\r\n") - 1;
1017
1018 b = nxt_buf_mem_alloc(c->mem_pool, size, 0);
1019 if (nxt_slow_path(b == NULL)) {
1020 nxt_controller_conn_close(task, c, req);
1021 return;
1022 }
1023
1024 b->next = body;
1025
1026 nxt_str_set(&str, "HTTP/1.1 ");
1027
1028 b->mem.free = nxt_cpymem(b->mem.free, str.start, str.length);
1029 b->mem.free = nxt_cpymem(b->mem.free, status_line.start,
1030 status_line.length);
1031
1032 nxt_str_set(&str, "\r\n"
1033 "Server: nginext/0.1\r\n"
1034 "Date: ");
1035
1036 b->mem.free = nxt_cpymem(b->mem.free, str.start, str.length);
1037
1038 b->mem.free = nxt_thread_time_string(task->thread, &date_cache,
1039 b->mem.free);
1040
1041 nxt_str_set(&str, "\r\n"
1042 "Content-Type: application/json\r\n"
1043 "Content-Length: ");
1044
1045 b->mem.free = nxt_cpymem(b->mem.free, str.start, str.length);
1046
1047 b->mem.free = nxt_sprintf(b->mem.free, b->mem.end, "%uz",
1048 nxt_buf_mem_used_size(&body->mem));
1049
1050 nxt_str_set(&str, "\r\n"
1051 "Connection: close\r\n"
1052 "\r\n");
1053
1054 b->mem.free = nxt_cpymem(b->mem.free, str.start, str.length);
1055
1056 c->write = b;
1057 c->write_state = &nxt_controller_conn_write_state;
1058
1059 nxt_conn_write(task->thread->engine, c);
1060}
1061
1062
1063static u_char *
1064nxt_controller_date(u_char *buf, nxt_realtime_t *now, struct tm *tm,
1065 size_t size, const char *format)
1066{
1067 static const char *week[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri",
1068 "Sat" };
1069
1070 static const char *month[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
1071 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
1072
1073 return nxt_sprintf(buf, buf + size, format,
1074 week[tm->tm_wday], tm->tm_mday,
1075 month[tm->tm_mon], tm->tm_year + 1900,
1076 tm->tm_hour, tm->tm_min, tm->tm_sec);
1077}