Merge branch 'nate-i50189-http-context' into 'master'

See merge request SchedMD/dev/slurm!1949
diff --git a/src/common/http.c b/src/common/http.c
index 73e03c8..cd68e92 100644
--- a/src/common/http.c
+++ b/src/common/http.c
@@ -411,3 +411,50 @@
 	dst->query = xstrdup(src->query);
 	dst->fragment = xstrdup(src->fragment);
 }
+
+extern void free_http_header(http_header_t *header)
+{
+	xassert(header->magic == HTTP_HEADER_MAGIC);
+	xfree(header->name);
+	xfree(header->value);
+	header->magic = ~HTTP_HEADER_MAGIC;
+	xfree(header);
+}
+
+/* find operator against http_header_t */
+static int _http_header_find_key(void *x, void *y)
+{
+	const http_header_t *entry = x;
+	const char *key = y;
+
+	xassert(entry->name);
+	xassert(entry->magic == HTTP_HEADER_MAGIC);
+
+	if (key == NULL)
+		return 0;
+
+	/* case insensitive compare per rfc2616:4.2 */
+	if (entry->name && !xstrcasecmp(entry->name, key))
+		return 1;
+	else
+		return 0;
+}
+
+extern const char *find_http_header(list_t *headers, const char *name)
+{
+	http_header_t *header = NULL;
+
+	if (!headers || !name)
+		return NULL;
+
+	header = (http_header_t *) list_find_first(headers,
+						   _http_header_find_key,
+						   (void *) name);
+
+	if (header) {
+		xassert(header->magic == HTTP_HEADER_MAGIC);
+		return header->value;
+	}
+
+	return NULL;
+}
diff --git a/src/common/http.h b/src/common/http.h
index 8705a2a..1d2aa4b 100644
--- a/src/common/http.h
+++ b/src/common/http.h
@@ -209,4 +209,23 @@
 /* Copy all members in URL */
 extern void url_copy_members(url_t *dst, const url_t *src);
 
+#define HTTP_HEADER_MAGIC 0x1aaffbe2
+
+/* HTTP header */
+typedef struct {
+	int magic; /* HTTP_HEADER_MAGIC */
+	char *name;
+	char *value;
+} http_header_t;
+
+/* Free http header and contents */
+extern void free_http_header(http_header_t *header);
+
+/* find http header from header list
+ * IN headers - list_t of http_header_t*
+ * IN name - name of header to find
+ * RET ptr to header value or NULL if not found
+ */
+extern const char *find_http_header(list_t *headers, const char *name);
+
 #endif /* SLURM_HTTP_H */
diff --git a/src/conmgr/con.c b/src/conmgr/con.c
index fdaaa44..c06f301 100644
--- a/src/conmgr/con.c
+++ b/src/conmgr/con.c
@@ -1931,8 +1931,7 @@
 	xassert(con->refs >= 0);
 
 	ref->magic = ~MAGIC_CON_MGR_FD_REF;
-	xfree(ref);
-	*ref_ptr = NULL;
+	xfree((*ref_ptr));
 }
 
 extern void conmgr_fd_free_ref(conmgr_fd_ref_t **ref_ptr)
diff --git a/src/conmgr/conmgr.h b/src/conmgr/conmgr.h
index abd7549..a582b46 100644
--- a/src/conmgr/conmgr.h
+++ b/src/conmgr/conmgr.h
@@ -426,7 +426,7 @@
 extern int conmgr_queue_send_fd(conmgr_fd_t *con, int fd);
 
 /*
- * Write binary data to connection (from callback).
+ * Write binary data to connection
  * NOTE: type=CON_TYPE_RAW only
  * IN con connection manager connection struct
  * IN buffer pointer to buffer
@@ -437,6 +437,17 @@
 				   const size_t bytes);
 
 /*
+ * Copy and write binary data to connection
+ * NOTE: type=CON_TYPE_RAW only
+ * IN ref reference to connection
+ * IN buffer pointer to buffer
+ * IN bytes number of bytes in buffer to write
+ * RET SLURM_SUCCESS or error
+ */
+extern int conmgr_con_queue_write_data(conmgr_fd_ref_t *ref, const void *buffer,
+				       const size_t bytes);
+
+/*
  * Write packed msg to connection (from callback).
  * NOTE: type=CON_TYPE_RPC only
  * IN con conmgr connection ptr
diff --git a/src/conmgr/io.c b/src/conmgr/io.c
index ba0bcd6..890d914 100644
--- a/src/conmgr/io.c
+++ b/src/conmgr/io.c
@@ -422,24 +422,33 @@
 	con->in->size = size;
 }
 
-extern int conmgr_queue_write_data(conmgr_fd_t *con, const void *buffer,
-				   const size_t bytes)
+/* Copy buffer into new buf_t */
+static buf_t *_buf_clone(const void *buffer, const size_t bytes)
 {
-	buf_t *buf;
+	buf_t *buf = NULL;
 
-	xassert(con->magic == MAGIC_CON_MGR_FD);
-
-	/* Ignore empty write requests */
-	if (!bytes)
-		return SLURM_SUCCESS;
+	xassert(bytes > 0);
 
 	buf = init_buf(bytes);
 
-	/* TODO: would be nice to avoid this copy */
 	memmove(get_buf_data(buf), buffer, bytes);
 
-	log_flag(NET, "%s: [%s] write of %zu bytes queued",
-		 __func__, con->name, bytes);
+	return buf;
+}
+
+/*
+ * Append buffer to output queue
+ * WARNING: caller must hold mgr.mutex lock
+ * IN con - connection to queue outgoing buffer
+ * IN buf - buffer to queue as output (takes ownership)
+ * RET SLURM_SUCCESS or error
+ */
+static int _append_output(conmgr_fd_t *con, buf_t *buf)
+{
+	xassert(con->magic == MAGIC_CON_MGR_FD);
+
+	log_flag(NET, "%s: [%s] write of %u bytes queued",
+		 __func__, con->name, get_buf_offset(buf));
 
 	log_flag_hex(NET_RAW, get_buf_data(buf), get_buf_offset(buf),
 		     "%s: queuing up write", __func__);
@@ -449,12 +458,54 @@
 	if (con_flag(con, FLAG_WATCH_WRITE_TIMEOUT))
 		con->last_write = timespec_now();
 
-	slurm_mutex_lock(&mgr.mutex);
 	EVENT_SIGNAL(&mgr.watch_sleep);
-	slurm_mutex_unlock(&mgr.mutex);
+
 	return SLURM_SUCCESS;
 }
 
