1352 lines
45 KiB
Diff
1352 lines
45 KiB
Diff
|
5af58c8af pmdastatsd: fix minor sizeof issues found by Coverity scan
|
||
|
b3f78dc82 pmlogconf: fix resource leak found by coverity scan
|
||
|
8a3ed1b26 pmdastatsd: initialize stack variable to keep Coverity happy
|
||
|
6902959e5 pmdastatsd: fix Coverity LOCK issues on error paths
|
||
|
548cad8c5 libpcp_web: ensure context is freed only after timer is fully closed
|
||
|
01e8bb436 services: pmlogger and pmie services want pmcd on boot
|
||
|
20959e794 Fix of 1845241 - Intermittent pmlogconf core dumps
|
||
|
32d6febf4 pcp-atop: resolve other paths of potential null task pointer dereference
|
||
|
cda567efe pmproxy: improve diagnostics, particularly relating to http requests
|
||
|
e0bb9e66c pmproxy: cleanup, remove unused flags and dead code in http encoding
|
||
|
9da331eb8 pmproxy: support the OPTIONS protocol in HTTP 1.1
|
||
|
1d84081af libpcp_web: add resilience to descriptor lookup paths
|
||
|
|
||
|
--- a/src/pmdas/statsd/src/aggregator-metric-duration-exact.c 2019-08-21 11:33:26.000000000 +1000
|
||
|
+++ b/src/pmdas/statsd/src/aggregator-metric-duration-exact.c 2020-06-11 13:10:57.393576397 +1000
|
||
|
@@ -45,7 +45,7 @@
|
||
|
double** new_values = realloc(collection->values, sizeof(double*) * new_length);
|
||
|
ALLOC_CHECK("Unable to allocate memory for collection value.");
|
||
|
collection->values = new_values;
|
||
|
- collection->values[collection->length] = (double*) malloc(sizeof(double*));
|
||
|
+ collection->values[collection->length] = (double*) malloc(sizeof(double));
|
||
|
ALLOC_CHECK("Unable to allocate memory for duration collection value.");
|
||
|
*(collection->values[collection->length]) = value;
|
||
|
collection->length = new_length;
|
||
|
--- a/src/pmdas/statsd/src/aggregator-metric-labels.c 2020-02-18 16:32:40.000000000 +1100
|
||
|
+++ b/src/pmdas/statsd/src/aggregator-metric-labels.c 2020-06-11 13:10:57.393576397 +1000
|
||
|
@@ -140,7 +140,7 @@
|
||
|
|
||
|
static char*
|
||
|
create_instance_label_segment_str(char* tags) {
|
||
|
- char buffer[JSON_BUFFER_SIZE];
|
||
|
+ char buffer[JSON_BUFFER_SIZE] = {'\0'};
|
||
|
size_t tags_length = strlen(tags) + 1;
|
||
|
if (tags_length > JSON_BUFFER_SIZE) {
|
||
|
return NULL;
|
||
|
@@ -197,7 +197,7 @@
|
||
|
ALLOC_CHECK("Unable to allocate memory for labels string in metric label record.");
|
||
|
memcpy((*out)->labels, datagram->tags, labels_length);
|
||
|
struct metric_label_metadata* meta =
|
||
|
- (struct metric_label_metadata*) malloc(sizeof(struct metric_label_metadata*));
|
||
|
+ (struct metric_label_metadata*) malloc(sizeof(struct metric_label_metadata));
|
||
|
ALLOC_CHECK("Unable to allocate memory for metric label metadata.");
|
||
|
(*out)->meta = meta;
|
||
|
(*out)->type = METRIC_TYPE_NONE;
|
||
|
--- a/src/pmdas/statsd/src/network-listener.c 2019-08-27 11:09:16.000000000 +1000
|
||
|
+++ b/src/pmdas/statsd/src/network-listener.c 2020-06-11 13:10:57.393576397 +1000
|
||
|
@@ -68,7 +68,7 @@
|
||
|
struct timeval tv;
|
||
|
freeaddrinfo(res);
|
||
|
int max_udp_packet_size = config->max_udp_packet_size;
|
||
|
- char *buffer = (char *) malloc(max_udp_packet_size * sizeof(char*));
|
||
|
+ char *buffer = (char *) malloc(max_udp_packet_size * sizeof(char));
|
||
|
struct sockaddr_storage src_addr;
|
||
|
socklen_t src_addr_len = sizeof(src_addr);
|
||
|
int rv;
|
||
|
--- a/src/pmlogconf/pmlogconf.c 2020-05-23 13:33:27.000000000 +1000
|
||
|
+++ b/src/pmlogconf/pmlogconf.c 2020-06-11 13:10:57.394576411 +1000
|
||
|
@@ -735,7 +735,7 @@
|
||
|
static int
|
||
|
evaluate_number_values(group_t *group, int type, numeric_cmp_t compare)
|
||
|
{
|
||
|
- unsigned int i, found;
|
||
|
+ int i, found;
|
||
|
pmValueSet *vsp;
|
||
|
pmValue *vp;
|
||
|
pmAtomValue atom;
|
||
|
@@ -769,7 +769,7 @@
|
||
|
static int
|
||
|
evaluate_string_values(group_t *group, string_cmp_t compare)
|
||
|
{
|
||
|
- unsigned int i, found;
|
||
|
+ int i, found;
|
||
|
pmValueSet *vsp;
|
||
|
pmValue *vp;
|
||
|
pmAtomValue atom;
|
||
|
@@ -828,7 +828,7 @@
|
||
|
static int
|
||
|
evaluate_string_regexp(group_t *group, regex_cmp_t compare)
|
||
|
{
|
||
|
- unsigned int i, found;
|
||
|
+ int i, found;
|
||
|
pmValueSet *vsp;
|
||
|
pmValue *vp;
|
||
|
pmAtomValue atom;
|
||
|
@@ -1478,6 +1478,10 @@
|
||
|
} else if (strncmp("#+ groupdir ", bytes, 12) == 0) {
|
||
|
group_dircheck(bytes + 12);
|
||
|
} else if (strncmp("#+ ", bytes, 3) == 0) {
|
||
|
+ if (group) {
|
||
|
+ /* reported by COVERITY RESOURCE LEAK */
|
||
|
+ group_free(group);
|
||
|
+ }
|
||
|
group = group_create(bytes + 3, line);
|
||
|
head = 0;
|
||
|
} else if (group) {
|
||
|
--- a/src/pmdas/statsd/src/aggregator-metrics.c 2020-02-18 16:32:40.000000000 +1100
|
||
|
+++ b/src/pmdas/statsd/src/aggregator-metrics.c 2020-06-11 13:10:57.394576411 +1000
|
||
|
@@ -212,7 +212,10 @@
|
||
|
VERBOSE_LOG(0, "Writing metrics to file...");
|
||
|
pthread_mutex_lock(&container->mutex);
|
||
|
metrics* m = container->metrics;
|
||
|
- if (strlen(config->debug_output_filename) == 0) return;
|
||
|
+ if (strlen(config->debug_output_filename) == 0) {
|
||
|
+ pthread_mutex_unlock(&container->mutex);
|
||
|
+ return;
|
||
|
+ }
|
||
|
int sep = pmPathSeparator();
|
||
|
char debug_output[MAXPATHLEN];
|
||
|
pmsprintf(
|
||
|
--- a/src/pmdas/statsd/src/aggregator-stats.c 2020-02-18 16:32:40.000000000 +1100
|
||
|
+++ b/src/pmdas/statsd/src/aggregator-stats.c 2020-06-11 13:10:57.394576411 +1000
|
||
|
@@ -141,7 +141,10 @@
|
||
|
write_stats_to_file(struct agent_config* config, struct pmda_stats_container* stats) {
|
||
|
VERBOSE_LOG(0, "Writing stats to file...");
|
||
|
pthread_mutex_lock(&stats->mutex);
|
||
|
- if (strlen(config->debug_output_filename) == 0) return;
|
||
|
+ if (strlen(config->debug_output_filename) == 0) {
|
||
|
+ pthread_mutex_unlock(&stats->mutex);
|
||
|
+ return;
|
||
|
+ }
|
||
|
int sep = pmPathSeparator();
|
||
|
char debug_output[MAXPATHLEN];
|
||
|
pmsprintf(
|
||
|
--- a/src/libpcp_web/src/webgroup.c 2020-05-22 11:29:27.000000000 +1000
|
||
|
+++ b/src/libpcp_web/src/webgroup.c 2020-06-11 13:10:57.394576411 +1000
|
||
|
@@ -56,17 +56,28 @@
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
+webgroup_release_context(uv_handle_t *handle)
|
||
|
+{
|
||
|
+ struct context *context = (struct context *)handle->data;
|
||
|
+
|
||
|
+ if (pmDebugOptions.http)
|
||
|
+ fprintf(stderr, "releasing context %p\n", context);
|
||
|
+
|
||
|
+ pmwebapi_free_context(context);
|
||
|
+}
|
||
|
+
|
||
|
+static void
|
||
|
webgroup_destroy_context(struct context *context, struct webgroups *groups)
|
||
|
{
|
||
|
context->garbage = 1;
|
||
|
|
||
|
if (pmDebugOptions.http)
|
||
|
- fprintf(stderr, "freeing context %p\n", context);
|
||
|
+ fprintf(stderr, "destroying context %p\n", context);
|
||
|
|
||
|
uv_timer_stop(&context->timer);
|
||
|
if (groups)
|
||
|
dictUnlink(groups->contexts, &context->randomid);
|
||
|
- pmwebapi_free_context(context);
|
||
|
+ uv_close((uv_handle_t *)&context->timer, webgroup_release_context);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
--- a/src/pmie/pmie.service.in 2020-05-27 13:36:47.000000000 +1000
|
||
|
+++ b/src/pmie/pmie.service.in 2020-06-11 13:10:57.394576411 +1000
|
||
|
@@ -4,6 +4,7 @@
|
||
|
After=network-online.target pmcd.service
|
||
|
After=pmie_check.timer pmie_check.path pmie_daily.timer
|
||
|
BindsTo=pmie_check.timer pmie_check.path pmie_daily.timer
|
||
|
+Wants=pmcd.service
|
||
|
|
||
|
[Service]
|
||
|
Type=notify
|
||
|
--- a/src/pmlogger/pmlogger.service.in 2020-05-22 16:48:32.000000000 +1000
|
||
|
+++ b/src/pmlogger/pmlogger.service.in 2020-06-11 13:10:57.394576411 +1000
|
||
|
@@ -4,6 +4,7 @@
|
||
|
After=network-online.target pmcd.service
|
||
|
After=pmlogger_check.timer pmlogger_check.path pmlogger_daily.timer pmlogger_daily-poll.timer
|
||
|
BindsTo=pmlogger_check.timer pmlogger_check.path pmlogger_daily.timer pmlogger_daily-poll.timer
|
||
|
+Wants=pmcd.service
|
||
|
|
||
|
[Service]
|
||
|
Type=notify
|
||
|
--- a/src/pcp/atop/showgeneric.c 2020-03-30 12:13:55.000000000 +1100
|
||
|
+++ b/src/pcp/atop/showgeneric.c 2020-06-11 13:10:57.395576426 +1000
|
||
|
@@ -2024,6 +2024,9 @@
|
||
|
*/
|
||
|
for (numusers=i=0; i < numprocs; i++, curprocs++)
|
||
|
{
|
||
|
+ if (*curprocs == NULL)
|
||
|
+ continue;
|
||
|
+
|
||
|
if (procsuppress(*curprocs, &procsel))
|
||
|
continue;
|
||
|
|
||
|
@@ -2069,6 +2072,9 @@
|
||
|
*/
|
||
|
for (numprogs=i=0; i < numprocs; i++, curprocs++)
|
||
|
{
|
||
|
+ if (*curprocs == NULL)
|
||
|
+ continue;
|
||
|
+
|
||
|
if (procsuppress(*curprocs, &procsel))
|
||
|
continue;
|
||
|
|
||
|
@@ -2112,6 +2118,9 @@
|
||
|
*/
|
||
|
for (numconts=i=0; i < numprocs; i++, curprocs++)
|
||
|
{
|
||
|
+ if (*curprocs == NULL)
|
||
|
+ continue;
|
||
|
+
|
||
|
if (procsuppress(*curprocs, &procsel))
|
||
|
continue;
|
||
|
|
||
|
--- a/src/libpcp_web/src/exports 2020-05-22 15:38:47.000000000 +1000
|
||
|
+++ b/src/libpcp_web/src/exports 2020-06-11 13:10:57.397576455 +1000
|
||
|
@@ -189,3 +189,14 @@
|
||
|
pmWebGroupDestroy;
|
||
|
sdsKeyDictCallBacks;
|
||
|
} PCP_WEB_1.12;
|
||
|
+
|
||
|
+PCP_WEB_1.14 {
|
||
|
+ global:
|
||
|
+ dictFetchValue;
|
||
|
+ http_method_str;
|
||
|
+ http_body_is_final;
|
||
|
+ http_parser_version;
|
||
|
+ http_parser_url_init;
|
||
|
+ http_parser_parse_url;
|
||
|
+ http_parser_settings_init;
|
||
|
+} PCP_WEB_1.13;
|
||
|
--- a/src/pmproxy/src/http.c 2020-03-23 09:47:47.000000000 +1100
|
||
|
+++ b/src/pmproxy/src/http.c 2020-06-11 13:10:57.398576470 +1000
|
||
|
@@ -21,6 +21,18 @@
|
||
|
static int chunked_transfer_size; /* pmproxy.chunksize, pagesize by default */
|
||
|
static int smallest_buffer_size = 128;
|
||
|
|
||
|
+#define MAX_PARAMS_SIZE 4096
|
||
|
+#define MAX_HEADERS_SIZE 128
|
||
|
+
|
||
|
+static sds HEADER_ACCESS_CONTROL_REQUEST_HEADERS,
|
||
|
+ HEADER_ACCESS_CONTROL_REQUEST_METHOD,
|
||
|
+ HEADER_ACCESS_CONTROL_ALLOW_METHODS,
|
||
|
+ HEADER_ACCESS_CONTROL_ALLOW_HEADERS,
|
||
|
+ HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
|
||
|
+ HEADER_ACCESS_CONTROL_ALLOWED_HEADERS,
|
||
|
+ HEADER_CONNECTION, HEADER_CONTENT_LENGTH,
|
||
|
+ HEADER_ORIGIN, HEADER_WWW_AUTHENTICATE;
|
||
|
+
|
||
|
/*
|
||
|
* Simple helpers to manage the cumulative addition of JSON
|
||
|
* (arrays and/or objects) to a buffer.
|
||
|
@@ -121,45 +133,9 @@
|
||
|
return "text/html";
|
||
|
if (flags & HTTP_FLAG_TEXT)
|
||
|
return "text/plain";
|
||
|
- if (flags & HTTP_FLAG_JS)
|
||
|
- return "text/javascript";
|
||
|
- if (flags & HTTP_FLAG_CSS)
|
||
|
- return "text/css";
|
||
|
- if (flags & HTTP_FLAG_ICO)
|
||
|
- return "image/x-icon";
|
||
|
- if (flags & HTTP_FLAG_JPG)
|
||
|
- return "image/jpeg";
|
||
|
- if (flags & HTTP_FLAG_PNG)
|
||
|
- return "image/png";
|
||
|
- if (flags & HTTP_FLAG_GIF)
|
||
|
- return "image/gif";
|
||
|
return "application/octet-stream";
|
||
|
}
|
||
|
|
||
|
-http_flags
|
||
|
-http_suffix_type(const char *suffix)
|
||
|
-{
|
||
|
- if (strcmp(suffix, "js") == 0)
|
||
|
- return HTTP_FLAG_JS;
|
||
|
- if (strcmp(suffix, "ico") == 0)
|
||
|
- return HTTP_FLAG_ICO;
|
||
|
- if (strcmp(suffix, "css") == 0)
|
||
|
- return HTTP_FLAG_CSS;
|
||
|
- if (strcmp(suffix, "png") == 0)
|
||
|
- return HTTP_FLAG_PNG;
|
||
|
- if (strcmp(suffix, "gif") == 0)
|
||
|
- return HTTP_FLAG_GIF;
|
||
|
- if (strcmp(suffix, "jpg") == 0)
|
||
|
- return HTTP_FLAG_JPG;
|
||
|
- if (strcmp(suffix, "jpeg") == 0)
|
||
|
- return HTTP_FLAG_JPG;
|
||
|
- if (strcmp(suffix, "html") == 0)
|
||
|
- return HTTP_FLAG_HTML;
|
||
|
- if (strcmp(suffix, "txt") == 0)
|
||
|
- return HTTP_FLAG_TEXT;
|
||
|
- return 0;
|
||
|
-}
|
||
|
-
|
||
|
static const char * const
|
||
|
http_content_encoding(http_flags flags)
|
||
|
{
|
||
|
@@ -259,26 +235,28 @@
|
||
|
|
||
|
header = sdscatfmt(sdsempty(),
|
||
|
"HTTP/%u.%u %u %s\r\n"
|
||
|
- "Connection: Keep-Alive\r\n"
|
||
|
- "Access-Control-Allow-Origin: *\r\n"
|
||
|
- "Access-Control-Allow-Headers: Accept, Accept-Language, Content-Language, Content-Type\r\n",
|
||
|
+ "%S: Keep-Alive\r\n",
|
||
|
parser->http_major, parser->http_minor,
|
||
|
- sts, http_status_mapping(sts));
|
||
|
+ sts, http_status_mapping(sts), HEADER_CONNECTION);
|
||
|
+ header = sdscatfmt(header,
|
||
|
+ "%S: *\r\n"
|
||
|
+ "%S: %S\r\n",
|
||
|
+ HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
|
||
|
+ HEADER_ACCESS_CONTROL_ALLOW_HEADERS,
|
||
|
+ HEADER_ACCESS_CONTROL_ALLOWED_HEADERS);
|
||
|
|
||
|
if (sts == HTTP_STATUS_UNAUTHORIZED && client->u.http.realm)
|
||
|
- header = sdscatfmt(header, "WWW-Authenticate: Basic realm=\"%S\"\r\n",
|
||
|
- client->u.http.realm);
|
||
|
+ header = sdscatfmt(header, "%S: Basic realm=\"%S\"\r\n",
|
||
|
+ HEADER_WWW_AUTHENTICATE, client->u.http.realm);
|
||
|
|
||
|
- if ((flags & HTTP_FLAG_STREAMING))
|
||
|
- header = sdscatfmt(header, "Transfer-encoding: %s\r\n", "chunked");
|
||
|
-
|
||
|
- if (!(flags & HTTP_FLAG_STREAMING))
|
||
|
- header = sdscatfmt(header, "Content-Length: %u\r\n", length);
|
||
|
+ if ((flags & (HTTP_FLAG_STREAMING | HTTP_FLAG_NO_BODY)))
|
||
|
+ header = sdscatfmt(header, "Transfer-encoding: chunked\r\n");
|
||
|
+ else
|
||
|
+ header = sdscatfmt(header, "%S: %u\r\n", HEADER_CONTENT_LENGTH, length);
|
||
|
|
||
|
- header = sdscatfmt(header,
|
||
|
- "Content-Type: %s%s\r\n"
|
||
|
- "Date: %s\r\n\r\n",
|
||
|
- http_content_type(flags), http_content_encoding(flags),
|
||
|
+ header = sdscatfmt(header, "Content-Type: %s%s\r\n",
|
||
|
+ http_content_type(flags), http_content_encoding(flags));
|
||
|
+ header = sdscatfmt(header, "Date: %s\r\n\r\n",
|
||
|
http_date_string(time(NULL), date, sizeof(date)));
|
||
|
|
||
|
if (pmDebugOptions.http && pmDebugOptions.desperate) {
|
||
|
@@ -288,8 +266,130 @@
|
||
|
return header;
|
||
|
}
|
||
|
|
||
|
+static sds
|
||
|
+http_header_value(struct client *client, sds header)
|
||
|
+{
|
||
|
+ if (client->u.http.headers == NULL)
|
||
|
+ return NULL;
|
||
|
+ return (sds)dictFetchValue(client->u.http.headers, header);
|
||
|
+}
|
||
|
+
|
||
|
+static sds
|
||
|
+http_headers_allowed(sds headers)
|
||
|
+{
|
||
|
+ (void)headers;
|
||
|
+ return sdsdup(HEADER_ACCESS_CONTROL_ALLOWED_HEADERS);
|
||
|
+}
|
||
|
+
|
||
|
+/* check whether the (preflight) method being proposed is acceptable */
|
||
|
+static int
|
||
|
+http_method_allowed(sds value, http_options options)
|
||
|
+{
|
||
|
+ if (strcmp(value, "GET") == 0 && (options & HTTP_OPT_GET))
|
||
|
+ return 1;
|
||
|
+ if (strcmp(value, "PUT") == 0 && (options & HTTP_OPT_PUT))
|
||
|
+ return 1;
|
||
|
+ if (strcmp(value, "POST") == 0 && (options & HTTP_OPT_POST))
|
||
|
+ return 1;
|
||
|
+ if (strcmp(value, "HEAD") == 0 && (options & HTTP_OPT_HEAD))
|
||
|
+ return 1;
|
||
|
+ if (strcmp(value, "TRACE") == 0 && (options & HTTP_OPT_TRACE))
|
||
|
+ return 1;
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static char *
|
||
|
+http_methods_string(char *buffer, size_t length, http_options options)
|
||
|
+{
|
||
|
+ char *p = buffer;
|
||
|
+
|
||
|
+ /* ensure room for all options, spaces and comma separation */
|
||
|
+ if (!options || length < 48)
|
||
|
+ return NULL;
|
||
|
+
|
||
|
+ memset(buffer, 0, length);
|
||
|
+ if (options & HTTP_OPT_GET)
|
||
|
+ strcat(p, ", GET");
|
||
|
+ if (options & HTTP_OPT_PUT)
|
||
|
+ strcat(p, ", PUT");
|
||
|
+ if (options & HTTP_OPT_HEAD)
|
||
|
+ strcat(p, ", HEAD");
|
||
|
+ if (options & HTTP_OPT_POST)
|
||
|
+ strcat(p, ", POST");
|
||
|
+ if (options & HTTP_OPT_TRACE)
|
||
|
+ strcat(p, ", TRACE");
|
||
|
+ if (options & HTTP_OPT_OPTIONS)
|
||
|
+ strcat(p, ", OPTIONS");
|
||
|
+ return p + 2; /* skip leading comma+space */
|
||
|
+}
|
||
|
+
|
||
|
+static sds
|
||
|
+http_response_trace(struct client *client)
|
||
|
+{
|
||
|
+ dictIterator *iterator;
|
||
|
+ dictEntry *entry;
|
||
|
+ sds result = sdsempty();
|
||
|
+
|
||
|
+ iterator = dictGetSafeIterator(client->u.http.headers);
|
||
|
+ while ((entry = dictNext(iterator)) != NULL)
|
||
|
+ result = sdscatfmt("%S: %S\r\n", dictGetKey(entry), dictGetVal(entry));
|
||
|
+ dictReleaseIterator(iterator);
|
||
|
+ return result;
|
||
|
+}
|
||
|
+
|
||
|
+static sds
|
||
|
+http_response_access(struct client *client, http_code sts, http_options options)
|
||
|
+{
|
||
|
+ struct http_parser *parser = &client->u.http.parser;
|
||
|
+ char buffer[64];
|
||
|
+ sds header, value, result;
|
||
|
+
|
||
|
+ value = http_header_value(client, HEADER_ACCESS_CONTROL_REQUEST_METHOD);
|
||
|
+ if (value && http_method_allowed(value, options) == 0)
|
||
|
+ sts = HTTP_STATUS_METHOD_NOT_ALLOWED;
|
||
|
+
|
||
|
+ parser->http_major = parser->http_minor = 1;
|
||
|
+
|
||
|
+ header = sdscatfmt(sdsempty(),
|
||
|
+ "HTTP/%u.%u %u %s\r\n"
|
||
|
+ "%S: Keep-Alive\r\n",
|
||
|
+ parser->http_major, parser->http_minor,
|
||
|
+ sts, http_status_mapping(sts), HEADER_CONNECTION);
|
||
|
+ header = sdscatfmt(header, "%S: %u\r\n", HEADER_CONTENT_LENGTH, 0);
|
||
|
+
|
||
|
+ if (sts >= HTTP_STATUS_OK && sts < HTTP_STATUS_BAD_REQUEST) {
|
||
|
+ if ((value = http_header_value(client, HEADER_ORIGIN)))
|
||
|
+ header = sdscatfmt(header, "%S: %S\r\n",
|
||
|
+ HEADER_ACCESS_CONTROL_ALLOW_ORIGIN, value);
|
||
|
+
|
||
|
+ header = sdscatfmt(header, "%S: %s\r\n",
|
||
|
+ HEADER_ACCESS_CONTROL_ALLOW_METHODS,
|
||
|
+ http_methods_string(buffer, sizeof(buffer), options));
|
||
|
+
|
||
|
+ value = http_header_value(client, HEADER_ACCESS_CONTROL_REQUEST_HEADERS);
|
||
|
+ if (value && (result = http_headers_allowed(value)) != NULL) {
|
||
|
+ header = sdscatfmt(header, "%S: %S\r\n",
|
||
|
+ HEADER_ACCESS_CONTROL_ALLOW_HEADERS, result);
|
||
|
+ sdsfree(result);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ if (sts == HTTP_STATUS_UNAUTHORIZED && client->u.http.realm)
|
||
|
+ header = sdscatfmt(header, "%S: Basic realm=\"%S\"\r\n",
|
||
|
+ HEADER_WWW_AUTHENTICATE, client->u.http.realm);
|
||
|
+
|
||
|
+ header = sdscatfmt(header, "Date: %s\r\n\r\n",
|
||
|
+ http_date_string(time(NULL), buffer, sizeof(buffer)));
|
||
|
+
|
||
|
+ if (pmDebugOptions.http && pmDebugOptions.desperate) {
|
||
|
+ fprintf(stderr, "access response to client %p\n", client);
|
||
|
+ fputs(header, stderr);
|
||
|
+ }
|
||
|
+ return header;
|
||
|
+}
|
||
|
+
|
||
|
void
|
||
|
-http_reply(struct client *client, sds message, http_code sts, http_flags type)
|
||
|
+http_reply(struct client *client, sds message,
|
||
|
+ http_code sts, http_flags type, http_options options)
|
||
|
{
|
||
|
http_flags flags = client->u.http.flags;
|
||
|
char length[32]; /* hex length */
|
||
|
@@ -313,6 +413,15 @@
|
||
|
|
||
|
suffix = sdsnewlen("0\r\n\r\n", 5); /* chunked suffix */
|
||
|
client->u.http.flags &= ~HTTP_FLAG_STREAMING; /* end of stream! */
|
||
|
+
|
||
|
+ } else if (flags & HTTP_FLAG_NO_BODY) {
|
||
|
+ if (client->u.http.parser.method == HTTP_OPTIONS)
|
||
|
+ buffer = http_response_access(client, sts, options);
|
||
|
+ else if (client->u.http.parser.method == HTTP_TRACE)
|
||
|
+ buffer = http_response_trace(client);
|
||
|
+ else /* HTTP_HEAD */
|
||
|
+ buffer = http_response_header(client, 0, sts, type);
|
||
|
+ suffix = NULL;
|
||
|
} else { /* regular non-chunked response - headers + response body */
|
||
|
if (client->buffer == NULL) {
|
||
|
suffix = message;
|
||
|
@@ -326,10 +435,11 @@
|
||
|
buffer = http_response_header(client, sdslen(suffix), sts, type);
|
||
|
}
|
||
|
|
||
|
- if (pmDebugOptions.http) {
|
||
|
- fprintf(stderr, "HTTP response (client=%p)\n%s%s",
|
||
|
- client, buffer, suffix);
|
||
|
- }
|
||
|
+ if (pmDebugOptions.http)
|
||
|
+ fprintf(stderr, "HTTP %s response (client=%p)\n%s%s",
|
||
|
+ http_method_str(client->u.http.parser.method),
|
||
|
+ client, buffer, suffix ? suffix : "");
|
||
|
+
|
||
|
client_write(client, buffer, suffix);
|
||
|
}
|
||
|
|
||
|
@@ -363,7 +473,7 @@
|
||
|
if (pmDebugOptions.desperate)
|
||
|
fputs(message, stderr);
|
||
|
}
|
||
|
- http_reply(client, message, status, HTTP_FLAG_HTML);
|
||
|
+ http_reply(client, message, status, HTTP_FLAG_HTML, 0);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
@@ -371,6 +481,7 @@
|
||
|
{
|
||
|
struct http_parser *parser = &client->u.http.parser;
|
||
|
http_flags flags = client->u.http.flags;
|
||
|
+ const char *method;
|
||
|
sds buffer, suffix;
|
||
|
|
||
|
/* If the client buffer length is now beyond a set maximum size,
|
||
|
@@ -390,16 +501,18 @@
|
||
|
buffer = sdsempty();
|
||
|
}
|
||
|
/* prepend a chunked transfer encoding message length (hex) */
|
||
|
- buffer = sdscatprintf(buffer, "%lX\r\n", (unsigned long)sdslen(client->buffer));
|
||
|
+ buffer = sdscatprintf(buffer, "%lX\r\n",
|
||
|
+ (unsigned long)sdslen(client->buffer));
|
||
|
suffix = sdscatfmt(client->buffer, "\r\n");
|
||
|
/* reset for next call - original released on I/O completion */
|
||
|
client->buffer = NULL; /* safe, as now held in 'suffix' */
|
||
|
|
||
|
if (pmDebugOptions.http) {
|
||
|
- fprintf(stderr, "HTTP chunked buffer (client %p, len=%lu)\n%s"
|
||
|
- "HTTP chunked suffix (client %p, len=%lu)\n%s",
|
||
|
- client, (unsigned long)sdslen(buffer), buffer,
|
||
|
- client, (unsigned long)sdslen(suffix), suffix);
|
||
|
+ method = http_method_str(client->u.http.parser.method);
|
||
|
+ fprintf(stderr, "HTTP %s chunk buffer (client %p, len=%lu)\n%s"
|
||
|
+ "HTTP %s chunk suffix (client %p, len=%lu)\n%s",
|
||
|
+ method, client, (unsigned long)sdslen(buffer), buffer,
|
||
|
+ method, client, (unsigned long)sdslen(suffix), suffix);
|
||
|
}
|
||
|
client_write(client, buffer, suffix);
|
||
|
|
||
|
@@ -527,6 +640,8 @@
|
||
|
|
||
|
if (length == 0)
|
||
|
return NULL;
|
||
|
+ if (length > MAX_PARAMS_SIZE)
|
||
|
+ return NULL;
|
||
|
for (p = url; p < end; p++) {
|
||
|
if (*p == '\0')
|
||
|
break;
|
||
|
@@ -558,6 +673,11 @@
|
||
|
struct servlet *servlet;
|
||
|
sds url;
|
||
|
|
||
|
+ if (pmDebugOptions.http || pmDebugOptions.appl0)
|
||
|
+ fprintf(stderr, "HTTP %s %.*s\n",
|
||
|
+ http_method_str(client->u.http.parser.method),
|
||
|
+ (int)length, offset);
|
||
|
+
|
||
|
if (!(url = http_url_decode(offset, length, &client->u.http.parameters)))
|
||
|
return NULL;
|
||
|
for (servlet = proxy->servlets; servlet != NULL; servlet = servlet->next) {
|
||
|
@@ -576,13 +696,24 @@
|
||
|
{
|
||
|
struct client *client = (struct client *)request->data;
|
||
|
struct servlet *servlet;
|
||
|
+ sds buffer;
|
||
|
int sts;
|
||
|
|
||
|
http_client_release(client); /* new URL, clean slate */
|
||
|
-
|
||
|
- if ((servlet = servlet_lookup(client, offset, length)) != NULL) {
|
||
|
+ /* server options - https://tools.ietf.org/html/rfc7231#section-4.3.7 */
|
||
|
+ if (length == 1 && *offset == '*' &&
|
||
|
+ client->u.http.parser.method == HTTP_OPTIONS) {
|
||
|
+ buffer = http_response_access(client, HTTP_STATUS_OK, HTTP_SERVER_OPTIONS);
|
||
|
+ client_write(client, buffer, NULL);
|
||
|
+ } else if ((servlet = servlet_lookup(client, offset, length)) != NULL) {
|
||
|
client->u.http.servlet = servlet;
|
||
|
if ((sts = client->u.http.parser.status_code) == 0) {
|
||
|
+ if (client->u.http.parser.method == HTTP_OPTIONS ||
|
||
|
+ client->u.http.parser.method == HTTP_TRACE ||
|
||
|
+ client->u.http.parser.method == HTTP_HEAD)
|
||
|
+ client->u.http.flags |= HTTP_FLAG_NO_BODY;
|
||
|
+ else
|
||
|
+ client->u.http.flags &= ~HTTP_FLAG_NO_BODY;
|
||
|
client->u.http.headers = dictCreate(&sdsOwnDictCallBacks, NULL);
|
||
|
return 0;
|
||
|
}
|
||
|
@@ -616,6 +747,11 @@
|
||
|
|
||
|
if (client->u.http.parser.status_code || !client->u.http.headers)
|
||
|
return 0; /* already in process of failing connection */
|
||
|
+ if (dictSize(client->u.http.headers) >= MAX_HEADERS_SIZE) {
|
||
|
+ client->u.http.parser.status_code =
|
||
|
+ HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE;
|
||
|
+ return 0;
|
||
|
+ }
|
||
|
|
||
|
field = sdsnewlen(offset, length);
|
||
|
if (pmDebugOptions.http)
|
||
|
@@ -826,6 +962,17 @@
|
||
|
if (chunked_transfer_size < smallest_buffer_size)
|
||
|
chunked_transfer_size = smallest_buffer_size;
|
||
|
|
||
|
+ HEADER_ACCESS_CONTROL_REQUEST_HEADERS = sdsnew("Access-Control-Request-Headers");
|
||
|
+ HEADER_ACCESS_CONTROL_REQUEST_METHOD = sdsnew("Access-Control-Request-Method");
|
||
|
+ HEADER_ACCESS_CONTROL_ALLOW_METHODS = sdsnew("Access-Control-Allow-Methods");
|
||
|
+ HEADER_ACCESS_CONTROL_ALLOW_HEADERS = sdsnew("Access-Control-Allow-Headers");
|
||
|
+ HEADER_ACCESS_CONTROL_ALLOW_ORIGIN = sdsnew("Access-Control-Allow-Origin");
|
||
|
+ HEADER_ACCESS_CONTROL_ALLOWED_HEADERS = sdsnew("Accept, Accept-Language, Content-Language, Content-Type");
|
||
|
+ HEADER_CONNECTION = sdsnew("Connection");
|
||
|
+ HEADER_CONTENT_LENGTH = sdsnew("Content-Length");
|
||
|
+ HEADER_ORIGIN = sdsnew("Origin");
|
||
|
+ HEADER_WWW_AUTHENTICATE = sdsnew("WWW-Authenticate");
|
||
|
+
|
||
|
register_servlet(proxy, &pmseries_servlet);
|
||
|
register_servlet(proxy, &pmwebapi_servlet);
|
||
|
}
|
||
|
@@ -839,4 +986,15 @@
|
||
|
servlet->close(proxy);
|
||
|
|
||
|
proxymetrics_close(proxy, METRICS_HTTP);
|
||
|
+
|
||
|
+ sdsfree(HEADER_ACCESS_CONTROL_REQUEST_HEADERS);
|
||
|
+ sdsfree(HEADER_ACCESS_CONTROL_REQUEST_METHOD);
|
||
|
+ sdsfree(HEADER_ACCESS_CONTROL_ALLOW_METHODS);
|
||
|
+ sdsfree(HEADER_ACCESS_CONTROL_ALLOW_HEADERS);
|
||
|
+ sdsfree(HEADER_ACCESS_CONTROL_ALLOW_ORIGIN);
|
||
|
+ sdsfree(HEADER_ACCESS_CONTROL_ALLOWED_HEADERS);
|
||
|
+ sdsfree(HEADER_CONNECTION);
|
||
|
+ sdsfree(HEADER_CONTENT_LENGTH);
|
||
|
+ sdsfree(HEADER_ORIGIN);
|
||
|
+ sdsfree(HEADER_WWW_AUTHENTICATE);
|
||
|
}
|
||
|
--- a/src/pmproxy/src/series.c 2020-02-25 17:47:56.000000000 +1100
|
||
|
+++ b/src/pmproxy/src/series.c 2020-06-11 13:10:57.398576470 +1000
|
||
|
@@ -1,5 +1,5 @@
|
||
|
/*
|
||
|
- * Copyright (c) 2019 Red Hat.
|
||
|
+ * Copyright (c) 2019-2020 Red Hat.
|
||
|
*
|
||
|
* This program is free software; you can redistribute it and/or modify it
|
||
|
* under the terms of the GNU Lesser General Public License as published
|
||
|
@@ -15,8 +15,7 @@
|
||
|
#include <assert.h>
|
||
|
|
||
|
typedef enum pmSeriesRestKey {
|
||
|
- RESTKEY_NONE = 0,
|
||
|
- RESTKEY_SOURCE,
|
||
|
+ RESTKEY_SOURCE = 1,
|
||
|
RESTKEY_DESC,
|
||
|
RESTKEY_INSTS,
|
||
|
RESTKEY_LABELS,
|
||
|
@@ -29,7 +28,8 @@
|
||
|
|
||
|
typedef struct pmSeriesRestCommand {
|
||
|
const char *name;
|
||
|
- unsigned int size;
|
||
|
+ unsigned int namelen : 16;
|
||
|
+ unsigned int options : 16;
|
||
|
pmSeriesRestKey key;
|
||
|
} pmSeriesRestCommand;
|
||
|
|
||
|
@@ -39,7 +39,8 @@
|
||
|
pmSeriesFlags flags;
|
||
|
pmSeriesTimeWindow window;
|
||
|
uv_work_t loading;
|
||
|
- unsigned int working;
|
||
|
+ unsigned int working : 1;
|
||
|
+ unsigned int options : 16;
|
||
|
int nsids;
|
||
|
pmSID *sids;
|
||
|
pmSID sid;
|
||
|
@@ -55,16 +56,25 @@
|
||
|
} pmSeriesBaton;
|
||
|
|
||
|
static pmSeriesRestCommand commands[] = {
|
||
|
- { .key = RESTKEY_QUERY, .name = "query", .size = sizeof("query")-1 },
|
||
|
- { .key = RESTKEY_DESC, .name = "descs", .size = sizeof("descs")-1 },
|
||
|
- { .key = RESTKEY_INSTS, .name = "instances", .size = sizeof("instances")-1 },
|
||
|
- { .key = RESTKEY_LABELS, .name = "labels", .size = sizeof("labels")-1 },
|
||
|
- { .key = RESTKEY_METRIC, .name = "metrics", .size = sizeof("metrics")-1 },
|
||
|
- { .key = RESTKEY_SOURCE, .name = "sources", .size = sizeof("sources")-1 },
|
||
|
- { .key = RESTKEY_VALUES, .name = "values", .size = sizeof("values")-1 },
|
||
|
- { .key = RESTKEY_LOAD, .name = "load", .size = sizeof("load")-1 },
|
||
|
- { .key = RESTKEY_PING, .name = "ping", .size = sizeof("ping")-1 },
|
||
|
- { .key = RESTKEY_NONE }
|
||
|
+ { .key = RESTKEY_QUERY, .options = HTTP_OPTIONS_GET | HTTP_OPTIONS_POST,
|
||
|
+ .name = "query", .namelen = sizeof("query")-1 },
|
||
|
+ { .key = RESTKEY_DESC, .options = HTTP_OPTIONS_GET | HTTP_OPTIONS_POST,
|
||
|
+ .name = "descs", .namelen = sizeof("descs")-1 },
|
||
|
+ { .key = RESTKEY_INSTS, .options = HTTP_OPTIONS_GET | HTTP_OPTIONS_POST,
|
||
|
+ .name = "instances", .namelen = sizeof("instances")-1 },
|
||
|
+ { .key = RESTKEY_LABELS, .options = HTTP_OPTIONS_GET | HTTP_OPTIONS_POST,
|
||
|
+ .name = "labels", .namelen = sizeof("labels")-1 },
|
||
|
+ { .key = RESTKEY_METRIC, .options = HTTP_OPTIONS_GET | HTTP_OPTIONS_POST,
|
||
|
+ .name = "metrics", .namelen = sizeof("metrics")-1 },
|
||
|
+ { .key = RESTKEY_SOURCE, .options = HTTP_OPTIONS_GET | HTTP_OPTIONS_POST,
|
||
|
+ .name = "sources", .namelen = sizeof("sources")-1 },
|
||
|
+ { .key = RESTKEY_VALUES, .options = HTTP_OPTIONS_GET | HTTP_OPTIONS_POST,
|
||
|
+ .name = "values", .namelen = sizeof("values")-1 },
|
||
|
+ { .key = RESTKEY_LOAD, .options = HTTP_OPTIONS_GET | HTTP_OPTIONS_POST,
|
||
|
+ .name = "load", .namelen = sizeof("load")-1 },
|
||
|
+ { .key = RESTKEY_PING, .options = HTTP_OPTIONS_GET,
|
||
|
+ .name = "ping", .namelen = sizeof("ping")-1 },
|
||
|
+ { .name = NULL } /* sentinel */
|
||
|
};
|
||
|
|
||
|
/* constant string keys (initialized during servlet setup) */
|
||
|
@@ -78,8 +88,8 @@
|
||
|
static const char pmseries_success[] = "{\"success\":true}\r\n";
|
||
|
static const char pmseries_failure[] = "{\"success\":false}\r\n";
|
||
|
|
||
|
-static pmSeriesRestKey
|
||
|
-pmseries_lookup_restkey(sds url)
|
||
|
+static pmSeriesRestCommand *
|
||
|
+pmseries_lookup_rest_command(sds url)
|
||
|
{
|
||
|
pmSeriesRestCommand *cp;
|
||
|
const char *name;
|
||
|
@@ -88,11 +98,11 @@
|
||
|
strncmp(url, "/series/", sizeof("/series/") - 1) == 0) {
|
||
|
name = (const char *)url + sizeof("/series/") - 1;
|
||
|
for (cp = &commands[0]; cp->name; cp++) {
|
||
|
- if (strncmp(cp->name, name, cp->size) == 0)
|
||
|
- return cp->key;
|
||
|
+ if (strncmp(cp->name, name, cp->namelen) == 0)
|
||
|
+ return cp;
|
||
|
}
|
||
|
}
|
||
|
- return RESTKEY_NONE;
|
||
|
+ return NULL;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
@@ -518,6 +528,7 @@
|
||
|
{
|
||
|
pmSeriesBaton *baton = (pmSeriesBaton *)arg;
|
||
|
struct client *client = baton->client;
|
||
|
+ http_options options = baton->options;
|
||
|
http_flags flags = client->u.http.flags;
|
||
|
http_code code;
|
||
|
sds msg;
|
||
|
@@ -545,7 +556,7 @@
|
||
|
msg = sdsnewlen(pmseries_failure, sizeof(pmseries_failure) - 1);
|
||
|
flags |= HTTP_FLAG_JSON;
|
||
|
}
|
||
|
- http_reply(client, msg, code, flags);
|
||
|
+ http_reply(client, msg, code, flags, options);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
@@ -555,6 +566,14 @@
|
||
|
fprintf(stderr, "series module setup (arg=%p)\n", arg);
|
||
|
}
|
||
|
|
||
|
+static void
|
||
|
+pmseries_log(pmLogLevel level, sds message, void *arg)
|
||
|
+{
|
||
|
+ pmSeriesBaton *baton = (pmSeriesBaton *)arg;
|
||
|
+
|
||
|
+ proxylog(level, message, baton->client->proxy);
|
||
|
+}
|
||
|
+
|
||
|
static pmSeriesSettings pmseries_settings = {
|
||
|
.callbacks.on_match = on_pmseries_match,
|
||
|
.callbacks.on_desc = on_pmseries_desc,
|
||
|
@@ -567,7 +586,7 @@
|
||
|
.callbacks.on_label = on_pmseries_label,
|
||
|
.callbacks.on_done = on_pmseries_done,
|
||
|
.module.on_setup = pmseries_setup,
|
||
|
- .module.on_info = proxylog,
|
||
|
+ .module.on_info = pmseries_log,
|
||
|
};
|
||
|
|
||
|
static void
|
||
|
@@ -686,7 +705,6 @@
|
||
|
case RESTKEY_PING:
|
||
|
break;
|
||
|
|
||
|
- case RESTKEY_NONE:
|
||
|
default:
|
||
|
client->u.http.parser.status_code = HTTP_STATUS_BAD_REQUEST;
|
||
|
break;
|
||
|
@@ -702,15 +720,16 @@
|
||
|
pmseries_request_url(struct client *client, sds url, dict *parameters)
|
||
|
{
|
||
|
pmSeriesBaton *baton;
|
||
|
- pmSeriesRestKey key;
|
||
|
+ pmSeriesRestCommand *command;
|
||
|
|
||
|
- if ((key = pmseries_lookup_restkey(url)) == RESTKEY_NONE)
|
||
|
+ if ((command = pmseries_lookup_rest_command(url)) == NULL)
|
||
|
return 0;
|
||
|
|
||
|
if ((baton = calloc(1, sizeof(*baton))) != NULL) {
|
||
|
client->u.http.data = baton;
|
||
|
baton->client = client;
|
||
|
- baton->restkey = key;
|
||
|
+ baton->restkey = command->key;
|
||
|
+ baton->options = command->options;
|
||
|
pmseries_setup_request_parameters(client, baton, parameters);
|
||
|
} else {
|
||
|
client->u.http.parser.status_code = HTTP_STATUS_INTERNAL_SERVER_ERROR;
|
||
|
@@ -794,10 +813,12 @@
|
||
|
|
||
|
if (baton->query == NULL) {
|
||
|
message = sdsnewlen(failed, sizeof(failed) - 1);
|
||
|
- http_reply(client, message, HTTP_STATUS_BAD_REQUEST, HTTP_FLAG_JSON);
|
||
|
+ http_reply(client, message, HTTP_STATUS_BAD_REQUEST,
|
||
|
+ HTTP_FLAG_JSON, baton->options);
|
||
|
} else if (baton->working) {
|
||
|
message = sdsnewlen(loading, sizeof(loading) - 1);
|
||
|
- http_reply(client, message, HTTP_STATUS_CONFLICT, HTTP_FLAG_JSON);
|
||
|
+ http_reply(client, message, HTTP_STATUS_CONFLICT,
|
||
|
+ HTTP_FLAG_JSON, baton->options);
|
||
|
} else {
|
||
|
uv_queue_work(client->proxy->events, &baton->loading,
|
||
|
pmseries_load_work, pmseries_load_done);
|
||
|
@@ -810,8 +831,17 @@
|
||
|
pmSeriesBaton *baton = (pmSeriesBaton *)client->u.http.data;
|
||
|
int sts;
|
||
|
|
||
|
- if (client->u.http.parser.status_code)
|
||
|
+ if (client->u.http.parser.status_code) {
|
||
|
+ on_pmseries_done(-EINVAL, baton);
|
||
|
+ return 1;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (client->u.http.parser.method == HTTP_OPTIONS ||
|
||
|
+ client->u.http.parser.method == HTTP_TRACE ||
|
||
|
+ client->u.http.parser.method == HTTP_HEAD) {
|
||
|
+ on_pmseries_done(0, baton);
|
||
|
return 0;
|
||
|
+ }
|
||
|
|
||
|
switch (baton->restkey) {
|
||
|
case RESTKEY_QUERY:
|
||
|
--- a/src/pmproxy/src/webapi.c 2020-04-17 15:39:17.000000000 +1000
|
||
|
+++ b/src/pmproxy/src/webapi.c 2020-06-11 13:10:57.399576484 +1000
|
||
|
@@ -1,5 +1,5 @@
|
||
|
/*
|
||
|
- * Copyright (c) 2019 Red Hat.
|
||
|
+ * Copyright (c) 2019-2020 Red Hat.
|
||
|
*
|
||
|
* This program is free software; you can redistribute it and/or modify it
|
||
|
* under the terms of the GNU Lesser General Public License as published
|
||
|
@@ -18,8 +18,7 @@
|
||
|
#include "util.h"
|
||
|
|
||
|
typedef enum pmWebRestKey {
|
||
|
- RESTKEY_NONE = 0,
|
||
|
- RESTKEY_CONTEXT,
|
||
|
+ RESTKEY_CONTEXT = 1,
|
||
|
RESTKEY_METRIC,
|
||
|
RESTKEY_FETCH,
|
||
|
RESTKEY_INDOM,
|
||
|
@@ -32,7 +31,8 @@
|
||
|
|
||
|
typedef struct pmWebRestCommand {
|
||
|
const char *name;
|
||
|
- unsigned int size;
|
||
|
+ unsigned int namelen : 16;
|
||
|
+ unsigned int options : 16;
|
||
|
pmWebRestKey key;
|
||
|
} pmWebRestCommand;
|
||
|
|
||
|
@@ -47,6 +47,7 @@
|
||
|
sds password; /* from basic auth header */
|
||
|
unsigned int times : 1;
|
||
|
unsigned int compat : 1;
|
||
|
+ unsigned int options : 16;
|
||
|
unsigned int numpmids;
|
||
|
unsigned int numvsets;
|
||
|
unsigned int numinsts;
|
||
|
@@ -56,21 +57,31 @@
|
||
|
} pmWebGroupBaton;
|
||
|
|
||
|
static pmWebRestCommand commands[] = {
|
||
|
- { .key = RESTKEY_CONTEXT, .name = "context", .size = sizeof("context")-1 },
|
||
|
- { .key = RESTKEY_PROFILE, .name = "profile", .size = sizeof("profile")-1 },
|
||
|
- { .key = RESTKEY_SCRAPE, .name = "metrics", .size = sizeof("metrics")-1 },
|
||
|
- { .key = RESTKEY_METRIC, .name = "metric", .size = sizeof("metric")-1 },
|
||
|
- { .key = RESTKEY_DERIVE, .name = "derive", .size = sizeof("derive")-1 },
|
||
|
- { .key = RESTKEY_FETCH, .name = "fetch", .size = sizeof("fetch")-1 },
|
||
|
- { .key = RESTKEY_INDOM, .name = "indom", .size = sizeof("indom")-1 },
|
||
|
- { .key = RESTKEY_STORE, .name = "store", .size = sizeof("store")-1 },
|
||
|
- { .key = RESTKEY_CHILD, .name = "children", .size = sizeof("children")-1 },
|
||
|
- { .key = RESTKEY_NONE }
|
||
|
+ { .key = RESTKEY_CONTEXT, .options = HTTP_OPTIONS_GET,
|
||
|
+ .name = "context", .namelen = sizeof("context")-1 },
|
||
|
+ { .key = RESTKEY_PROFILE, .options = HTTP_OPTIONS_GET,
|
||
|
+ .name = "profile", .namelen = sizeof("profile")-1 },
|
||
|
+ { .key = RESTKEY_SCRAPE, .options = HTTP_OPTIONS_GET,
|
||
|
+ .name = "metrics", .namelen = sizeof("metrics")-1 },
|
||
|
+ { .key = RESTKEY_METRIC, .options = HTTP_OPTIONS_GET,
|
||
|
+ .name = "metric", .namelen = sizeof("metric")-1 },
|
||
|
+ { .key = RESTKEY_DERIVE, .options = HTTP_OPTIONS_GET | HTTP_OPTIONS_POST,
|
||
|
+ .name = "derive", .namelen = sizeof("derive")-1 },
|
||
|
+ { .key = RESTKEY_FETCH, .options = HTTP_OPTIONS_GET,
|
||
|
+ .name = "fetch", .namelen = sizeof("fetch")-1 },
|
||
|
+ { .key = RESTKEY_INDOM, .options = HTTP_OPTIONS_GET,
|
||
|
+ .name = "indom", .namelen = sizeof("indom")-1 },
|
||
|
+ { .key = RESTKEY_STORE, .options = HTTP_OPTIONS_GET,
|
||
|
+ .name = "store", .namelen = sizeof("store")-1 },
|
||
|
+ { .key = RESTKEY_CHILD, .options = HTTP_OPTIONS_GET,
|
||
|
+ .name = "children", .namelen = sizeof("children")-1 },
|
||
|
+ { .name = NULL } /* sentinel */
|
||
|
};
|
||
|
|
||
|
static pmWebRestCommand openmetrics[] = {
|
||
|
- { .key = RESTKEY_SCRAPE, .name = "/metrics", .size = sizeof("/metrics")-1 },
|
||
|
- { .key = RESTKEY_NONE }
|
||
|
+ { .key = RESTKEY_SCRAPE, .options = HTTP_OPTIONS_GET,
|
||
|
+ .name = "/metrics", .namelen = sizeof("/metrics")-1 },
|
||
|
+ { .name = NULL } /* sentinel */
|
||
|
};
|
||
|
|
||
|
static sds PARAM_NAMES, PARAM_NAME, PARAM_PMIDS, PARAM_PMID,
|
||
|
@@ -78,8 +89,8 @@
|
||
|
PARAM_CONTEXT, PARAM_CLIENT;
|
||
|
|
||
|
|
||
|
-static pmWebRestKey
|
||
|
-pmwebapi_lookup_restkey(sds url, unsigned int *compat, sds *context)
|
||
|
+static pmWebRestCommand *
|
||
|
+pmwebapi_lookup_rest_command(sds url, unsigned int *compat, sds *context)
|
||
|
{
|
||
|
pmWebRestCommand *cp;
|
||
|
const char *name, *ctxid = NULL;
|
||
|
@@ -94,7 +105,7 @@
|
||
|
name++;
|
||
|
} while (isdigit((int)(*name)));
|
||
|
if (*name++ != '/')
|
||
|
- return RESTKEY_NONE;
|
||
|
+ return NULL;
|
||
|
*context = sdsnewlen(ctxid, name - ctxid - 1);
|
||
|
}
|
||
|
if (*name == '_') {
|
||
|
@@ -102,13 +113,13 @@
|
||
|
*compat = 1; /* backward-compatibility mode */
|
||
|
}
|
||
|
for (cp = &commands[0]; cp->name; cp++)
|
||
|
- if (strncmp(cp->name, name, cp->size) == 0)
|
||
|
- return cp->key;
|
||
|
+ if (strncmp(cp->name, name, cp->namelen) == 0)
|
||
|
+ return cp;
|
||
|
}
|
||
|
for (cp = &openmetrics[0]; cp->name; cp++)
|
||
|
- if (strncmp(cp->name, url, cp->size) == 0)
|
||
|
- return cp->key;
|
||
|
- return RESTKEY_NONE;
|
||
|
+ if (strncmp(cp->name, url, cp->namelen) == 0)
|
||
|
+ return cp;
|
||
|
+ return NULL;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
@@ -584,9 +595,10 @@
|
||
|
{
|
||
|
pmWebGroupBaton *baton = (pmWebGroupBaton *)arg;
|
||
|
struct client *client = (struct client *)baton->client;
|
||
|
- sds quoted, msg;
|
||
|
+ http_options options = baton->options;
|
||
|
http_flags flags = client->u.http.flags;
|
||
|
http_code code;
|
||
|
+ sds quoted, msg;
|
||
|
|
||
|
if (pmDebugOptions.series)
|
||
|
fprintf(stderr, "%s: client=%p (sts=%d,msg=%s)\n", "on_pmwebapi_done",
|
||
|
@@ -596,7 +608,9 @@
|
||
|
code = HTTP_STATUS_OK;
|
||
|
/* complete current response with JSON suffix if needed */
|
||
|
if ((msg = baton->suffix) == NULL) { /* empty OK response */
|
||
|
- if (flags & HTTP_FLAG_JSON) {
|
||
|
+ if (flags & HTTP_FLAG_NO_BODY) {
|
||
|
+ msg = sdsempty();
|
||
|
+ } else if (flags & HTTP_FLAG_JSON) {
|
||
|
msg = sdsnewlen("{", 1);
|
||
|
if (context)
|
||
|
msg = sdscatfmt(msg, "\"context\":%S,", context);
|
||
|
@@ -628,10 +642,18 @@
|
||
|
sdsfree(quoted);
|
||
|
}
|
||
|
|
||
|
- http_reply(client, msg, code, flags);
|
||
|
+ http_reply(client, msg, code, flags, options);
|
||
|
client_put(client);
|
||
|
}
|
||
|
|
||
|
+static void
|
||
|
+on_pmwebapi_info(pmLogLevel level, sds message, void *arg)
|
||
|
+{
|
||
|
+ pmWebGroupBaton *baton = (pmWebGroupBaton *)arg;
|
||
|
+
|
||
|
+ proxylog(level, message, baton->client->proxy);
|
||
|
+}
|
||
|
+
|
||
|
static pmWebGroupSettings pmwebapi_settings = {
|
||
|
.callbacks.on_context = on_pmwebapi_context,
|
||
|
.callbacks.on_metric = on_pmwebapi_metric,
|
||
|
@@ -645,7 +667,7 @@
|
||
|
.callbacks.on_scrape_labels = on_pmwebapi_scrape_labels,
|
||
|
.callbacks.on_check = on_pmwebapi_check,
|
||
|
.callbacks.on_done = on_pmwebapi_done,
|
||
|
- .module.on_info = proxylog,
|
||
|
+ .module.on_info = on_pmwebapi_info,
|
||
|
};
|
||
|
|
||
|
/*
|
||
|
@@ -734,7 +756,6 @@
|
||
|
client->u.http.flags |= HTTP_FLAG_JSON;
|
||
|
break;
|
||
|
|
||
|
- case RESTKEY_NONE:
|
||
|
default:
|
||
|
client->u.http.parser.status_code = HTTP_STATUS_BAD_REQUEST;
|
||
|
break;
|
||
|
@@ -750,11 +771,11 @@
|
||
|
pmwebapi_request_url(struct client *client, sds url, dict *parameters)
|
||
|
{
|
||
|
pmWebGroupBaton *baton;
|
||
|
- pmWebRestKey key;
|
||
|
+ pmWebRestCommand *command;
|
||
|
unsigned int compat = 0;
|
||
|
sds context = NULL;
|
||
|
|
||
|
- if ((key = pmwebapi_lookup_restkey(url, &compat, &context)) == RESTKEY_NONE) {
|
||
|
+ if (!(command = pmwebapi_lookup_rest_command(url, &compat, &context))) {
|
||
|
sdsfree(context);
|
||
|
return 0;
|
||
|
}
|
||
|
@@ -762,7 +783,8 @@
|
||
|
if ((baton = calloc(1, sizeof(*baton))) != NULL) {
|
||
|
client->u.http.data = baton;
|
||
|
baton->client = client;
|
||
|
- baton->restkey = key;
|
||
|
+ baton->restkey = command->key;
|
||
|
+ baton->options = command->options;
|
||
|
baton->compat = compat;
|
||
|
baton->context = context;
|
||
|
pmwebapi_setup_request_parameters(client, baton, parameters);
|
||
|
@@ -885,17 +907,27 @@
|
||
|
uv_loop_t *loop = client->proxy->events;
|
||
|
uv_work_t *work;
|
||
|
|
||
|
- /* fail early if something has already gone wrong */
|
||
|
- if (client->u.http.parser.status_code != 0)
|
||
|
+ /* take a reference on the client to prevent freeing races on close */
|
||
|
+ client_get(client);
|
||
|
+
|
||
|
+ if (client->u.http.parser.status_code) {
|
||
|
+ on_pmwebapi_done(NULL, -EINVAL, NULL, baton);
|
||
|
return 1;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (client->u.http.parser.method == HTTP_OPTIONS ||
|
||
|
+ client->u.http.parser.method == HTTP_TRACE ||
|
||
|
+ client->u.http.parser.method == HTTP_HEAD) {
|
||
|
+ on_pmwebapi_done(NULL, 0, NULL, baton);
|
||
|
+ return 0;
|
||
|
+ }
|
||
|
|
||
|
- if ((work = (uv_work_t *)calloc(1, sizeof(uv_work_t))) == NULL)
|
||
|
+ if ((work = (uv_work_t *)calloc(1, sizeof(uv_work_t))) == NULL) {
|
||
|
+ client_put(client);
|
||
|
return 1;
|
||
|
+ }
|
||
|
work->data = baton;
|
||
|
|
||
|
- /* take a reference on the client to prevent freeing races on close */
|
||
|
- client_get(client);
|
||
|
-
|
||
|
/* submit command request to worker thread */
|
||
|
switch (baton->restkey) {
|
||
|
case RESTKEY_CONTEXT:
|
||
|
@@ -925,11 +957,10 @@
|
||
|
case RESTKEY_SCRAPE:
|
||
|
uv_queue_work(loop, work, pmwebapi_scrape, pmwebapi_work_done);
|
||
|
break;
|
||
|
- case RESTKEY_NONE:
|
||
|
default:
|
||
|
+ pmwebapi_work_done(work, -EINVAL);
|
||
|
client->u.http.parser.status_code = HTTP_STATUS_BAD_REQUEST;
|
||
|
- client_put(client);
|
||
|
- free(work);
|
||
|
+ on_pmwebapi_done(NULL, -EINVAL, NULL, baton);
|
||
|
return 1;
|
||
|
}
|
||
|
return 0;
|
||
|
--- a/src/pmproxy/src/http.h 2019-12-02 16:43:20.000000000 +1100
|
||
|
+++ b/src/pmproxy/src/http.h 2020-06-11 13:10:57.398576470 +1000
|
||
|
@@ -1,5 +1,5 @@
|
||
|
/*
|
||
|
- * Copyright (c) 2019 Red Hat.
|
||
|
+ * Copyright (c) 2019-2020 Red Hat.
|
||
|
*
|
||
|
* This program is free software; you can redistribute it and/or modify it
|
||
|
* under the terms of the GNU Lesser General Public License as published
|
||
|
@@ -34,29 +34,39 @@
|
||
|
HTTP_FLAG_JSON = (1<<0),
|
||
|
HTTP_FLAG_TEXT = (1<<1),
|
||
|
HTTP_FLAG_HTML = (1<<2),
|
||
|
- HTTP_FLAG_JS = (1<<3),
|
||
|
- HTTP_FLAG_CSS = (1<<4),
|
||
|
- HTTP_FLAG_ICO = (1<<5),
|
||
|
- HTTP_FLAG_JPG = (1<<6),
|
||
|
- HTTP_FLAG_PNG = (1<<7),
|
||
|
- HTTP_FLAG_GIF = (1<<8),
|
||
|
HTTP_FLAG_UTF8 = (1<<10),
|
||
|
HTTP_FLAG_UTF16 = (1<<11),
|
||
|
+ HTTP_FLAG_NO_BODY = (1<<13),
|
||
|
HTTP_FLAG_COMPRESS = (1<<14),
|
||
|
HTTP_FLAG_STREAMING = (1<<15),
|
||
|
/* maximum 16 for server.h */
|
||
|
} http_flags;
|
||
|
|
||
|
+typedef enum http_options {
|
||
|
+ HTTP_OPT_GET = (1 << HTTP_GET),
|
||
|
+ HTTP_OPT_PUT = (1 << HTTP_PUT),
|
||
|
+ HTTP_OPT_HEAD = (1 << HTTP_HEAD),
|
||
|
+ HTTP_OPT_POST = (1 << HTTP_POST),
|
||
|
+ HTTP_OPT_TRACE = (1 << HTTP_TRACE),
|
||
|
+ HTTP_OPT_OPTIONS = (1 << HTTP_OPTIONS),
|
||
|
+ /* maximum 16 in command opts fields */
|
||
|
+} http_options;
|
||
|
+
|
||
|
+#define HTTP_COMMON_OPTIONS (HTTP_OPT_HEAD | HTTP_OPT_TRACE | HTTP_OPT_OPTIONS)
|
||
|
+#define HTTP_OPTIONS_GET (HTTP_COMMON_OPTIONS | HTTP_OPT_GET)
|
||
|
+#define HTTP_OPTIONS_PUT (HTTP_COMMON_OPTIONS | HTTP_OPT_PUT)
|
||
|
+#define HTTP_OPTIONS_POST (HTTP_COMMON_OPTIONS | HTTP_OPT_POST)
|
||
|
+#define HTTP_SERVER_OPTIONS (HTTP_OPTIONS_GET | HTTP_OPT_PUT | HTTP_OPT_POST)
|
||
|
+
|
||
|
typedef unsigned int http_code;
|
||
|
|
||
|
extern void http_transfer(struct client *);
|
||
|
-extern void http_reply(struct client *, sds, http_code, http_flags);
|
||
|
+extern void http_reply(struct client *, sds, http_code, http_flags, http_options);
|
||
|
extern void http_error(struct client *, http_code, const char *);
|
||
|
|
||
|
extern int http_decode(const char *, size_t, sds);
|
||
|
extern const char *http_status_mapping(http_code);
|
||
|
extern const char *http_content_type(http_flags);
|
||
|
-extern http_flags http_suffix_type(const char *);
|
||
|
|
||
|
extern sds http_get_buffer(struct client *);
|
||
|
extern void http_set_buffer(struct client *, sds, http_flags);
|
||
|
--- a/qa/1837 1970-01-01 10:00:00.000000000 +1000
|
||
|
+++ b/qa/1837 2020-06-11 13:10:57.396576440 +1000
|
||
|
@@ -0,0 +1,55 @@
|
||
|
+#!/bin/sh
|
||
|
+# PCP QA Test No. 1837
|
||
|
+# Exercise PMWEBAPI handling server OPTIONS.
|
||
|
+#
|
||
|
+# Copyright (c) 2020 Red Hat. All Rights Reserved.
|
||
|
+#
|
||
|
+
|
||
|
+seq=`basename $0`
|
||
|
+echo "QA output created by $seq"
|
||
|
+
|
||
|
+# get standard environment, filters and checks
|
||
|
+. ./common.product
|
||
|
+. ./common.filter
|
||
|
+. ./common.check
|
||
|
+
|
||
|
+_check_series
|
||
|
+which curl >/dev/null 2>&1 || _notrun "No curl binary installed"
|
||
|
+curl --request-targets 2>&1 | grep -q 'requires parameter' && \
|
||
|
+ _notrun "Test requires curl --request-targets option"
|
||
|
+
|
||
|
+status=1 # failure is the default!
|
||
|
+$sudo rm -rf $tmp.* $seq.full
|
||
|
+trap "cd $here; _cleanup; exit \$status" 0 1 2 3 15
|
||
|
+
|
||
|
+pmproxy_was_running=false
|
||
|
+[ -f $PCP_RUN_DIR/pmproxy.pid ] && pmproxy_was_running=true
|
||
|
+echo "pmproxy_was_running=$pmproxy_was_running" >>$here/$seq.full
|
||
|
+
|
||
|
+_cleanup()
|
||
|
+{
|
||
|
+ if $pmproxy_was_running
|
||
|
+ then
|
||
|
+ echo "Restart pmproxy ..." >>$here/$seq.full
|
||
|
+ _service pmproxy restart >>$here/$seq.full 2>&1
|
||
|
+ _wait_for_pmproxy
|
||
|
+ else
|
||
|
+ echo "Stopping pmproxy ..." >>$here/$seq.full
|
||
|
+ _service pmproxy stop >>$here/$seq.full 2>&1
|
||
|
+ fi
|
||
|
+ $sudo rm -f $tmp.*
|
||
|
+}
|
||
|
+
|
||
|
+# real QA test starts here
|
||
|
+_service pmproxy restart >/dev/null 2>&1
|
||
|
+
|
||
|
+curl -isS --request-target "*" -X OPTIONS http://localhost:44322 \
|
||
|
+ 2>&1 | tee -a $here/$seq.full | _webapi_header_filter
|
||
|
+
|
||
|
+echo >>$here/$seq.full
|
||
|
+echo "=== pmproxy log ===" >>$here/$seq.full
|
||
|
+cat $PCP_LOG_DIR/pmproxy/pmproxy.log >>$here/$seq.full
|
||
|
+
|
||
|
+# success, all done
|
||
|
+status=0
|
||
|
+exit
|
||
|
--- a/qa/1837.out 1970-01-01 10:00:00.000000000 +1000
|
||
|
+++ b/qa/1837.out 2020-06-11 13:10:57.397576455 +1000
|
||
|
@@ -0,0 +1,6 @@
|
||
|
+QA output created by 1837
|
||
|
+
|
||
|
+Access-Control-Allow-Methods: GET, PUT, HEAD, POST, TRACE, OPTIONS
|
||
|
+Content-Length: 0
|
||
|
+Date: DATE
|
||
|
+HTTP/1.1 200 OK
|
||
|
--- a/qa/780 2020-04-14 14:41:41.000000000 +1000
|
||
|
+++ b/qa/780 2020-06-11 13:10:57.397576455 +1000
|
||
|
@@ -1,8 +1,8 @@
|
||
|
#!/bin/sh
|
||
|
# PCP QA Test No. 780
|
||
|
-# Exercise PMWEBAPI Access-Control-Allow-Origin HTTP header.
|
||
|
+# Exercise PMWEBAPI CORS headers.
|
||
|
#
|
||
|
-# Copyright (c) 2014,2019 Red Hat.
|
||
|
+# Copyright (c) 2014,2019-2020 Red Hat.
|
||
|
#
|
||
|
|
||
|
seq=`basename $0`
|
||
|
@@ -16,7 +16,6 @@
|
||
|
_check_series
|
||
|
which curl >/dev/null 2>&1 || _notrun "No curl binary installed"
|
||
|
|
||
|
-signal=$PCP_BINADM_DIR/pmsignal
|
||
|
status=1 # failure is the default!
|
||
|
$sudo rm -rf $tmp.* $seq.full
|
||
|
trap "cd $here; _cleanup; exit \$status" 0 1 2 3 15
|
||
|
@@ -39,13 +38,21 @@
|
||
|
$sudo rm -f $tmp.*
|
||
|
}
|
||
|
|
||
|
-unset http_proxy
|
||
|
-unset HTTP_PROXY
|
||
|
-
|
||
|
# real QA test starts here
|
||
|
_service pmproxy restart >/dev/null 2>&1
|
||
|
|
||
|
-curl -s -S "http://localhost:44323/pmapi/context" -I | _webapi_header_filter
|
||
|
+echo "=== Basic" | tee -a $here/$seq.full
|
||
|
+curl -IsS "http://localhost:44323/pmapi/context" | _webapi_header_filter
|
||
|
+
|
||
|
+echo "=== Preflight" | tee -a $here/$seq.full
|
||
|
+curl -isS -X OPTIONS "http://localhost:44323/series/query?expr=hinv*" | _webapi_header_filter
|
||
|
+
|
||
|
+echo "=== OK Request Method" | tee -a $here/$seq.full
|
||
|
+curl -isS -X OPTIONS -H "Origin: http://example.com" -H "Access-Control-Request-Method: GET" "http://localhost:44323/pmapi/context" | _webapi_header_filter
|
||
|
+
|
||
|
+echo "=== Bad Request Method" | tee -a $here/$seq.full
|
||
|
+curl -isS -X OPTIONS -H "Origin: http://example.com" -H "Access-Control-Request-Method: BAD" "http://localhost:44323/pmapi/context" | _webapi_header_filter
|
||
|
+
|
||
|
echo >>$here/$seq.full
|
||
|
echo "=== pmproxy log ===" >>$here/$seq.full
|
||
|
cat $PCP_LOG_DIR/pmproxy/pmproxy.log >>$here/$seq.full
|
||
|
--- a/qa/780.out 2020-03-23 09:47:47.000000000 +1100
|
||
|
+++ b/qa/780.out 2020-06-11 13:10:57.397576455 +1000
|
||
|
@@ -1,8 +1,27 @@
|
||
|
QA output created by 780
|
||
|
+=== Basic
|
||
|
|
||
|
Access-Control-Allow-Headers: Accept, Accept-Language, Content-Language, Content-Type
|
||
|
Access-Control-Allow-Origin: *
|
||
|
-Content-Length: SIZE
|
||
|
Content-Type: application/json
|
||
|
Date: DATE
|
||
|
HTTP/1.1 200 OK
|
||
|
+Transfer-encoding: chunked
|
||
|
+=== Preflight
|
||
|
+
|
||
|
+Access-Control-Allow-Methods: GET, HEAD, POST, TRACE, OPTIONS
|
||
|
+Content-Length: 0
|
||
|
+Date: DATE
|
||
|
+HTTP/1.1 200 OK
|
||
|
+=== OK Request Method
|
||
|
+
|
||
|
+Access-Control-Allow-Methods: GET, HEAD, TRACE, OPTIONS
|
||
|
+Access-Control-Allow-Origin: http://example.com
|
||
|
+Content-Length: 0
|
||
|
+Date: DATE
|
||
|
+HTTP/1.1 200 OK
|
||
|
+=== Bad Request Method
|
||
|
+
|
||
|
+Content-Length: 0
|
||
|
+Date: DATE
|
||
|
+HTTP/1.1 405 Method Not Allowed
|
||
|
--- a/qa/common.check 2020-05-20 10:51:37.000000000 +1000
|
||
|
+++ b/qa/common.check 2020-06-11 13:10:57.397576455 +1000
|
||
|
@@ -2696,7 +2696,7 @@
|
||
|
tee -a $here/$seq.full \
|
||
|
| col -b \
|
||
|
| sed \
|
||
|
- -e 's/^\(Content-Length:\) [0-9][0-9]*/\1 SIZE/g' \
|
||
|
+ -e 's/^\(Content-Length:\) [1-9][0-9]*/\1 SIZE/g' \
|
||
|
-e 's/^\(Date:\).*/\1 DATE/g' \
|
||
|
-e 's/\(\"context\":\) [0-9][0-9]*/\1 CTXID/g' \
|
||
|
-e '/^Connection: Keep-Alive/d' \
|
||
|
--- a/qa/group 2020-05-28 09:15:22.000000000 +1000
|
||
|
+++ b/qa/group 2020-06-11 13:10:57.397576455 +1000
|
||
|
@@ -1757,6 +1757,7 @@
|
||
|
1724 pmda.bpftrace local python
|
||
|
1768 pmfind local
|
||
|
1793 pmrep pcp2xxx python local
|
||
|
+1837 pmproxy local
|
||
|
1855 pmda.rabbitmq local
|
||
|
1896 pmlogger logutil pmlc local
|
||
|
4751 libpcp threads valgrind local pcp
|
||
|
--- a/qa/1211.out 2020-01-20 16:53:42.000000000 +1100
|
||
|
+++ b/qa/1211.out 2020-06-11 13:10:57.399576484 +1000
|
||
|
@@ -507,9 +507,11 @@
|
||
|
Perform simple source-based query ...
|
||
|
|
||
|
Error handling - descriptor for bad series identifier
|
||
|
-pmseries: [Error] no descriptor for series identifier no.such.identifier
|
||
|
|
||
|
no.such.identifier
|
||
|
+ PMID: PM_ID_NULL
|
||
|
+ Data Type: ??? InDom: unknown 0xffffffff
|
||
|
+ Semantics: unknown Units: unknown
|
||
|
|
||
|
Error handling - metric name for bad series identifier
|
||
|
|
||
|
--- a/src/libpcp_web/src/query.c 2020-01-20 15:43:31.000000000 +1100
|
||
|
+++ b/src/libpcp_web/src/query.c 2020-06-11 13:10:57.399576484 +1000
|
||
|
@@ -1938,11 +1938,15 @@
|
||
|
return -EPROTO;
|
||
|
}
|
||
|
|
||
|
- /* sanity check - were we given an invalid series identifier? */
|
||
|
+ /* were we given a non-metric series identifier? (e.g. an instance) */
|
||
|
if (elements[0]->type == REDIS_REPLY_NIL) {
|
||
|
- infofmt(msg, "no descriptor for series identifier %s", series);
|
||
|
- batoninfo(baton, PMLOG_ERROR, msg);
|
||
|
- return -EINVAL;
|
||
|
+ desc->indom = sdscpylen(desc->indom, "unknown", 7);
|
||
|
+ desc->pmid = sdscpylen(desc->pmid, "PM_ID_NULL", 10);
|
||
|
+ desc->semantics = sdscpylen(desc->semantics, "unknown", 7);
|
||
|
+ desc->source = sdscpylen(desc->source, "unknown", 7);
|
||
|
+ desc->type = sdscpylen(desc->type, "unknown", 7);
|
||
|
+ desc->units = sdscpylen(desc->units, "unknown", 7);
|
||
|
+ return 0;
|
||
|
}
|
||
|
|
||
|
if (extract_string(baton, series, elements[0], &desc->indom, "indom") < 0)
|