727 lines
20 KiB
C
727 lines
20 KiB
C
/*
|
|
*
|
|
***** BEGIN LICENSE BLOCK *****
|
|
|
|
Copyright (C) 2009-2019 Olof Hagsand
|
|
Copyright (C) 2020-2021 Olof Hagsand and Rubicon Communications, LLC(Netgate)
|
|
|
|
This file is part of CLIXON.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
|
|
Alternatively, the contents of this file may be used under the terms of
|
|
the GNU General Public License Version 3 or later (the "GPL"),
|
|
in which case the provisions of the GPL are applicable instead
|
|
of those above. If you wish to allow use of your version of this file only
|
|
under the terms of the GPL, and not to allow others to
|
|
use your version of this file under the terms of Apache License version 2,
|
|
indicate your decision by deleting the provisions above and replace them with
|
|
the notice and other provisions required by the GPL. If you do not delete
|
|
the provisions above, a recipient may use your version of this file under
|
|
the terms of any one of the Apache License version 2 or the GPL.
|
|
|
|
***** END LICENSE BLOCK *****
|
|
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "clixon_config.h" /* generated by config & autoconf */
|
|
#endif
|
|
|
|
/* The clixon evhtp code can be compiled with or without threading support
|
|
* The choice is set at libevhtp compile time by cmake. Eg:
|
|
* cmake -DEVHTP_DISABLE_EVTHR=ON # Disable threads.
|
|
* Default in testing is disabled threads.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <syslog.h>
|
|
#include <pwd.h>
|
|
#include <ctype.h>
|
|
#include <assert.h>
|
|
#include <signal.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/socket.h>
|
|
#include <arpa/inet.h>
|
|
#include <sys/resource.h>
|
|
|
|
#include <openssl/ssl.h>
|
|
#include <openssl/rand.h>
|
|
#include <openssl/err.h>
|
|
#include <openssl/x509v3.h>
|
|
|
|
/* cligen */
|
|
#include <cligen/cligen.h>
|
|
|
|
/* clicon */
|
|
#include <clixon/clixon.h>
|
|
|
|
#ifdef HAVE_LIBEVHTP /* To get restconf_native.h include files right */
|
|
/* evhtp */
|
|
#include <event2/buffer.h> /* evbuffer */
|
|
#define EVHTP_DISABLE_REGEX
|
|
#define EVHTP_DISABLE_EVTHR
|
|
#include <evhtp/evhtp.h>
|
|
#endif /* HAVE_LIBEVHTP */
|
|
|
|
#ifdef HAVE_LIBNGHTTP2
|
|
#include <nghttp2/nghttp2.h>
|
|
#endif
|
|
|
|
/* restconf */
|
|
#include "restconf_lib.h" /* generic shared with plugins */
|
|
#include "restconf_handle.h"
|
|
#include "restconf_api.h" /* generic not shared with plugins */
|
|
#include "restconf_err.h"
|
|
#include "restconf_root.h"
|
|
#include "restconf_native.h" /* Restconf-openssl mode specific headers*/
|
|
#include "restconf_nghttp2.h" /* Restconf-openssl mode specific headers*/
|
|
|
|
#ifdef HAVE_LIBNGHTTP2 /* XXX MOVE */
|
|
|
|
#define ARRLEN(x) (sizeof(x) / sizeof(x[0]))
|
|
|
|
static restconf_stream_data *
|
|
restconf_stream_data_new(restconf_conn_h *rc,
|
|
int32_t stream_id)
|
|
{
|
|
restconf_stream_data *sd;
|
|
|
|
sd = malloc(sizeof(restconf_stream_data));
|
|
memset(sd, 0, sizeof(restconf_stream_data));
|
|
sd->sd_stream_id = stream_id;
|
|
sd->sd_fd = -1;
|
|
INSQ(sd, rc->rc_streams);
|
|
return sd;
|
|
}
|
|
|
|
#ifdef NOTUSED
|
|
static void
|
|
delete_http2_stream_data(restconf_stream_data *sd)
|
|
{
|
|
if (sd->fd != -1) {
|
|
close(sd->fd);
|
|
}
|
|
free(sd->request_path);
|
|
free(sd);
|
|
}
|
|
#endif
|
|
|
|
#ifdef NOTUSED
|
|
static int
|
|
send_client_connection_header(nghttp2_session *session)
|
|
{
|
|
int retval = -1;
|
|
nghttp2_settings_entry iv[1] = {
|
|
{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}};
|
|
int rv;
|
|
|
|
clicon_debug(1, "%s", __FUNCTION__);
|
|
/* client 24 bytes magic string will be sent by nghttp2 library */
|
|
rv = nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, ARRLEN(iv));
|
|
if (rv != 0) {
|
|
clicon_err(OE_XML, 0, "Could not submit SETTINGS: %s", nghttp2_strerror(rv));
|
|
goto done;
|
|
}
|
|
retval = 0;
|
|
done:
|
|
return retval;
|
|
}
|
|
#endif /* NOTUSED */
|
|
|
|
/* Clixon error category specialized log callback for nghttp2
|
|
* @param[in] handle Application-specific handle
|
|
* @param[in] suberr Application-specific handle
|
|
* @param[out] cb Read log/error string into this buffer
|
|
*/
|
|
int
|
|
clixon_nghttp2_log_cb(void *handle,
|
|
int suberr,
|
|
cbuf *cb)
|
|
{
|
|
clicon_debug(1, "%s", __FUNCTION__);
|
|
cprintf(cb, "Fatal error: %s", nghttp2_strerror(suberr));
|
|
return 0;
|
|
}
|
|
|
|
|
|
#ifdef NOTUSED
|
|
static void
|
|
nghttp2_print_header(const uint8_t *name,
|
|
size_t namelen,
|
|
const uint8_t *value,
|
|
size_t valuelen)
|
|
{
|
|
clicon_debug(1, "%s %s", name, value);
|
|
}
|
|
|
|
/* Print HTTP headers to |f|. Please note that this function does not
|
|
take into account that header name and value are sequence of
|
|
octets, therefore they may contain non-printable characters. */
|
|
static void
|
|
nghttp2_print_headers(nghttp2_nv *nva,
|
|
size_t nvlen)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < nvlen; ++i)
|
|
nghttp2_print_header(nva[i].name, nva[i].namelen, nva[i].value, nva[i].valuelen);
|
|
}
|
|
#endif /* NOTUSED */
|
|
|
|
/*! Transmit the |data|, |length| bytes, to the network.
|
|
* This callback is required if the application uses
|
|
* `nghttp2_session_send()` to send data to the remote endpoint. If
|
|
* the application uses solely `nghttp2_session_mem_send()` instead,
|
|
* this callback function is unnecessary.
|
|
*/
|
|
static ssize_t
|
|
send_callback(nghttp2_session *session,
|
|
const uint8_t *data,
|
|
size_t length,
|
|
int flags,
|
|
void *user_data)
|
|
{
|
|
restconf_conn_h *rc = (restconf_conn_h *)user_data;
|
|
int ret;
|
|
|
|
clicon_debug(1, "%s %zu:", __FUNCTION__, length);
|
|
#if 0
|
|
{
|
|
int i;
|
|
for (i=0; i<length; i++)
|
|
fprintf(stderr, "%02x", data[i]&255);
|
|
fprintf(stderr, "\n");
|
|
}
|
|
#endif
|
|
/* encrypt & send message */
|
|
if ((ret = SSL_write(rc->rc_ssl, data, length)) < 0)
|
|
return ret;
|
|
return ret;
|
|
}
|
|
|
|
/*! Invoked when |session| wants to receive data from the remote peer.
|
|
*/
|
|
static ssize_t
|
|
recv_callback(nghttp2_session *session,
|
|
uint8_t *buf,
|
|
size_t length,
|
|
int flags,
|
|
void *user_data)
|
|
{
|
|
// restconf_conn_h *rc = (restconf_conn_h *)user_data;
|
|
clicon_debug(1, "%s", __FUNCTION__);
|
|
return 0;
|
|
}
|
|
|
|
/*! Callback for each incoming http request for path /
|
|
*
|
|
* This are all messages except /.well-known, Registered with evhtp_set_cb
|
|
*
|
|
* @param[in] req evhtp http request structure defining the incoming message
|
|
* @param[in] arg cx_evhtp handle clixon specific fields
|
|
* @retval void
|
|
* Discussion: problematic if fatal error -1 is returneod from clixon routines
|
|
* without actually terminating. Consider:
|
|
* 1) sending some error? and/or
|
|
* 2) terminating the process?
|
|
*/
|
|
static int
|
|
restconf_nghttp2_root(restconf_conn_h *rc)
|
|
{
|
|
int retval = -1;
|
|
clicon_handle h;
|
|
// int ret;
|
|
cvec *qvec = NULL;
|
|
|
|
clicon_debug(1, "------------");
|
|
if ((h = rc->rc_h) == NULL){
|
|
clicon_err(OE_RESTCONF, EINVAL, "arg is NULL");
|
|
goto done;
|
|
}
|
|
|
|
/* get accepted connection */
|
|
/* Query vector, ie the ?a=x&b=y stuff */
|
|
if ((qvec = cvec_new(0)) ==NULL){
|
|
clicon_err(OE_UNIX, errno, "cvec_new");
|
|
goto done;
|
|
}
|
|
/* call generic function */
|
|
if (api_root_restconf(h, rc, qvec) < 0)
|
|
goto done;
|
|
// /* Clear (fcgi) paramaters from this request */
|
|
// if (restconf_param_del_all(h) < 0)
|
|
// 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 retval; /* void */
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
static int
|
|
on_frame_recv_callback(nghttp2_session *session,
|
|
const nghttp2_frame *frame,
|
|
void *user_data)
|
|
{
|
|
int retval = -1;
|
|
restconf_conn_h *rc = (restconf_conn_h *)user_data;
|
|
restconf_stream_data *sd;
|
|
char *path;
|
|
|
|
clicon_debug(1, "%s %d", __FUNCTION__, frame->hd.stream_id);
|
|
switch (frame->hd.type) {
|
|
case NGHTTP2_DATA:
|
|
case NGHTTP2_HEADERS:
|
|
/* Check that the client request has finished */
|
|
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
|
/* For DATA and HEADERS frame, this callback may be called after
|
|
on_stream_close_callback. Check that stream still alive. */
|
|
if ((sd = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)) == NULL)
|
|
return 0;
|
|
if ((path = restconf_uripath(rc->rc_h)) == NULL)
|
|
goto ok;
|
|
if (strcmp(path, "/" RESTCONF_API) == 0){
|
|
if (restconf_nghttp2_root(rc) < 0)
|
|
goto done;
|
|
}
|
|
else if (strcmp(path, RESTCONF_WELL_KNOWN) == 0){
|
|
}
|
|
else
|
|
; /* ignore */
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
ok:
|
|
retval = 0;
|
|
done:
|
|
return retval;
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
static int
|
|
on_invalid_frame_recv_callback(nghttp2_session *session,
|
|
const nghttp2_frame *frame,
|
|
int lib_error_code,
|
|
void *user_data)
|
|
{
|
|
// restconf_conn_h *rc = (restconf_conn_h *)user_data;
|
|
clicon_debug(1, "%s", __FUNCTION__);
|
|
return 0;
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
static int
|
|
on_data_chunk_recv_callback(nghttp2_session *session,
|
|
uint8_t flags,
|
|
int32_t stream_id,
|
|
const uint8_t *data,
|
|
size_t len,
|
|
void *user_data)
|
|
{
|
|
// restconf_conn_h *rc = (restconf_conn_h *)user_data;
|
|
|
|
clicon_debug(1, "%s %d", __FUNCTION__, stream_id);
|
|
// if (sd->sd_session == session &&
|
|
// sd->sd_stream_id == stream_id)
|
|
// fwrite(data, 1, len, stdout); /* This is where data is printed */
|
|
return 0;
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
static int
|
|
before_frame_send_callback(nghttp2_session *session,
|
|
const nghttp2_frame *frame,
|
|
void *user_data)
|
|
{
|
|
// restconf_conn_h *rc = (restconf_conn_h *)user_data;
|
|
|
|
clicon_debug(1, "%s", __FUNCTION__);
|
|
return 0;
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
static int
|
|
on_frame_send_callback(nghttp2_session *session,
|
|
const nghttp2_frame *frame,
|
|
void *user_data)
|
|
{
|
|
// restconf_conn_h *rc = (restconf_conn_h *)user_data;
|
|
clicon_debug(1, "%s", __FUNCTION__);
|
|
return 0;
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
static int
|
|
on_frame_not_send_callback(nghttp2_session *session,
|
|
const nghttp2_frame *frame,
|
|
int lib_error_code,
|
|
void *user_data)
|
|
{
|
|
// restconf_conn_h *rc = (restconf_conn_h *)user_data;
|
|
clicon_debug(1, "%s", __FUNCTION__);
|
|
return 0;
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
static int
|
|
on_stream_close_callback(nghttp2_session *session,
|
|
int32_t stream_id,
|
|
nghttp2_error_code error_code,
|
|
void *user_data)
|
|
{
|
|
// restconf_conn_h *rc = (restconf_conn_h *)user_data;
|
|
clicon_debug(1, "%s", __FUNCTION__);
|
|
//session_data *sd = (session_data*)user_data;
|
|
return 0;
|
|
}
|
|
|
|
/*! Reception of header block in HEADERS or PUSH_PROMISE is started.
|
|
*/
|
|
static int
|
|
on_begin_headers_callback(nghttp2_session *session,
|
|
const nghttp2_frame *frame,
|
|
void *user_data)
|
|
{
|
|
restconf_conn_h *rc = (restconf_conn_h *)user_data;
|
|
restconf_stream_data *sd;
|
|
|
|
clicon_debug(1, "%s", __FUNCTION__);
|
|
if (frame->hd.type == NGHTTP2_HEADERS &&
|
|
frame->headers.cat == NGHTTP2_HCAT_REQUEST) {
|
|
sd = restconf_stream_data_new(rc, frame->hd.stream_id);
|
|
nghttp2_session_set_stream_user_data(session, frame->hd.stream_id, sd);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#ifdef XXX
|
|
/*! Translate http header by capitalizing, prepend w HTTP_ and - -> _
|
|
* Example: Host -> HTTP_HOST
|
|
*/
|
|
static int
|
|
evhtp_convert_fcgi(evhtp_header_t *hdr,
|
|
void *arg)
|
|
{
|
|
int retval = -1;
|
|
clicon_handle h = (clicon_handle)arg;
|
|
cbuf *cb = NULL;
|
|
int i;
|
|
char c;
|
|
|
|
if ((cb = cbuf_new()) == NULL){
|
|
clicon_err(OE_UNIX, errno, "cbuf_new");
|
|
goto done;
|
|
}
|
|
/* convert key name */
|
|
cprintf(cb, "HTTP_");
|
|
for (i=0; i<strlen(hdr->key); i++){
|
|
c = hdr->key[i] & 0xff;
|
|
if (islower(c))
|
|
cprintf(cb, "%c", toupper(c));
|
|
else if (c == '-')
|
|
cprintf(cb, "_");
|
|
else
|
|
cprintf(cb, "%c", c);
|
|
}
|
|
if (restconf_param_set(h, cbuf_get(cb), hdr->val) < 0)
|
|
goto done;
|
|
retval = 0;
|
|
done:
|
|
if (cb)
|
|
cbuf_free(cb);
|
|
return retval;
|
|
}
|
|
#endif
|
|
|
|
/*! Map from nghttp2 headers to "fcgi" type parameters used in clixon code
|
|
* Both |name| and |value| are guaranteed to be NULL-terminated.
|
|
*/
|
|
static int
|
|
nghttp2_hdr2clixon(clicon_handle h,
|
|
char *name,
|
|
char *value)
|
|
{
|
|
int retval = -1;
|
|
|
|
if (strcmp(name, ":path") == 0){
|
|
/* XXX "/restconf" Is PATH really REQUEST_URI? */
|
|
if (restconf_param_set(h, "REQUEST_URI", value) < 0) /* XXX string? */
|
|
goto done;
|
|
}
|
|
else if (strcmp(name, ":method") == 0){
|
|
if (restconf_param_set(h, "REQUEST_METHOD", value) < 0)
|
|
goto done;
|
|
}
|
|
else if (strcmp(name, ":scheme") == 0){
|
|
if (strcmp(value, "https") == 0 &&
|
|
restconf_param_set(h, "HTTPS", "https") < 0) /* some string or NULL */
|
|
goto done;
|
|
}
|
|
else if (strcmp(name, ":authority") == 0){
|
|
if (restconf_param_set(h, "HTTP_HOST", value) < 0)
|
|
goto done;
|
|
}
|
|
else if (restconf_convert_hdr(h, name, value) < 0)
|
|
goto done;
|
|
retval = 0;
|
|
done:
|
|
return retval;
|
|
}
|
|
|
|
/*! Header name/value pair is received
|
|
* Both |name| and |value| are guaranteed to be NULL-terminated.
|
|
* If the application uses `nghttp2_session_mem_recv()`, it can return
|
|
* :enum:`NGHTTP2_ERR_PAUSE` to make `nghttp2_session_mem_recv()`
|
|
* return without processing further input bytes.
|
|
*/
|
|
static int
|
|
on_header_callback(nghttp2_session *session,
|
|
const nghttp2_frame *frame,
|
|
const uint8_t *name,
|
|
size_t namelen,
|
|
const uint8_t *value,
|
|
size_t valuelen,
|
|
uint8_t flags,
|
|
void *user_data)
|
|
{
|
|
int retval = -1;
|
|
restconf_conn_h *rc = (restconf_conn_h *)user_data;
|
|
restconf_stream_data *sd;
|
|
|
|
clicon_debug(1, "%s %d:", __FUNCTION__, frame->hd.stream_id);
|
|
switch (frame->hd.type){
|
|
case NGHTTP2_HEADERS:
|
|
assert (frame->headers.cat == NGHTTP2_HCAT_REQUEST);
|
|
clicon_debug(1, "%s %s %s", __FUNCTION__, name, value);
|
|
if ((sd = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)) == NULL)
|
|
break;
|
|
if (nghttp2_hdr2clixon(rc->rc_h, (char*)name, (char*)value) < 0)
|
|
goto done;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
retval = 0;
|
|
done:
|
|
return retval;
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
static ssize_t
|
|
select_padding_callback(nghttp2_session *session,
|
|
const nghttp2_frame *frame,
|
|
size_t max_payloadlen,
|
|
void *user_data)
|
|
{
|
|
// restconf_conn_h *rc = (restconf_conn_h *)user_data;
|
|
clicon_debug(1, "%s", __FUNCTION__);
|
|
return 0;
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
static ssize_t
|
|
data_source_read_length_callback(nghttp2_session *session,
|
|
uint8_t frame_type,
|
|
int32_t stream_id,
|
|
int32_t session_remote_window_size,
|
|
int32_t stream_remote_window_size,
|
|
uint32_t remote_max_frame_size,
|
|
void *user_data)
|
|
{
|
|
// restconf_conn_h *rc = (restconf_conn_h *)user_data;
|
|
clicon_debug(1, "%s", __FUNCTION__);
|
|
return 0;
|
|
}
|
|
|
|
/*! Invoked when a frame header is received.
|
|
* Unlike :type:`nghttp2_on_frame_recv_callback`, this callback will
|
|
* also be called when frame header of CONTINUATION frame is received.
|
|
*/
|
|
static int
|
|
on_begin_frame_callback(nghttp2_session *session,
|
|
const nghttp2_frame_hd *hd,
|
|
void *user_data)
|
|
{
|
|
// restconf_conn_h *rc = (restconf_conn_h *)user_data;
|
|
clicon_debug(1, "%s type:%d", __FUNCTION__, hd->type);
|
|
|
|
if (hd->type == NGHTTP2_CONTINUATION)
|
|
assert(0);
|
|
return 0;
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
static int
|
|
send_data_callback(nghttp2_session *session,
|
|
nghttp2_frame *frame,
|
|
const uint8_t *framehd, size_t length,
|
|
nghttp2_data_source *source,
|
|
void *user_data)
|
|
{
|
|
// restconf_conn_h *rc = (restconf_conn_h *)user_data;
|
|
clicon_debug(1, "%s", __FUNCTION__);
|
|
return 0;
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
static ssize_t
|
|
pack_extension_callback(nghttp2_session *session,
|
|
uint8_t *buf, size_t len,
|
|
const nghttp2_frame *frame,
|
|
void *user_data)
|
|
{
|
|
// restconf_conn_h *rc = (restconf_conn_h *)user_data;
|
|
clicon_debug(1, "%s", __FUNCTION__);
|
|
return 0;
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
static int
|
|
unpack_extension_callback(nghttp2_session *session,
|
|
void **payload,
|
|
const nghttp2_frame_hd *hd,
|
|
void *user_data)
|
|
{
|
|
// restconf_conn_h *rc = (restconf_conn_h *)user_data;
|
|
clicon_debug(1, "%s", __FUNCTION__);
|
|
return 0;
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
static int
|
|
on_extension_chunk_recv_callback(nghttp2_session *session,
|
|
const nghttp2_frame_hd *hd,
|
|
const uint8_t *data,
|
|
size_t len,
|
|
void *user_data)
|
|
{
|
|
// restconf_conn_h *rc = (restconf_conn_h *)user_data;
|
|
clicon_debug(1, "%s", __FUNCTION__);
|
|
return 0;
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
static int
|
|
error_callback(nghttp2_session *session,
|
|
const char *msg,
|
|
size_t len,
|
|
void *user_data)
|
|
{
|
|
// restconf_conn_h *rc = (restconf_conn_h *)user_data;
|
|
clicon_debug(1, "%s", __FUNCTION__);
|
|
return 0;
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
static int
|
|
error_callback2(nghttp2_session *session,
|
|
int lib_error_code,
|
|
const char *msg,
|
|
size_t len,
|
|
void *user_data)
|
|
{
|
|
// restconf_conn_h *rc = (restconf_conn_h *)user_data;
|
|
clicon_debug(1, "%s", __FUNCTION__);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* XXX see session_recv
|
|
*/
|
|
int
|
|
http2_recv(restconf_conn_h *rc,
|
|
const unsigned char *buf,
|
|
size_t n)
|
|
{
|
|
int retval = -1;
|
|
nghttp2_error ngerr;
|
|
|
|
clicon_debug(1, "%s", __FUNCTION__);
|
|
if (rc->rc_ngsession == NULL){
|
|
clicon_err(OE_RESTCONF, EINVAL, "No nghttp2 session");
|
|
goto done;
|
|
}
|
|
if ((ngerr = nghttp2_session_mem_recv(rc->rc_ngsession, buf, n)) < 0){
|
|
clicon_err(OE_NGHTTP2, ngerr, "nghttp2_session_mem_recv");
|
|
goto done;
|
|
}
|
|
retval = 0;
|
|
done:
|
|
return retval;
|
|
}
|
|
|
|
/*! Initialize callbacks
|
|
*/
|
|
int
|
|
http2_session_init(restconf_conn_h *rc)
|
|
{
|
|
nghttp2_session_callbacks *callbacks = NULL;
|
|
nghttp2_session *session = NULL;
|
|
|
|
nghttp2_session_callbacks_new(&callbacks);
|
|
nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
|
|
nghttp2_session_callbacks_set_recv_callback(callbacks, recv_callback);
|
|
nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, on_frame_recv_callback);
|
|
nghttp2_session_callbacks_set_on_invalid_frame_recv_callback(callbacks, on_invalid_frame_recv_callback);
|
|
nghttp2_session_callbacks_set_on_data_chunk_recv_callback(callbacks, on_data_chunk_recv_callback);
|
|
nghttp2_session_callbacks_set_before_frame_send_callback(callbacks, before_frame_send_callback);
|
|
nghttp2_session_callbacks_set_on_frame_send_callback(callbacks, on_frame_send_callback);
|
|
nghttp2_session_callbacks_set_on_frame_not_send_callback(callbacks, on_frame_not_send_callback);
|
|
nghttp2_session_callbacks_set_on_stream_close_callback(callbacks, on_stream_close_callback);
|
|
nghttp2_session_callbacks_set_on_begin_headers_callback(callbacks, on_begin_headers_callback);
|
|
nghttp2_session_callbacks_set_on_header_callback(callbacks, on_header_callback);
|
|
nghttp2_session_callbacks_set_select_padding_callback(callbacks, select_padding_callback);
|
|
nghttp2_session_callbacks_set_data_source_read_length_callback(callbacks, data_source_read_length_callback);
|
|
nghttp2_session_callbacks_set_on_begin_frame_callback(callbacks, on_begin_frame_callback);
|
|
|
|
nghttp2_session_callbacks_set_send_data_callback(callbacks, send_data_callback);
|
|
nghttp2_session_callbacks_set_pack_extension_callback(callbacks, pack_extension_callback);
|
|
nghttp2_session_callbacks_set_unpack_extension_callback(callbacks, unpack_extension_callback);
|
|
nghttp2_session_callbacks_set_on_extension_chunk_recv_callback(callbacks, on_extension_chunk_recv_callback);
|
|
nghttp2_session_callbacks_set_error_callback(callbacks, error_callback);
|
|
nghttp2_session_callbacks_set_error_callback2(callbacks, error_callback2);
|
|
|
|
/* Register callbacks with nghttp2 */
|
|
nghttp2_session_server_new(&session, callbacks, rc);
|
|
nghttp2_session_callbacks_del(callbacks);
|
|
rc->rc_ngsession = session;
|
|
return 0;
|
|
}
|
|
|
|
#endif /* HAVE_LIBNGHTTP2 */
|