+static int _write_data(conmgr_fd_t *con, const void *buffer, const size_t bytes)
+{
+	int rc = EINVAL;
+	buf_t *buf = NULL;
+
+	xassert(con->magic == MAGIC_CON_MGR_FD);
+
+	/* Ignore empty write requests */
+	if (!bytes)
+		return SLURM_SUCCESS;
+
+	/* TODO: would be nice to avoid this copy */
+	buf = _buf_clone(buffer, bytes);
+
+	slurm_mutex_lock(&mgr.mutex);
+	rc = _append_output(con, buf);
+	slurm_mutex_unlock(&mgr.mutex);
+
+	return rc;
+}
+
+extern int conmgr_queue_write_data(conmgr_fd_t *con, const void *buffer,
+				   const size_t bytes)
+{
+	xassert(con->magic == MAGIC_CON_MGR_FD);
+	xassert(buffer || !bytes);
+
+	return _write_data(con, buffer, bytes);
+}
+
+extern int conmgr_con_queue_write_data(conmgr_fd_ref_t *ref, const void *buffer,
+				       const size_t bytes)
+{
+	if (!ref)
+		return EINVAL;
+
+	xassert(ref->magic == MAGIC_CON_MGR_FD_REF);
+	xassert(ref->con->magic == MAGIC_CON_MGR_FD);
+	xassert(buffer || !bytes);
+
+	return _write_data(ref->con, buffer, bytes);
+}
+
 static int _get_input_buffer(const conmgr_fd_t *con, const void **data_ptr,
 			     size_t *bytes_ptr)
 {
diff --git a/src/slurmrestd/http.c b/src/slurmrestd/http.c
index 666dc3a..e4915f4 100644
--- a/src/slurmrestd/http.c
+++ b/src/slurmrestd/http.c
@@ -50,6 +50,8 @@
 #include "src/common/xmalloc.h"
 #include "src/common/xstring.h"
 
+#include "src/conmgr/conmgr.h"
+
 #include "src/interfaces/http_parser.h"
 
 #include "src/slurmrestd/http.h"
@@ -59,10 +61,8 @@
 #define MAGIC 0xDFAFFEEF
 #define MAX_BODY_BYTES 52428800 /* 50MB */
 
-#define MAGIC_REQUEST_T 0xdbadaaaf
 /* Data to handed around by http_parser to call backs */
 typedef struct {
-	int magic;
 	/* Requested URL */
 	url_t url;
 	/* Request HTTP method */
@@ -76,8 +76,6 @@
 	/* RFC7230-6.1 "Connection: Close" */
 	bool connection_close;
 	int expect; /* RFC7231-5.1.1 expect requested */
-	/* Connection context */
-	http_context_t *context;
 	/* Body of request (may be NULL) */
 	char *body;
 	/* if provided: expected body length to process or 0 */
@@ -93,6 +91,22 @@
 	} http_version;
 } request_t;
 
+typedef struct http_context_s {
+	int magic; /* MAGIC */
+	/* reference to assigned connection */
+	conmgr_fd_ref_t *ref;
+	/* assigned connection */
+	conmgr_fd_t *con;
+	/* Authentication context (auth_context_type_t) */
+	void *auth;
+	/* callback to call on each HTTP request */
+	on_http_request_t on_http_request;
+	/* http parser plugin state */
+	http_parser_state_t *parser;
+	/* http request_t */
+	request_t request;
+} http_context_t;
+
 /* default keep_alive value which appears to be implementation specific */
 static int DEFAULT_KEEP_ALIVE = 5; //default to 5s to match apache2
 
@@ -110,13 +124,6 @@
 	return ESLURM_HTTP_UNSUPPORTED_VERSION;
 }
 
