* Most tests work with HTTP/2 support using nghttp2
* except non-ssl http/1->/2 upgrade * Restconf: ensure HEAD method works everywhere GET does.
This commit is contained in:
parent
b680e3c5ac
commit
84f5762ab5
59 changed files with 1683 additions and 1107 deletions
|
|
@ -54,6 +54,8 @@
|
|||
#include <arpa/inet.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
#include <openssl/ssl.h>
|
||||
|
||||
/* cligen */
|
||||
#include <cligen/cligen.h>
|
||||
|
||||
|
|
@ -61,13 +63,12 @@
|
|||
#include <clixon/clixon.h>
|
||||
|
||||
#ifdef HAVE_LIBEVHTP
|
||||
/* evhtp */
|
||||
#include <event2/buffer.h> /* evbuffer */
|
||||
#define EVHTP_DISABLE_REGEX
|
||||
#define EVHTP_DISABLE_EVTHR
|
||||
|
||||
#include <evhtp/evhtp.h>
|
||||
#include <evhtp/sslutils.h> /* XXX inline this / use SSL directly */
|
||||
|
||||
#endif /* HAVE_LIBEVHTP */
|
||||
|
||||
#ifdef HAVE_LIBNGHTTP2 /* To get restconf_native.h include files right */
|
||||
|
|
@ -81,7 +82,9 @@
|
|||
#include "restconf_err.h"
|
||||
#include "restconf_root.h"
|
||||
#include "restconf_native.h" /* Restconf-openssl mode specific headers*/
|
||||
#ifdef HAVE_LIBEVHTP
|
||||
#include "restconf_evhtp.h" /* evhtp http/1 */
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_LIBEVHTP
|
||||
static char*
|
||||
|
|
@ -264,15 +267,19 @@ evhtp_params_set(clicon_handle h,
|
|||
goto fail;
|
||||
}
|
||||
clicon_debug(1, "%s conn->ssl:%d", __FUNCTION__, req->conn->ssl?1:0);
|
||||
/* Slightly awkward way of taking cert subject and CN and add it to restconf parameters
|
||||
* instead of accessing it directly */
|
||||
if ((ssl = req->conn->ssl) != NULL){
|
||||
if (restconf_param_set(h, "HTTPS", "https") < 0) /* some string or NULL */
|
||||
goto done;
|
||||
/* SSL subject fields, eg CN (Common Name) , can add more here? */
|
||||
if ((subject = (char*)htp_sslutil_subject_tostr(req->conn->ssl)) != NULL){
|
||||
if (ssl_x509_name_oneline(req->conn->ssl, &subject) < 0)
|
||||
goto done;
|
||||
if (subject != NULL) {
|
||||
if (uri_str2cvec(subject, '/', '=', 1, &cvv) < 0)
|
||||
goto done;
|
||||
if ((cn = cvec_find_str(cvv, "CN")) != NULL){
|
||||
if (restconf_param_set(h, "SSL_CN", cn) < 0)
|
||||
if (restconf_param_set(h, "SSL_CN", cn) < 0) /* Can be used by callback */
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
|
@ -298,6 +305,111 @@ evhtp_params_set(clicon_handle h,
|
|||
goto done;
|
||||
}
|
||||
|
||||
/*! We got -1 back from lower layers, create a 500 Internal Server error
|
||||
* Catch all on fatal error. This does not terminate the process but closes request
|
||||
* stream
|
||||
*/
|
||||
static int
|
||||
evhtp_internal_error(evhtp_request_t *req)
|
||||
{
|
||||
if (strlen(clicon_err_reason) &&
|
||||
req->buffer_out){
|
||||
evbuffer_add_printf(req->buffer_out, "%s", clicon_err_reason);
|
||||
evhtp_send_reply(req, EVHTP_RES_500);
|
||||
}
|
||||
clicon_err_reset();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Send reply
|
||||
* @see htp__create_reply_
|
||||
*/
|
||||
static int
|
||||
native_send_reply(restconf_conn *rc,
|
||||
restconf_stream_data *sd,
|
||||
evhtp_request_t *req)
|
||||
{
|
||||
int retval = -1;
|
||||
|
||||
cg_var *cv;
|
||||
int minor;
|
||||
int major;
|
||||
|
||||
switch (req->proto) {
|
||||
case EVHTP_PROTO_10:
|
||||
if (req->flags & EVHTP_REQ_FLAG_KEEPALIVE) {
|
||||
/* protocol is HTTP/1.0 and clients wants to keep established */
|
||||
if (restconf_reply_header(sd, "Connection", "keep-alive") < 0)
|
||||
goto done;
|
||||
}
|
||||
major = htparser_get_major(req->conn->parser); /* XXX Look origin */
|
||||
minor = htparser_get_minor(req->conn->parser);
|
||||
break;
|
||||
case EVHTP_PROTO_11:
|
||||
if (!(req->flags & EVHTP_REQ_FLAG_KEEPALIVE)) {
|
||||
/* protocol is HTTP/1.1 but client wanted to close */
|
||||
if (restconf_reply_header(sd, "Connection", "keep-alive") < 0)
|
||||
goto done;
|
||||
}
|
||||
major = htparser_get_major(req->conn->parser);
|
||||
minor = htparser_get_minor(req->conn->parser);
|
||||
break;
|
||||
default:
|
||||
/* this sometimes happens when a response is made but paused before
|
||||
* the method has been parsed */
|
||||
major = 1;
|
||||
minor = 0;
|
||||
break;
|
||||
}
|
||||
cprintf(sd->sd_outp_buf, "HTTP/%u.%u %u %s\r\n",
|
||||
major,
|
||||
minor,
|
||||
sd->sd_code,
|
||||
restconf_code2reason(sd->sd_code));
|
||||
/* Loop over headers */
|
||||
cv = NULL;
|
||||
while ((cv = cvec_each(sd->sd_outp_hdrs, cv)) != NULL)
|
||||
cprintf(sd->sd_outp_buf, "%s: %s\r\n", cv_name_get(cv), cv_string_get(cv));
|
||||
cprintf(sd->sd_outp_buf, "\r\n");
|
||||
// cvec_reset(rc->rc_outp_hdrs); /* Is now done in restconf_connection but can be done here */
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*!
|
||||
*/
|
||||
static int
|
||||
restconf_evhtp_reply(restconf_conn *rc,
|
||||
restconf_stream_data *sd,
|
||||
evhtp_request_t *req)
|
||||
{
|
||||
int retval = -1;
|
||||
|
||||
req->status = sd->sd_code;
|
||||
req->flags |= EVHTP_REQ_FLAG_FINISHED; /* Signal to evhtp to read next request */
|
||||
/* If body, add a content-length header
|
||||
* A server MUST NOT send a Content-Length header field in any response
|
||||
* with a status code of 1xx (Informational) or 204 (No Content). A
|
||||
* server MUST NOT send a Content-Length header field in any 2xx
|
||||
* (Successful) response to a CONNECT request (Section 4.3.6 of
|
||||
* [RFC7231]).
|
||||
*/
|
||||
if (restconf_reply_header(sd, "Content-Length", "%d",
|
||||
(sd->sd_body!=NULL)?cbuf_len(sd->sd_body):0) < 0)
|
||||
goto done;
|
||||
/* Create reply and write headers */
|
||||
if (native_send_reply(rc, sd, req) < 0)
|
||||
goto done;
|
||||
/* Write a body */
|
||||
if (sd->sd_body){
|
||||
cbuf_append_str(sd->sd_outp_buf, cbuf_get(sd->sd_body));
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Callback for each incoming http request for path /
|
||||
*
|
||||
* This are all messages except /.well-known, Registered with evhtp_set_cb
|
||||
|
|
@ -314,16 +426,35 @@ void
|
|||
restconf_path_root(evhtp_request_t *req,
|
||||
void *arg)
|
||||
{
|
||||
int retval = -1;
|
||||
clicon_handle h;
|
||||
int ret;
|
||||
cvec *qvec = NULL;
|
||||
int retval = -1;
|
||||
clicon_handle h;
|
||||
int ret;
|
||||
cvec *qvec = NULL;
|
||||
evhtp_connection_t *evconn;
|
||||
restconf_conn *rc;
|
||||
restconf_stream_data *sd;
|
||||
|
||||
clicon_debug(1, "------------");
|
||||
if ((h = (clicon_handle)arg) == NULL){
|
||||
clicon_err(OE_RESTCONF, EINVAL, "arg is NULL");
|
||||
evhtp_internal_error(req);
|
||||
goto done;
|
||||
}
|
||||
/* evhtp connect struct */
|
||||
if ((evconn = evhtp_request_get_connection(req)) == NULL){
|
||||
clicon_err(OE_DAEMON, EFAULT, "evhtp_request_get_connection");
|
||||
evhtp_internal_error(req);
|
||||
goto done;
|
||||
}
|
||||
/* get clixon request connect pointer from generic evhtp application pointer */
|
||||
rc = evconn->arg;
|
||||
if ((sd = restconf_stream_find(rc, 0)) == NULL){
|
||||
clicon_err(OE_RESTCONF, EINVAL, "No stream_data");
|
||||
evhtp_internal_error(req);
|
||||
goto done;
|
||||
}
|
||||
sd->sd_req = req;
|
||||
sd->sd_proto = (req->proto == EVHTP_PROTO_10)?HTTP_10:HTTP_11;
|
||||
/* input debug */
|
||||
if (clicon_debug_get())
|
||||
evhtp_headers_for_each(req->headers_in, evhtp_print_header, h);
|
||||
|
|
@ -332,27 +463,55 @@ restconf_path_root(evhtp_request_t *req,
|
|||
/* Query vector, ie the ?a=x&b=y stuff */
|
||||
if ((qvec = cvec_new(0)) ==NULL){
|
||||
clicon_err(OE_UNIX, errno, "cvec_new");
|
||||
evhtp_internal_error(req);
|
||||
goto done;
|
||||
}
|
||||
/* set fcgi-like paramaters (ignore query vector) */
|
||||
if ((ret = evhtp_params_set(h, req, qvec)) < 0)
|
||||
/* Get indata
|
||||
*/
|
||||
{
|
||||
size_t len;
|
||||
unsigned char *buf;
|
||||
|
||||
len = evbuffer_get_length(req->buffer_in);
|
||||
if (len > 0){
|
||||
if ((buf = evbuffer_pullup(req->buffer_in, len)) == NULL){
|
||||
clicon_err(OE_CFG, errno, "evbuffer_pullup");
|
||||
goto done;
|
||||
}
|
||||
/* Note the pullup may not be null-terminated */
|
||||
cbuf_append_buf(sd->sd_indata, buf, len);
|
||||
}
|
||||
}
|
||||
/* set fcgi-like paramaters (ignore query vector)
|
||||
* ret = 0 means an error has already been sent
|
||||
*/
|
||||
if ((ret = evhtp_params_set(h, req, qvec)) < 0){
|
||||
evhtp_internal_error(req);
|
||||
goto done;
|
||||
}
|
||||
if (ret == 1){
|
||||
/* call generic function */
|
||||
if (api_root_restconf(h, req, qvec) < 0)
|
||||
goto done;
|
||||
if (api_root_restconf(h, sd, qvec) < 0){
|
||||
evhtp_internal_error(req);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
/* Clear (fcgi) paramaters from this request */
|
||||
if (restconf_param_del_all(h) < 0)
|
||||
/* Clear input request parameters from this request */
|
||||
if (restconf_param_del_all(h) < 0){
|
||||
evhtp_internal_error(req);
|
||||
goto done;
|
||||
}
|
||||
/* All parameters for sending a reply are here
|
||||
*/
|
||||
if (sd->sd_code){
|
||||
if (restconf_evhtp_reply(rc, sd, req) < 0){
|
||||
evhtp_internal_error(req);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
clicon_debug(1, "%s %d", __FUNCTION__, retval);
|
||||
/* Catch all on fatal error. This does not terminate the process but closes request stream */
|
||||
if (retval < 0){
|
||||
evhtp_send_reply(req, EVHTP_RES_ERROR);
|
||||
}
|
||||
if (qvec)
|
||||
cvec_free(qvec);
|
||||
return; /* void */
|
||||
|
|
@ -368,37 +527,67 @@ void
|
|||
restconf_path_wellknown(evhtp_request_t *req,
|
||||
void *arg)
|
||||
{
|
||||
int retval = -1;
|
||||
clicon_handle h;
|
||||
int ret;
|
||||
int retval = -1;
|
||||
clicon_handle h;
|
||||
int ret;
|
||||
evhtp_connection_t *evconn;
|
||||
restconf_conn *rc;
|
||||
restconf_stream_data *sd;
|
||||
|
||||
clicon_debug(1, "------------");
|
||||
if ((h = (clicon_handle)arg) == NULL){
|
||||
clicon_err(OE_RESTCONF, EINVAL, "arg is NULL");
|
||||
evhtp_internal_error(req);
|
||||
goto done;
|
||||
}
|
||||
/* evhtp connect struct */
|
||||
if ((evconn = evhtp_request_get_connection(req)) == NULL){
|
||||
clicon_err(OE_DAEMON, EFAULT, "evhtp_request_get_connection");
|
||||
evhtp_internal_error(req);
|
||||
goto done;
|
||||
}
|
||||
/* get clixon request connect pointer from generic evhtp application pointer */
|
||||
rc = evconn->arg;
|
||||
if ((sd = restconf_stream_find(rc, 0)) == NULL){
|
||||
clicon_err(OE_RESTCONF, EINVAL, "No stream_data");
|
||||
evhtp_internal_error(req);
|
||||
goto done;
|
||||
}
|
||||
sd->sd_req = req;
|
||||
sd->sd_proto = (req->proto == EVHTP_PROTO_10)?HTTP_10:HTTP_11;
|
||||
/* input debug */
|
||||
if (clicon_debug_get())
|
||||
evhtp_headers_for_each(req->headers_in, evhtp_print_header, h);
|
||||
/* get accepted connection */
|
||||
|
||||
/* set fcgi-like paramaters (ignore query vector) */
|
||||
if ((ret = evhtp_params_set(h, req, NULL)) < 0)
|
||||
if ((ret = evhtp_params_set(h, req, NULL)) < 0){
|
||||
evhtp_internal_error(req);
|
||||
goto done;
|
||||
}
|
||||
if (ret == 1){
|
||||
/* call generic function */
|
||||
if (api_well_known(h, req) < 0)
|
||||
if (api_well_known(h, sd) < 0){
|
||||
evhtp_internal_error(req);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
/* Clear (fcgi) paramaters from this request */
|
||||
if (restconf_param_del_all(h) < 0)
|
||||
/* Clear input request parameters from this request */
|
||||
if (restconf_param_del_all(h) < 0){
|
||||
evhtp_internal_error(req);
|
||||
goto done;
|
||||
}
|
||||
/* All parameters for sending a reply are here
|
||||
*/
|
||||
if (sd->sd_code){
|
||||
if (restconf_evhtp_reply(rc, sd, req) < 0){
|
||||
evhtp_internal_error(req);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
/* Catch all on fatal error. This does not terminate the process but closes request stream */
|
||||
if (retval < 0){
|
||||
evhtp_send_reply(req, EVHTP_RES_ERROR);
|
||||
}
|
||||
clicon_debug(1, "%s %d", __FUNCTION__, retval);
|
||||
return; /* void */
|
||||
}
|
||||
#endif /* HAVE_LIBEVHTP */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue