http_header.c 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. #include "http_header.h"
  3. static void web_client_enable_deflate(struct web_client *w, bool gzip) {
  4. if(gzip)
  5. web_client_flag_set(w, WEB_CLIENT_ENCODING_GZIP);
  6. else
  7. web_client_flag_set(w, WEB_CLIENT_ENCODING_DEFLATE);
  8. if(!web_client_check_conn_unix(w) && !web_client_check_conn_tcp(w) && !web_client_check_conn_cloud(w))
  9. return;
  10. if(unlikely(w->response.zinitialized)) {
  11. // compression has already been initialized for this client.
  12. return;
  13. }
  14. if(unlikely(w->response.sent)) {
  15. netdata_log_error("%llu: Cannot enable compression in the middle of a conversation.", w->id);
  16. return;
  17. }
  18. w->response.zstream.zalloc = Z_NULL;
  19. w->response.zstream.zfree = Z_NULL;
  20. w->response.zstream.opaque = Z_NULL;
  21. w->response.zstream.next_in = (Bytef *)w->response.data->buffer;
  22. w->response.zstream.avail_in = 0;
  23. w->response.zstream.total_in = 0;
  24. w->response.zstream.next_out = w->response.zbuffer;
  25. w->response.zstream.avail_out = 0;
  26. w->response.zstream.total_out = 0;
  27. w->response.zstream.zalloc = Z_NULL;
  28. w->response.zstream.zfree = Z_NULL;
  29. w->response.zstream.opaque = Z_NULL;
  30. // Select GZIP compression: windowbits = 15 + 16 = 31
  31. if(deflateInit2(&w->response.zstream, web_gzip_level, Z_DEFLATED, 15 + ((gzip)?16:0), 8, web_gzip_strategy) != Z_OK) {
  32. netdata_log_error("%llu: Failed to initialize zlib. Proceeding without compression.", w->id);
  33. return;
  34. }
  35. w->response.zsent = 0;
  36. w->response.zoutput = true;
  37. w->response.zinitialized = true;
  38. if(!web_client_check_conn_cloud(w))
  39. // cloud sends the entire response at once, not in chunks
  40. web_client_flag_set(w, WEB_CLIENT_CHUNKED_TRANSFER);
  41. netdata_log_debug(D_DEFLATE, "%llu: Initialized compression.", w->id);
  42. }
  43. static void http_header_origin(struct web_client *w, const char *v, size_t len __maybe_unused) {
  44. freez(w->origin);
  45. w->origin = strdupz(v);
  46. }
  47. static void http_header_connection(struct web_client *w, const char *v, size_t len __maybe_unused) {
  48. if(strcasestr(v, "keep-alive"))
  49. web_client_enable_keepalive(w);
  50. }
  51. static void http_header_dnt(struct web_client *w, const char *v, size_t len __maybe_unused) {
  52. if(respect_web_browser_do_not_track_policy) {
  53. if (*v == '0') web_client_disable_donottrack(w);
  54. else if (*v == '1') web_client_enable_donottrack(w);
  55. }
  56. }
  57. static void http_header_user_agent(struct web_client *w, const char *v, size_t len __maybe_unused) {
  58. if(w->mode == HTTP_REQUEST_MODE_STREAM) {
  59. freez(w->user_agent);
  60. w->user_agent = strdupz(v);
  61. }
  62. }
  63. static void http_header_x_auth_token(struct web_client *w, const char *v, size_t len __maybe_unused) {
  64. freez(w->auth_bearer_token);
  65. w->auth_bearer_token = strdupz(v);
  66. }
  67. static void http_header_host(struct web_client *w, const char *v, size_t len) {
  68. char buffer[NI_MAXHOST];
  69. strncpyz(buffer, v, (len < sizeof(buffer) - 1 ? len : sizeof(buffer) - 1));
  70. freez(w->server_host);
  71. w->server_host = strdupz(buffer);
  72. }
  73. static void http_header_accept_encoding(struct web_client *w, const char *v, size_t len __maybe_unused) {
  74. if(web_enable_gzip) {
  75. if(strcasestr(v, "gzip"))
  76. web_client_enable_deflate(w, true);
  77. // does not seem to work
  78. // else if(strcasestr(v, "deflate"))
  79. // web_client_enable_deflate(w, 0);
  80. }
  81. }
  82. static void http_header_x_forwarded_host(struct web_client *w, const char *v, size_t len) {
  83. char buffer[NI_MAXHOST];
  84. strncpyz(buffer, v, (len < sizeof(buffer) - 1 ? len : sizeof(buffer) - 1));
  85. freez(w->forwarded_host);
  86. w->forwarded_host = strdupz(buffer);
  87. }
  88. static void http_header_x_forwarded_for(struct web_client *w, const char *v, size_t len) {
  89. char buffer[NI_MAXHOST];
  90. strncpyz(buffer, v, (len < sizeof(buffer) - 1 ? len : sizeof(buffer) - 1));
  91. freez(w->forwarded_for);
  92. w->forwarded_for = strdupz(buffer);
  93. }
  94. static void http_header_x_transaction_id(struct web_client *w, const char *v, size_t len) {
  95. char buffer[UUID_STR_LEN * 2];
  96. strncpyz(buffer, v, (len < sizeof(buffer) - 1 ? len : sizeof(buffer) - 1));
  97. uuid_parse_flexi(buffer, w->transaction); // will not alter w->transaction if it fails
  98. }
  99. static void http_header_x_netdata_account_id(struct web_client *w, const char *v, size_t len) {
  100. if(web_client_flag_check(w, WEB_CLIENT_FLAG_CONN_CLOUD) && w->acl == HTTP_ACL_ACLK) {
  101. char buffer[UUID_STR_LEN * 2];
  102. strncpyz(buffer, v, (len < sizeof(buffer) - 1 ? len : sizeof(buffer) - 1));
  103. uuid_parse_flexi(buffer, w->auth.cloud_account_id); // will not alter w->cloud_account_id if it fails
  104. }
  105. }
  106. static void http_header_x_netdata_role(struct web_client *w, const char *v, size_t len) {
  107. if(web_client_flag_check(w, WEB_CLIENT_FLAG_CONN_CLOUD) && w->acl == HTTP_ACL_ACLK) {
  108. char buffer[100];
  109. strncpyz(buffer, v, (len < sizeof(buffer) - 1 ? len : sizeof(buffer) - 1));
  110. if (strcasecmp(buffer, "admin") == 0)
  111. w->access = HTTP_ACCESS_ADMIN;
  112. else if(strcasecmp(buffer, "member") == 0)
  113. w->access = HTTP_ACCESS_MEMBER;
  114. else
  115. w->access = HTTP_ACCESS_ANY;
  116. web_client_flags_clear_auth(w);
  117. web_client_flag_set(w, WEB_CLIENT_FLAG_AUTH_CLOUD);
  118. }
  119. }
  120. static void http_header_x_netdata_user_name(struct web_client *w, const char *v, size_t len) {
  121. if(web_client_flag_check(w, WEB_CLIENT_FLAG_CONN_CLOUD) && w->acl == HTTP_ACL_ACLK) {
  122. strncpyz(w->auth.client_name, v, (len < sizeof(w->auth.client_name) - 1 ? len : sizeof(w->auth.client_name) - 1));
  123. }
  124. }
  125. static void http_header_x_netdata_auth(struct web_client *w, const char *v, size_t len __maybe_unused) {
  126. if(web_client_flag_check(w, WEB_CLIENT_FLAG_CONN_CLOUD) && w->acl == HTTP_ACL_ACLK)
  127. // we don't need authorization bearer when the request comes from netdata cloud
  128. return;
  129. if(strncasecmp(v, "Bearer ", 7) == 0) {
  130. v = &v[7];
  131. while(*v && isspace(*v)) v++;
  132. web_client_bearer_token_auth(w, v);
  133. }
  134. }
  135. struct {
  136. uint32_t hash;
  137. const char *key;
  138. void (*cb)(struct web_client *w, const char *value, size_t value_len);
  139. } supported_headers[] = {
  140. { .hash = 0, .key = "Origin", .cb = http_header_origin },
  141. { .hash = 0, .key = "Connection", .cb = http_header_connection },
  142. { .hash = 0, .key = "DNT", .cb = http_header_dnt },
  143. { .hash = 0, .key = "User-Agent", .cb = http_header_user_agent},
  144. { .hash = 0, .key = "X-Auth-Token", .cb = http_header_x_auth_token },
  145. { .hash = 0, .key = "Host", .cb = http_header_host },
  146. { .hash = 0, .key = "Accept-Encoding", .cb = http_header_accept_encoding },
  147. { .hash = 0, .key = "X-Forwarded-Host", .cb = http_header_x_forwarded_host },
  148. { .hash = 0, .key = "X-Forwarded-For", .cb = http_header_x_forwarded_for },
  149. { .hash = 0, .key = "X-Transaction-Id", .cb = http_header_x_transaction_id },
  150. { .hash = 0, .key = "X-Netdata-Account-Id", .cb = http_header_x_netdata_account_id },
  151. { .hash = 0, .key = "X-Netdata-Role", .cb = http_header_x_netdata_role },
  152. { .hash = 0, .key = "X-Netdata-User-Name", .cb = http_header_x_netdata_user_name },
  153. { .hash = 0, .key = "X-Netdata-Auth", .cb = http_header_x_netdata_auth },
  154. // for historical reasons.
  155. // there are a few nightly versions of netdata UI that incorrectly use this instead of X-Netdata-Auth
  156. { .hash = 0, .key = "Authorization", .cb = http_header_x_netdata_auth },
  157. // terminator
  158. { .hash = 0, .key = NULL, .cb = NULL }
  159. };
  160. char *http_header_parse_line(struct web_client *w, char *s) {
  161. if(unlikely(!supported_headers[0].hash)) {
  162. // initialize the hashes, the first time it runs
  163. for(size_t i = 0; supported_headers[i].key ;i++)
  164. supported_headers[i].hash = simple_uhash(supported_headers[i].key);
  165. }
  166. char *e = s;
  167. // find the colon
  168. while(*e && *e != ':') e++;
  169. if(!*e) return e;
  170. // get the name
  171. *e = '\0';
  172. // find the value
  173. char *v = e + 1, *ve;
  174. // skip leading spaces from value
  175. while(*v == ' ') v++;
  176. ve = v;
  177. // find the \r
  178. while(*ve && *ve != '\r') ve++;
  179. if(!*ve || ve[1] != '\n') {
  180. *e = ':';
  181. return ve;
  182. }
  183. // terminate the value
  184. *ve = '\0';
  185. uint32_t hash = simple_uhash(s);
  186. for(size_t i = 0; supported_headers[i].key ;i++) {
  187. if(likely(hash != supported_headers[i].hash || strcasecmp(s, supported_headers[i].key) != 0))
  188. continue;
  189. supported_headers[i].cb(w, v, ve - v);
  190. break;
  191. }
  192. *e = ':';
  193. *ve = '\r';
  194. return ve;
  195. }