-extern void free_http_header(http_header_entry_t *header)
-{
-	xfree(header->name);
-	xfree(header->value);
-	xfree(header);
-}
-
 static void _free_http_header(void *header)
 {
 	free_http_header(header);
@@ -124,16 +131,14 @@
 
 static void _request_init(http_context_t *context)
 {
-	request_t *request = context->request;
+	request_t *request = &context->request;
 
 	xassert(request);
 	xassert(context->magic == MAGIC);
 
 	*request = (request_t) {
-		.magic = MAGIC_REQUEST_T,
 		.url = URL_INITIALIZER,
 		.method = HTTP_REQUEST_INVALID,
-		.context = context,
 		.keep_alive = -1,
 	};
 
@@ -142,11 +147,9 @@
 
 static void _request_free_members(http_context_t *context)
 {
-	request_t *request = context->request;
+	request_t *request = &context->request;
 
 	xassert(context->magic == MAGIC);
-	xassert(request->magic == MAGIC_REQUEST_T);
-	xassert(request->context == context);
 
 	url_free_members(&request->url);
 	FREE_NULL_LIST(request->headers);
@@ -170,12 +173,11 @@
 
 static int _on_request(const http_parser_request_t *req, void *arg)
 {
-	request_t *request = arg;
-	http_context_t *context = request->context;
+	http_context_t *context = arg;
+	request_t *request = &context->request;
 	int rc = EINVAL;
 
 	xassert(context->magic == MAGIC);
-	xassert(request->magic == MAGIC_REQUEST_T);
 
 	request->http_version.major = req->http_version.major;
 	request->http_version.minor = req->http_version.minor;
@@ -228,12 +230,11 @@
 
 static int _on_header(const http_parser_header_t *header, void *arg)
 {
-	request_t *request = arg;
-	http_context_t *context = request->context;
-	http_header_entry_t *entry = NULL;
+	http_context_t *context = arg;
+	request_t *request = &context->request;
+	http_header_t *entry = NULL;
 
 	xassert(context->magic == MAGIC);
-	xassert(request->magic == MAGIC_REQUEST_T);
 
 	log_flag(NET, "%s: [%s] Header: %s Value: %s",
 		 __func__, conmgr_con_get_name(context->ref), header->name,
@@ -241,6 +242,7 @@
 
 	/* Add copy to list of headers */
 	entry = xmalloc(sizeof(*entry));
+	entry->magic = HTTP_HEADER_MAGIC;
 	entry->name = xstrdup(header->name);
 	entry->value = xstrdup(header->value);
 	list_append(request->headers, entry);
@@ -264,8 +266,7 @@
 			request->keep_alive = ibuffer;
 		} else {
 			error("%s: [%s] invalid Keep-Alive value %s",
-			      __func__,
-			      conmgr_fd_get_name(request->context->con),
+			      __func__, conmgr_con_get_name(context->ref),
 			      header->value);
 			return _send_reject(
 				context, HTTP_STATUS_CODE_ERROR_NOT_ACCEPTABLE,
@@ -319,11 +320,10 @@
 
 static int _on_headers_complete(void *arg)
 {
-	request_t *request = arg;
-	http_context_t *context = request->context;
+	http_context_t *context = arg;
+	request_t *request = &context->request;
 
 	xassert(context->magic == MAGIC);
-	xassert(request->magic == MAGIC_REQUEST_T);
 
 	if ((request->http_version.major == 1) &&
 	    (request->http_version.minor == 0)) {
@@ -355,13 +355,14 @@
 	if (request->expect) {
 		int rc = EINVAL;
 		send_http_response_args_t args = {
-			.con = context->con,
 			.http_major = request->http_version.major,
 			.http_minor = request->http_version.minor,
 			.status_code = request->expect,
 			.body_length = 0,
 		};
 
+		args.con = conmgr_fd_get_ref(context->ref);
+
 		if ((rc = send_http_response(&args)))
 			return rc;
 	}
@@ -371,13 +372,12 @@
 
 static int _on_content(const http_parser_content_t *content, void *arg)
 {
-	request_t *request = arg;
-	http_context_t *context = request->context;
+	http_context_t *context = arg;
+	request_t *request = &context->request;
 	const void *at = get_buf_data(content->buffer);
 	const size_t length = get_buf_offset(content->buffer);
 
 	xassert(context->magic == MAGIC);
-	xassert(request->magic == MAGIC_REQUEST_T);
 
 	log_flag_hex(NET_RAW, at, length, "%s: [%s] received HTTP content",
 	       __func__, conmgr_con_get_name(context->ref));
@@ -456,7 +456,7 @@
 static int _write_fmt_header(conmgr_fd_t *con, const char *name,
 			     const char *value)
 {
-	const char *buffer = _fmt_header(name, value);
+	char *buffer = _fmt_header(name, value);
 	int rc = conmgr_queue_write_data(con, buffer, strlen(buffer));
 	xfree(buffer);
 	return rc;
@@ -474,7 +474,13 @@
 	return xstrdup_printf("%s: %zu" CRLF, name, value);
 }
 
-extern int send_http_connection_close(http_context_t *ctxt)
+/*
+ * Send HTTP close notification header
+ *	Warns the client that we are about to close the connection.
+ * IN ctxt - connection context
+ * RET SLURM_SUCCESS or error
+ */
+static int _send_http_connection_close(http_context_t *ctxt)
 {
 	return _write_fmt_header(ctxt->con, "Connection", "Close");
 }
@@ -520,8 +526,9 @@
 	/* send along any requested headers */
 	if (args->headers) {
 		list_itr_t *itr = list_iterator_create(args->headers);
-		http_header_entry_t *header = NULL;
+		http_header_t *header = NULL;
 		while ((header = list_next(itr))) {
+			xassert(header->magic == HTTP_HEADER_MAGIC);
 			if ((rc = _write_fmt_header(args->con, header->name,
 						    header->value)))
 				break;
@@ -578,10 +585,8 @@
 static int _send_reject(http_context_t *context, http_status_code_t status_code,
 			slurm_err_t error_number)
 {
-	request_t *request = context->request;
-	xassert(request->magic == MAGIC_REQUEST_T);
+	request_t *request = &context->request;
 	send_http_response_args_t args = {
-		.con = request->context->con,
 		.http_major = request->http_version.major,
 		.http_minor = request->http_version.minor,
 		.status_code = status_code,
@@ -591,6 +596,8 @@
 
 	xassert(context->magic == MAGIC);
 
+	args.con = conmgr_fd_get_ref(context->ref);
+
 	/* If we don't have a requested client version, default to 0.9 */
 	if ((args.http_major == 0) && (args.http_minor == 0))
 		args.http_minor = 9;
@@ -602,10 +609,11 @@
 	if (request->connection_close ||
 	    _valid_http_version(request->http_version.major,
 				request->http_version.minor))
-		send_http_connection_close(request->context);
+		_send_http_connection_close(context);
 
 	/* ensure connection gets closed */
-	(void) conmgr_queue_close_fd(request->context->con);
+	conmgr_con_queue_close_free(&context->ref);
+	context->con = NULL;
 
 	/* reset connection to avoid any possible auth inheritance */
 	_request_reset(context);
@@ -613,10 +621,10 @@
 	return error_number;
 }
 
-static int _on_message_complete_request(request_t *request)
+static int _on_message_complete_request(http_context_t *context)
 {
 	int rc = EINVAL;
-	http_context_t *context = request->context;
+	request_t *request = &context->request;
 	on_http_request_args_t args = {
 		.method = request->method,
 		.headers = request->headers,
@@ -633,24 +641,28 @@
 	};
 
 	xassert(context->magic == MAGIC);
-	xassert(request->magic == MAGIC_REQUEST_T);
 
-	if ((rc = context->on_http_request(&args)))
+	if (!(args.con = conmgr_con_link(context->ref)) ||
+	    !(args.name = conmgr_con_get_name(args.con))) {
+		rc = SLURM_COMMUNICATIONS_MISSING_SOCKET_ERROR;
+		log_flag(NET, "%s: connection missing: %s",
+			 __func__, slurm_strerror(rc));
+	} else if ((rc = context->on_http_request(&args)))
 		log_flag(NET, "%s: [%s] on_http_request rejected: %s",
 			 __func__, conmgr_con_get_name(context->ref),
 			 slurm_strerror(rc));
 
+	conmgr_fd_free_ref(&args.con);
 	return rc;
 }
 
 static int _on_content_complete(void *arg)
 {
-	request_t *request = arg;
-	http_context_t *context = request->context;
+	http_context_t *context = arg;
+	request_t *request = &context->request;
 	int rc = EINVAL;
 
 	xassert(context->magic == MAGIC);
-	xassert(request->magic == MAGIC_REQUEST_T);
 
 	if ((request->expected_body_length > 0) &&
 	    (request->expected_body_length != request->body_length)) {
@@ -661,7 +673,7 @@
 				    ESLURM_HTTP_INVALID_CONTENT_LENGTH);
 	}
 
-	if ((rc = _on_message_complete_request(request)))
+	if ((rc = _on_message_complete_request(context)))
 		return rc;
 
 	if (request->keep_alive) {
@@ -673,7 +685,7 @@
 	if (request->connection_close) {
 		/* Notify client that this connection will be closed now */
 		if (request->connection_close)
-			send_http_connection_close(context);
+			_send_http_connection_close(context);
 
 		conmgr_con_queue_close_free(&context->ref);
 		context->con = NULL;
@@ -695,19 +707,15 @@
 		.on_content_complete = _on_content_complete,
 	};
 	int rc = SLURM_SUCCESS;
-	request_t *request = context->request;
 	ssize_t bytes_parsed = -1;
 	buf_t *buffer = NULL;
 
 	xassert(context->magic == MAGIC);
-	xassert(context->con);
-	xassert(context->ref);
-	xassert(request->magic == MAGIC_REQUEST_T);
-	xassert(request->context == context);
+	xassert(conmgr_fd_get_ref(context->ref) == context->con);
 
 	if (!context->parser &&
 	    (rc = http_parser_g_new_parse_request(
-		     conmgr_con_get_name(context->ref), &callbacks, request,
+		     conmgr_con_get_name(context->ref), &callbacks, context,
 		     &context->parser))) {
 		log_flag(NET, "%s: [%s] Creating new HTTP parser failed: %s",
 			 __func__, conmgr_con_get_name(context->ref),
@@ -754,59 +762,16 @@
 	return rc;
 }
 
-static http_context_t *_http_context_new(void)
-{
-	http_context_t *context = xmalloc(sizeof(*context));
-	context->magic = MAGIC;
-	return context;
-}
-
-/* find operator against http_header_entry_t */
-static int _http_header_find_key(void *x, void *y)
-{
-	http_header_entry_t *entry = (http_header_entry_t *)x;
-	const char *key = (const char *)y;
-	xassert(entry->name);
-
-	if (key == NULL)
-		return 0;
-	/* case insensitive compare per rfc2616:4.2 */
-	if (entry->name && !xstrcasecmp(entry->name, key))
-		return 1;
-	else
-		return 0;
-}
-
-extern const char *find_http_header(list_t *headers, const char *name)
-{
-	http_header_entry_t *header = NULL;
-
-	if (!headers || !name)
-		return NULL;
-
-	header = (http_header_entry_t *)list_find_first(
-		headers, _http_header_find_key, (void *)name);
-
-	if (header)
-		return header->value;
-	else
-		return NULL;
-}
-
 extern http_context_t *setup_http_context(conmgr_fd_t *con,
 					  on_http_request_t on_http_request)
 {
-	http_context_t *context = _http_context_new();
+	http_context_t *context = xmalloc(sizeof(*context));
 
-	xassert(context->magic == MAGIC);
-	xassert(!context->con);
-	xassert(!context->request);
+	context->magic = MAGIC;
 	context->con = con;
 	context->ref = conmgr_fd_new_ref(con);
 	context->on_http_request = on_http_request;
 
-	/* Must use type since context->request is void ptr */
-	context->request = xmalloc(sizeof(request_t));
 	_request_init(context);
 
 	return context;
@@ -815,7 +780,6 @@
 extern void on_http_connection_finish(conmgr_fd_t *con, void *ctxt)
 {
 	http_context_t *context = (http_context_t *) ctxt;
-	request_t *request = NULL;
 
 	if (!context)
 		return;
@@ -824,19 +788,51 @@
 	http_parser_g_free_parse_request(&context->parser);
 
 	/* release request */
-	request = context->request;
-	xassert(request->magic == MAGIC_REQUEST_T);
 	_request_free_members(context);
-	request->magic = ~MAGIC_REQUEST_T;
-	xfree(context->request);
 
 	/* auth should have been released long before now */
 	xassert(!context->auth);
 	FREE_NULL_REST_AUTH(context->auth);
 
+	xassert(conmgr_fd_get_ref(context->ref) == context->con);
 	conmgr_fd_free_ref(&context->ref);
 	context->con = NULL;
 
 	context->magic = ~MAGIC;
 	xfree(context);
 }
+
+extern void *http_context_get_auth(http_context_t *context)
+{
+	if (!context)
+		return NULL;
+
+	xassert(context->magic == MAGIC);
+
+	return context->auth;
+}
+
+extern void *http_context_set_auth(http_context_t *context, void *auth)
+{
+	void *old = NULL;
+
+	if (!context)
+		return auth;
+
+	xassert(context->magic == MAGIC);
+
+	old = context->auth;
+	context->auth = auth;
+
+	return old;
+}
+
+extern void http_context_free_null_auth(http_context_t *context)
+{
+	if (!context)
+		return;
+
+	xassert(context->magic == MAGIC);
+
+	FREE_NULL_REST_AUTH(context->auth);
+}
diff --git a/src/slurmrestd/http.h b/src/slurmrestd/http.h
index c219807..ec2aee7 100644
--- a/src/slurmrestd/http.h
+++ b/src/slurmrestd/http.h
@@ -43,7 +43,6 @@
 #include "src/common/list.h"
 
 #include "src/conmgr/conmgr.h"
-#include "src/interfaces/http_parser.h"
 
 struct on_http_request_args_s;
 typedef struct on_http_request_args_s on_http_request_args_t;
@@ -58,28 +57,17 @@
  */
 typedef int (*on_http_request_t)(on_http_request_args_t *args);
 
-typedef struct {
-	int magic;
-	/* reference to assigned connection */
-	conmgr_fd_ref_t *ref;
-	/* assigned connection */
-	conmgr_fd_t *con;
-	/* Authentication context (auth_context_type_t) */
-	void *auth;
-	/* callback to call on each HTTP request */
-	on_http_request_t on_http_request;
-	/* http parser plugin state */
-	http_parser_state_t *parser;
-	/* http request_t */
-	void *request;
-} http_context_t;
+/* Opaque connection context */
+typedef struct http_context_s http_context_t;
 
 typedef struct on_http_request_args_s {
 	const http_request_method_t method; /* HTTP request method */
-	list_t *headers; /* list of http_header_entry_t from client */
+	list_t *headers; /* list_t of http_header_t* from client */
 	const char *path; /* requested URL path (may be NULL) */
 	const char *query; /* requested URL query (may be NULL) */
 	http_context_t *context; /* calling context (do not xfree) */
+	conmgr_fd_ref_t *con; /* reference to connection */
+	const char *name; /* connection name */
 	uint16_t http_major; /* HTTP major version */
 	uint16_t http_minor; /* HTTP minor version */
 	const char *content_type; /* header content-type */
@@ -89,19 +77,6 @@
 	const char *body_encoding; /* body encoding type or NULL */
 } on_http_request_args_t;
 
-typedef struct {
-	char *name;
-	char *value;
-} http_header_entry_t;
-extern void free_http_header(http_header_entry_t *);
-
-/* find http header from header list
- * IN headers List of http_header_entry_t
- * IN name name of header to find
- * RET ptr to header value or NULL if not found
- */
-extern const char *find_http_header(list_t *headers, const char *name);
-
 /*
  * Call back for new connection to setup HTTP
  *
@@ -124,21 +99,14 @@
 	uint16_t http_major; /* HTTP major version */
 	uint16_t http_minor; /* HTTP minor version */
 	http_status_code_t status_code; /* HTTP status code to send */
-	list_t *headers; /* list of http_header_entry_t to send (can be empty) */
+	/* list of http_header_entry_t to send (can be empty) */
+	list_t *headers; /* list_t of http_header_t* from client */
 	const char *body; /* body to send or NULL */
 	size_t body_length; /* bytes in body to send or 0 */
 	const char *body_encoding; /* body encoding type or NULL */
 } send_http_response_args_t;
 
 /*
- * Send HTTP close notification.
- * 	Warns the client that we are about to close the connection.
- * IN args arguments of response
- * RET SLURM_SUCCESS or error
- */
-extern int send_http_connection_close(http_context_t *ctxt);
-
-/*
  * Send HTTP response
  * IN args arguments of response
  * RET SLURM_SUCCESS or error
@@ -161,4 +129,25 @@
  */
 extern void on_http_connection_finish(conmgr_fd_t *con, void *ctxt);
 
+/*
+ * Get (arbitrary) auth pointer from context
+ * IN context - connection context
+ * RET auth pointer or NULL
+ */
+extern void *http_context_get_auth(http_context_t *context);
+
+/*
+ * Set and Get (arbitrary) auth pointer from context
+ * IN context - connection context
+ * IN auth - (arbitrary) auth pointer to set into context
+ * RET Prior auth pointer or auth arg if context==NULL
+ */
+extern void *http_context_set_auth(http_context_t *context, void *auth);
+
+/*
+ * Release and NULL auth pointer from context
+ * IN context - connection context
+ */
+extern void http_context_free_null_auth(http_context_t *context);
+
 #endif /* SLURMRESTD_HTTP_H */
diff --git a/src/slurmrestd/operations.c b/src/slurmrestd/operations.c
index 4dfb9d5..b030a2a 100644
--- a/src/slurmrestd/operations.c
+++ b/src/slurmrestd/operations.c
@@ -46,6 +46,7 @@
 #include "src/common/xstring.h"
 #include "src/interfaces/serializer.h"
 
+#include "src/slurmrestd/http.h"
 #include "src/slurmrestd/operations.h"
 #include "src/slurmrestd/rest_auth.h"
 
@@ -76,11 +77,6 @@
 	float q; /* quality factor (priority) */
 } http_header_accept_t;
 
-static const char *_name(const on_http_request_args_t *args)
-{
-	return conmgr_fd_get_name(args->context->con);
-}
-
 static void _check_path_magic(const path_t *path)
 {
 	xassert(path->magic == PATH_MAGIC);
@@ -225,13 +221,12 @@
 	return rc;
 }
 
-static int _operations_router_reject(const on_http_request_args_t *args,
+static int _operations_router_reject(on_http_request_args_t *args,
 				     const char *err,
 				     http_status_code_t err_code,
 				     const char *body_encoding)
 {
 	send_http_response_args_t send_args = {
-		.con = args->context->con,
 		.headers = list_create(NULL),
 		.http_major = args->http_major,
 		.http_minor = args->http_minor,
@@ -240,18 +235,21 @@
 		.body_encoding = (body_encoding ? body_encoding : "text/plain"),
 		.body_length = (err ? strlen(err) : 0),
 	};
-	http_header_entry_t close = {
+	http_header_t close = {
+		.magic = HTTP_HEADER_MAGIC,
 		.name = "Connection",
 		.value = "Close",
 	};
 
+	send_args.con = conmgr_fd_get_ref(args->con);
+
 	/* Always warn that connection will be closed after the body is sent */
 	list_append(send_args.headers, &close);
 
 	(void) send_http_response(&send_args);
 
 	/* close connection on error */
-	conmgr_queue_close_fd(args->context->con);
+	conmgr_queue_close_fd(send_args.con);
 
 	FREE_NULL_LIST(send_args.headers);
 
@@ -408,7 +406,7 @@
 		*read_mime = MIME_TYPE_URL_ENCODED;
 
 		debug4("%s: [%s] did not provide a known content type header. Assuming URL encoded.",
-		       __func__, _name(args));
+		       __func__, args->name);
 	}
 
 	if (args->accept) {
@@ -419,17 +417,17 @@
 			xassert(ptr->magic == MAGIC_HEADER_ACCEPT);
 
 			debug4("%s: [%s] accepts %s with q=%f",
-			       __func__, _name(args), ptr->type, ptr->q);
+			       __func__, args->name, ptr->type, ptr->q);
 
 			if ((*write_mime = resolve_mime_type(ptr->type,
 							     plugin_ptr))) {
 				debug4("%s: [%s] found accepts %s=%s with q=%f",
-				       __func__, _name(args), ptr->type,
+				       __func__, args->name, ptr->type,
 				       *write_mime, ptr->q);
 				break;
 			} else {
 				debug4("%s: [%s] rejecting accepts %s with q=%f",
-				       __func__, _name(args), ptr->type,
+				       __func__, args->name, ptr->type,
 				       ptr->q);
 			}
 		}
@@ -437,7 +435,7 @@
 		FREE_NULL_LIST(accept);
 	} else {
 		debug3("%s: [%s] Accept header not specified. Defaulting to JSON.",
-		       __func__, _name(args));
+		       __func__, args->name);
 		*write_mime = MIME_TYPE_JSON;
 	}
 
@@ -478,14 +476,14 @@
 		 * requests.
 		 */
 		debug("%s: [%s] Overriding content type from %s to %s for %s",
-		      __func__, _name(args), *read_mime, MIME_TYPE_URL_ENCODED,
+		      __func__, args->name, *read_mime, MIME_TYPE_URL_ENCODED,
 		      get_http_method_string(args->method));
 
 		*read_mime = MIME_TYPE_URL_ENCODED;
 	}
 
 	debug3("%s: [%s] mime read: %s write: %s",
-	       __func__, _name(args), *read_mime, *write_mime);
+	       __func__, args->name, *read_mime, *write_mime);
 
 	return SLURM_SUCCESS;
 }
@@ -500,22 +498,24 @@
 	data_t *resp = data_new();
 	char *body = NULL;
 	http_status_code_t e;
+	void *auth = NULL;
 
 	xassert(op_path);
 	debug3("%s: [%s] BEGIN: calling ctxt handler: 0x%"PRIXPTR"[%d] for path: %s",
-	       __func__, _name(args), (uintptr_t) op_path->callback,
+	       __func__, args->name, (uintptr_t) op_path->callback,
 	       callback_tag, args->path);
 
-	rc = wrap_openapi_ctxt_callback(_name(args), args->method, params,
-					query, callback_tag, resp,
-					args->context->auth, parser, op_path,
-					meta);
+	auth = http_context_set_auth(args->context, NULL);
+
+	rc = wrap_openapi_ctxt_callback(args->name, args->method, params, query,
+					callback_tag, resp, auth, parser,
+					op_path, meta);
 
 	/*
 	 * Clear auth context after callback is complete. Client has to provide
 	 * full auth for every request already.
 	 */
-	FREE_NULL_REST_AUTH(args->context->auth);
+	FREE_NULL_REST_AUTH(auth);
 
 	if (data_get_type(resp) != DATA_TYPE_NULL) {
 		int rc2;
@@ -539,11 +539,11 @@
 		 *
 		 */
 		send_http_response_args_t send_args = {
-			.con = args->context->con,
 			.http_major = args->http_major,
 			.http_minor = args->http_minor,
 			.status_code = HTTP_STATUS_CODE_REDIRECT_NOT_MODIFIED,
 		};
+		send_args.con = conmgr_fd_get_ref(args->con);
 		e = send_args.status_code;
 		rc = send_http_response(&send_args);
 	} else if (rc && (rc != ESLURM_REST_EMPTY_RESULT)) {
@@ -580,7 +580,6 @@
 		rc = _operations_router_reject(args, body, e, write_mime);
 	} else {
 		send_http_response_args_t send_args = {
-			.con = args->context->con,
 			.http_major = args->http_major,
 			.http_minor = args->http_minor,
 			.status_code = HTTP_STATUS_CODE_SUCCESS_OK,
@@ -588,6 +587,8 @@
 			.body_length = 0,
 		};
 
+		send_args.con = conmgr_fd_get_ref(args->con);
+
 		if (body) {
 			send_args.body = body;
 			send_args.body_length = strlen(body);
@@ -599,7 +600,7 @@
 	}
 
 	debug3("%s: [%s] END: calling handler: (0x%"PRIXPTR") callback_tag %d for path: %s rc[%d]=%s status[%d]=%s",
-	       __func__, _name(args), (uintptr_t) op_path->callback,
+	       __func__, args->name, (uintptr_t) op_path->callback,
 	       callback_tag, args->path, rc, slurm_strerror(rc), e,
 	       get_http_status_code_string(e));
 
@@ -621,12 +622,12 @@
 	data_parser_t *parser = NULL;
 
 	info("%s: [%s] %s %s",
-	     __func__, _name(args), get_http_method_string(args->method),
+	     __func__, args->name, get_http_method_string(args->method),
 	     args->path);
 
 	if ((rc = rest_authenticate_http_request(args))) {
 		error("%s: [%s] authentication failed: %s",
-		      __func__, _name(args), slurm_strerror(rc));
+		      __func__, args->name, slurm_strerror(rc));
 		_operations_router_reject(args, "Authentication failure",
 					  HTTP_STATUS_CODE_ERROR_UNAUTHORIZED,
 					  NULL);
@@ -653,7 +654,7 @@
 	slurm_rwlock_unlock(&paths_lock);
 
 	debug5("%s: [%s] found callback handler: (0x%"PRIXPTR") callback_tag=%d path=%s parser=%s",
-	       __func__, _name(args), (uintptr_t) path->op_path->callback,
+	       __func__, args->name, (uintptr_t) path->op_path->callback,
 	       callback_tag, args->path,
 	       (parser ? data_parser_get_plugin(parser) : ""));
 
@@ -671,7 +672,7 @@
 	FREE_NULL_DATA(params);
 
 	/* always clear the auth context */
-	FREE_NULL_REST_AUTH(args->context->auth);
+	http_context_free_null_auth(args->context);
 
 	return rc;
 }
diff --git a/src/slurmrestd/plugins/auth/jwt/jwt.c b/src/slurmrestd/plugins/auth/jwt/jwt.c
index 7811b2e..2990a57 100644
--- a/src/slurmrestd/plugins/auth/jwt/jwt.c
+++ b/src/slurmrestd/plugins/auth/jwt/jwt.c
@@ -91,12 +91,12 @@
 					  rest_auth_context_t *ctxt)
 {
 	plugin_data_t *data;
-	const char *key, *user_name, *bearer, *name;
+	const char *key, *user_name, *bearer;
+	const char *name = args->name;
 
 	key = find_http_header(args->headers, HTTP_HEADER_USER_TOKEN);
 	bearer = find_http_header(args->headers, HTTP_HEADER_AUTH);
 	user_name = find_http_header(args->headers, HTTP_HEADER_USER_NAME);
-	name = conmgr_fd_get_name(args->context->con);
 
 	if (!key && !user_name && !bearer) {
 		debug3("%s: [%s] skipping token authentication",
diff --git a/src/slurmrestd/plugins/auth/local/local.c b/src/slurmrestd/plugins/auth/local/local.c
index f24f413..73084f6 100644
--- a/src/slurmrestd/plugins/auth/local/local.c
+++ b/src/slurmrestd/plugins/auth/local/local.c
@@ -134,15 +134,16 @@
 			const char *header_user_name)
 {
 	int rc;
-	const char *name = conmgr_fd_get_name(args->context->con);
+	const char *name = args->name;
+	conmgr_fd_t *con = conmgr_fd_get_ref(args->con);
 	uid_t cred_uid;
 	gid_t cred_gid;
 	pid_t cred_pid;
 
 	xassert(!ctxt->user_name);
 
-	if ((rc = conmgr_get_fd_auth_creds(args->context->con, &cred_uid,
-					   &cred_gid, &cred_pid))) {
+	if ((rc = conmgr_get_fd_auth_creds(con, &cred_uid, &cred_gid,
+					   &cred_pid))) {
 		/* socket may be remote, local auth doesn't apply */
 		debug("%s: [%s] unable to get socket ownership: %s",
 		      __func__, name, slurm_strerror(rc));
@@ -245,11 +246,11 @@
 	struct stat status = { 0 };
 	const char *header_user_name = find_http_header(args->headers,
 							HTTP_HEADER_USER_NAME);
-	const conmgr_fd_status_t cstatus =
-		conmgr_fd_get_status(args->context->con);
-	const int input_fd = conmgr_fd_get_input_fd(args->context->con);
-	const int output_fd = conmgr_fd_get_output_fd(args->context->con);
-	const char *name = conmgr_fd_get_name(args->context->con);
+	conmgr_fd_t *con = conmgr_fd_get_ref(args->con);
+	const conmgr_fd_status_t cstatus = conmgr_fd_get_status(con);
+	const int input_fd = conmgr_fd_get_input_fd(con);
+	const int output_fd = conmgr_fd_get_output_fd(con);
+	const char *name = args->name;
 
 	xassert(!ctxt->user_name);
 
diff --git a/src/slurmrestd/rest_auth.c b/src/slurmrestd/rest_auth.c
index 3148edf..3b1c379 100644
--- a/src/slurmrestd/rest_auth.c
+++ b/src/slurmrestd/rest_auth.c
@@ -164,18 +164,13 @@
 extern int rest_authenticate_http_request(on_http_request_args_t *args)
 {
 	int rc = ESLURM_AUTH_CRED_INVALID;
-	rest_auth_context_t *context =
-		(rest_auth_context_t *) args->context->auth;
-
-	if (context) {
-		fatal("%s: authentication context already set for connection: %s",
-		      __func__, conmgr_fd_get_name(args->context->con));
-	}
-
-	args->context->auth = context = rest_auth_g_new();
+	rest_auth_context_t *context = rest_auth_g_new();
 
 	_check_magic(context);
 
+	if (http_context_set_auth(args->context, context))
+		fatal_abort("authentication context already set for connection");
+
 	/* continue if already authenticated via plugin */
 	if (context->plugin_id)
 		return rest_auth_g_apply(context);
@@ -194,7 +189,10 @@
 			break;
 	}
 
-	FREE_NULL_REST_AUTH(args->context->auth);
+	if (http_context_set_auth(args->context, NULL) != context)
+		fatal_abort("authentication context unexpectedly changed");
+
+	FREE_NULL_REST_AUTH(context);
 	return rc;
 }