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)