772
// terminate the value
773
*ve = '\0';
774
775
- // fprintf(stderr, "HEADER: '%s' = '%s'\n", s, v);
775
uint32_t hash = simple_uhash(s);
776
777
if(hash == hash_origin && !strcasecmp(s, "Origin"))
811
return ve;
812
}
813
815
-// http_request_validate()
816
-// returns:
817
-// = 0 : all good, process the request
818
-// > 0 : request is not supported
819
-// < 0 : request is incomplete - wait for more data
820
-
821
-typedef enum {
822
- HTTP_VALIDATION_OK,
823
- HTTP_VALIDATION_NOT_SUPPORTED,
824
- HTTP_VALIDATION_MALFORMED_URL,
825
-#ifdef ENABLE_HTTPS
826
- HTTP_VALIDATION_INCOMPLETE,
827
- HTTP_VALIDATION_REDIRECT
828
-#else
829
- HTTP_VALIDATION_INCOMPLETE
830
-#endif
831
-} HTTP_VALIDATION;
832
-
833
-static inline HTTP_VALIDATION http_request_validate(struct web_client *w) {
834
- char *s = (char *)buffer_tostring(w->response.data), *encoded_url = NULL;
835
-
836
- size_t last_pos = w->header_parse_last_size;
837
- if(last_pos > 4) last_pos -= 4; // allow searching for \r\n\r\n
838
- else last_pos = 0;
839
-
840
- w->header_parse_tries++;
841
- w->header_parse_last_size = buffer_strlen(w->response.data);
842
-
843
- if(w->header_parse_tries > 1) {
844
- if(w->header_parse_last_size < last_pos)
845
- last_pos = 0;
846
-
847
- if(strstr(&s[last_pos], "\r\n\r\n") == NULL) {
848
- if(w->header_parse_tries > 10) {
849
- info("Disabling slow client after %zu attempts to read the request (%zu bytes received)", w->header_parse_tries, buffer_strlen(w->response.data));
850
- w->header_parse_tries = 0;
851
- w->header_parse_last_size = 0;
852
- web_client_disable_wait_receive(w);
853
- return HTTP_VALIDATION_NOT_SUPPORTED;
854
- }
855
-
856
- return HTTP_VALIDATION_INCOMPLETE;
857
- }
858
- }
859
-
814
+/**
815
+ * Valid Method
816
+ *
817
+ * Netdata accepts only three methods, including one of these three(STREAM) is an internal method.
818
+ *
819
+ * @param w is the structure with the client request
820
+ * @param s is the start string to parse
821
+ *
822
+ * @return it returns the next address to parse case the method is valid and NULL otherwise.
823
+ */
824
+static inline char *web_client_valid_method(struct web_client *w, char *s) {
825
// is is a valid request?
826
if(!strncmp(s, "GET ", 4)) {
862
- encoded_url = s = &s[4];
827
+ s = &s[4];
828
w->mode = WEB_CLIENT_MODE_NORMAL;
829
}
830
else if(!strncmp(s, "OPTIONS ", 8)) {
866
- encoded_url = s = &s[8];
831
+ s = &s[8];
832
w->mode = WEB_CLIENT_MODE_OPTIONS;
833
}
834
else if(!strncmp(s, "STREAM ", 7)) {
835
+ s = &s[7];
836
+
837
#ifdef ENABLE_HTTPS
838
if ( (w->ssl.flags) && (netdata_use_ssl_on_stream & NETDATA_SSL_FORCE)){
872
- w->header_parse_tries = 0;
873
- w->header_parse_last_size = 0;
874
- web_client_disable_wait_receive(w);
839
char hostname[256];
840
char *copyme = strstr(s,"hostname=");
841
if ( copyme ){
856
hostname[13] = 0x00;
857
}
858
error("The server is configured to always use encrypt connection, please enable the SSL on slave with hostname '%s'.",hostname);
895
- return HTTP_VALIDATION_NOT_SUPPORTED;
859
+ s = NULL;
860
}
861
#endif
862
899
- encoded_url = s = &s[7];
863
w->mode = WEB_CLIENT_MODE_STREAM;
864
}
865
else {
866
+ s = NULL;
867
+ }
868
+
869
+ return s;
870
+}
871
+
872
+/**
873
+ * Set Path Query
874
+ *
875
+ * Set the pointers to the path and query string according to the input.
876
+ *
877
+ * @param w is the structure with the client request
878
+ * @param s is the first address of the string.
879
+ * @param ptr is the address of the separator.
880
+ */
881
+static void web_client_set_path_query(struct web_client *w, char *s, char *ptr) {
882
+ w->url_path_length = (size_t)(ptr -s);
883
+
884
+ w->url_search_path = ptr;
885
+}
886
+
887
+/**
888
+ * Split path query
889
+ *
890
+ * Do the separation between path and query string
891
+ *
892
+ * @param w is the structure with the client request
893
+ * @param s is the string to parse
894
+ */
895
+void web_client_split_path_query(struct web_client *w, char *s) {
896
+ //I am assuming here that the separator character(?) is not encoded
897
+ char *ptr = strchr(s, '?');
898
+ if(ptr) {
899
+ w->separator = '?';
900
+ web_client_set_path_query(w, s, ptr);
901
+ return;
902
+ }
903
+
904
+ //Here I test the second possibility, the URL is completely encoded by the user.
905
+ //I am not using the strcasestr, because it is fastest to check %3f and compare
906
+ //the next character.
907
+ //We executed some tests with "encodeURI(uri);" described in https://www.w3schools.com/jsref/jsref_encodeuri.asp
908
+ //on July 1st, 2019, that show us that URLs won't have '?','=' and '&' encoded, but we decided to move in front
909
+ //with the next part, because users can develop their own encoded that won't follow this rule.
910
+ char *moveme = s;
911
+ while (moveme) {
912
+ ptr = strchr(moveme, '%');
913
+ if(ptr) {
914
+ char *test = (ptr+1);
915
+ if (!strncmp(test, "3f", 2) || !strncmp(test, "3F", 2)) {
916
+ w->separator = *ptr;
917
+ web_client_set_path_query(w, s, ptr);
918
+ return;
919
+ }
920
+ ptr++;
921
+ }
922
+
923
+ moveme = ptr;
924
+ }
925
+
926
+ w->separator = 0x00;
927
+ w->url_path_length = strlen(s);
928
+ w->url_search_path = NULL;
929
+}
930
+
931
+/**
932
+ * Request validate
933
+ *
934
+ * @param w is the structure with the client request
935
+ *
936
+ * @return It returns HTTP_VALIDATION_OK on success and another code present
937
+ * in the enum HTTP_VALIDATION otherwise.
938
+ */
939
+static inline HTTP_VALIDATION http_request_validate(struct web_client *w) {
940
+ char *s = (char *)buffer_tostring(w->response.data), *encoded_url = NULL;
941
+
942
+ size_t last_pos = w->header_parse_last_size;
943
+
944
+ w->header_parse_tries++;
945
+ w->header_parse_last_size = buffer_strlen(w->response.data);
946
+
947
+ int is_it_valid;
948
+ if(w->header_parse_tries > 1) {
949
+ if(last_pos > 4) last_pos -= 4; // allow searching for \r\n\r\n
950
+ else last_pos = 0;
951
+
952
+ if(w->header_parse_last_size < last_pos)
953
+ last_pos = 0;
954
+
955
+ is_it_valid = url_is_request_complete(s, &s[last_pos], w->header_parse_last_size);
956
+ if(!is_it_valid) {
957
+ if(w->header_parse_tries > 10) {
958
+ info("Disabling slow client after %zu attempts to read the request (%zu bytes received)", w->header_parse_tries, buffer_strlen(w->response.data));
959
+ w->header_parse_tries = 0;
960
+ w->header_parse_last_size = 0;
961
+ web_client_disable_wait_receive(w);
962
+ return HTTP_VALIDATION_NOT_SUPPORTED;
963
+ }
964
+
965
+ return HTTP_VALIDATION_INCOMPLETE;
966
+ }
967
+
968
+ is_it_valid = 1;
969
+ } else {
970
+ last_pos = w->header_parse_last_size;
971
+ is_it_valid = url_is_request_complete(s, &s[last_pos], w->header_parse_last_size);
972
+ }
973
+
974
+ s = web_client_valid_method(w, s);
975
+ if (!s) {
976
w->header_parse_tries = 0;
977
w->header_parse_last_size = 0;
978
web_client_disable_wait_receive(w);
979
+
980
return HTTP_VALIDATION_NOT_SUPPORTED;
981
+ } else if (!is_it_valid) {
982
+ //Invalid request, we have more data after the end of message
983
+ char *check = strstr((char *)buffer_tostring(w->response.data), "\r\n\r\n");
984
+ if(check) {
985
+ check += 4;
986
+ if (*check) {
987
+ w->header_parse_tries = 0;
988
+ w->header_parse_last_size = 0;
989
+ web_client_disable_wait_receive(w);
990
+ return HTTP_VALIDATION_NOT_SUPPORTED;
991
+ }
992
+ }
993
+
994
+ web_client_enable_wait_receive(w);
995
+ return HTTP_VALIDATION_INCOMPLETE;
996
}
997
909
- // find the SPACE + "HTTP/"
910
- while(*s) {
911
- // find the next space
912
- while (*s && *s != ' ') s++;
998
+ //After the method we have the path and query string together
999
+ encoded_url = s;
1000
914
- // is it SPACE + "HTTP/" ?
915
- if(*s && !strncmp(s, " HTTP/", 6)) break;
916
- else s++;
917
- }
1001
+ //we search for the position where we have " HTTP/", because it finishes the user request
1002
+ s = url_find_protocol(s);
1003
1004
// incomplete requests
1005
if(unlikely(!*s)) {
1010
// we have the end of encoded_url - remember it
1011
char *ue = s;
1012
1013
+ //Variables used to map the variables in the query string case it is present
1014
+ int total_variables;
1015
+ char *ptr_variables[WEB_FIELDS_MAX];
1016
+
1017
// make sure we have complete request
1018
// complete requests contain: \r\n\r\n
1019
while(*s) {
1031
// a valid complete HTTP request found
1032
1033
*ue = '\0';
945
- if(!url_decode_r(w->decoded_url, encoded_url, NETDATA_WEB_REQUEST_URL_SIZE + 1))
946
- return HTTP_VALIDATION_MALFORMED_URL;
1034
+ if(w->mode != WEB_CLIENT_MODE_NORMAL) {
1035
+ if(!url_decode_r(w->decoded_url, encoded_url, NETDATA_WEB_REQUEST_URL_SIZE + 1))
1036
+ return HTTP_VALIDATION_MALFORMED_URL;
1037
+ } else {
1038
+ web_client_split_path_query(w, encoded_url);
1039
+
1040
+ if (w->separator) {
1041
+ *w->url_search_path = 0x00;
1042
+ }
1043
+
1044
+ if(!url_decode_r(w->decoded_url, encoded_url, NETDATA_WEB_REQUEST_URL_SIZE + 1))
1045
+ return HTTP_VALIDATION_MALFORMED_URL;
1046
+
1047
+ if (w->separator) {
1048
+ *w->url_search_path = w->separator;
1049
+
1050
+ char *from = (encoded_url + w->url_path_length);
1051
+ total_variables = url_map_query_string(ptr_variables, from);
1052
+
1053
+ if (url_parse_query_string(w->decoded_query_string, NETDATA_WEB_REQUEST_URL_SIZE + 1, ptr_variables, total_variables)) {
1054
+ return HTTP_VALIDATION_MALFORMED_URL;
1055
+ }
1056
+ }
1057
+ }
1058
*ue = ' ';
948
-
1059
+
1060
// copy the URL - we are going to overwrite parts of it
1061
// TODO -- ideally we we should avoid copying buffers around
1062
strncpyz(w->last_url, w->decoded_url, NETDATA_WEB_REQUEST_URL_SIZE);
1063
+ if (w->separator) {
1064
+ *w->url_search_path = 0x00;
1065
+ }
1066
#ifdef ENABLE_HTTPS
1067
if ( (!web_client_check_unix(w)) && (netdata_srv_ctx) ) {
1068
if ((w->ssl.conn) && ((w->ssl.flags & NETDATA_SSL_NO_HANDSHAKE) && (netdata_use_ssl_on_http & NETDATA_SSL_FORCE) && (w->mode != WEB_CLIENT_MODE_STREAM)) ) {