metal/mtce-common/src/common/httpUtil.cpp

1216 lines
37 KiB
C++

/*
* Copyright (c) 2015-2016 Wind River Systems, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*
*/
/**
* @file
* Wind River CGTS Platform Common HTTP utility Module
*
*/
#include <stdlib.h>
using namespace std;
#include "httpUtil.h"
#include "jsonUtil.h"
#include "tokenUtil.h" /* for ... tokenUtil_handler */
#include "nodeUtil.h" /* for ... string_contains */
#include "timeUtil.h" /* for ... time_debug_type */
#include "keyClass.h" /* for ... add_key, del_key */
static keyClass keyValObject ;
static char rest_api_filename[MAX_FILENAME_LEN];
static char rest_api_log_str [MAX_API_LOG_LEN];
static libEvent nullEvent ;
#define HTTP_GET_STR "GET"
#define HTTP_PUT_STR "PUT"
#define HTTP_PATCH_STR "PATCH"
#define HTTP_POST_STR "POST"
#define HTTP_DELETE_STR "DELETE"
#define HTTP_UNKNOWN_STR "UNKNOWN"
/* convert http event type to its string name */
const char * getHttpCmdType_str ( evhttp_cmd_type type )
{
switch (type)
{
case EVHTTP_REQ_GET: return(HTTP_GET_STR);
case EVHTTP_REQ_PUT: return(HTTP_PUT_STR);
case EVHTTP_REQ_PATCH: return(HTTP_PATCH_STR);
case EVHTTP_REQ_POST: return(HTTP_POST_STR);
case EVHTTP_REQ_DELETE: return(HTTP_DELETE_STR);
case EVHTTP_REQ_HEAD:
case EVHTTP_REQ_OPTIONS:
case EVHTTP_REQ_TRACE:
case EVHTTP_REQ_CONNECT:
default:
break ;
}
return(HTTP_UNKNOWN_STR);
}
/* ***********************************************************************
*
* Name : httpUtil_event_init
*
* Description: Initialize the supplied libevent structure to default
* start values including with the supplied hostname,
* service , ip and port values.
*
* Note: No memory allication is performed.
*
* ************************************************************************/
int httpUtil_event_init ( libEvent * ptr ,
string hostname,
string service,
string ip,
int port )
{
/* Default Starting States */
ptr->sequence = 0 ;
ptr->request = SERVICE_NONE ;
ptr->state = HTTP__TRANSMIT ;
ptr->log_prefix = hostname ;
ptr->log_prefix.append(" ") ;
ptr->log_prefix.append(service) ;
/* Execution Controls */
ptr->stuck = 0 ;
ptr->count = 0 ;
ptr->timeout = 0 ;
ptr->retries = 0 ;
ptr->cur_retries = 0 ;
ptr->max_retries = 0 ;
ptr->active = false ;
ptr->mutex = false ;
ptr->found = false ;
ptr->blocking = false ;
ptr->noncritical = false ;
ptr->rx_retry_cnt= 0 ;
ptr->rx_retry_max= 1000 ;
ptr->uuid.clear();
ptr->new_uuid.clear() ;
/* Service Specific Request Info */
ptr->ip = ip ;
ptr->port = port ;
ptr->hostname = hostname ;
ptr->service = service ;
/* Copy the mtce token into the libEvent struct for this command */
ptr->token.url.clear();
ptr->token.token.clear();
ptr->token.issued.clear();
ptr->token.expiry.clear();
ptr->token.delay = false ;
ptr->token.refreshed = false ;
/* Instance Specific Request Data Data */
ptr->entity_path.clear() ;
ptr->entity_path_next.clear() ;
ptr->address.clear();
ptr->payload.clear();
ptr->response.clear();
ptr->operation.clear();
ptr->information.clear();
ptr->result.clear();
ptr->label.clear();
/** Default the user agent to mtce ; other users and commands can override */
ptr->user_agent = "mtce/1.0" ;
ptr->admin_url.clear();
ptr->internal_url.clear();
ptr->public_url.clear();
/* HTTP Specific Info */
ptr->type = EVHTTP_REQ_GET ; /* request type GET/PUT/PATCH etc */
/* Result Info */
ptr->status = FAIL;
ptr->http_status = 0 ;
ptr->low_wm = ptr->med_wm = ptr->high_wm = false ;
node_inv_init ( ptr->inv_info ) ;
ptr->this_time = 0 ;
ptr->prev_time = 0 ;
memset (&ptr->req_str[0], 0, MAX_API_LOG_LEN);
return (PASS);
}
/* initialize this module */
void httpUtil_init ( void )
{
httpUtil_event_init ( &nullEvent, "null", "null" , "0.0.0.0", 0);
nullEvent.request = SERVICE_NONE ;
snprintf (&rest_api_filename[0], MAX_FILENAME_LEN, "/var/log/%s_api.log",
program_invocation_short_name );
}
/* ***********************************************************************
*
* Name : httpUtil_free_conn
*
* Description: Free an event's connection memory if it exists.
*
* ************************************************************************/
void httpUtil_free_conn ( libEvent & event )
{
if ( event.conn )
{
hlog3 ("%s Free Connection (%p)\n", event.log_prefix.c_str(), event.conn );
evhttp_connection_free ( event.conn );
event.conn = NULL ;
}
else
{
hlog1 ("%s Already Freed Connection\n", event.log_prefix.c_str());
}
}
/* ***********************************************************************
*
* Name : httpUtil_free_base
*
* Description: Free an event's base memory if it exists.
*
* ************************************************************************/
void httpUtil_free_base ( libEvent & event )
{
/* Free the base */
if ( event.base )
{
hlog3 ("%s Free Base (%p)\n", event.log_prefix.c_str(), event.base );
event_base_free(event.base);
event.base = NULL ;
if ( event.conn )
{
hlog ("%s Free Connection (%p) --------- along with base\n",
event.log_prefix.c_str(), event.conn );
evhttp_connection_free ( event.conn );
event.conn = NULL ;
}
}
else
{
hlog1 ("%s Already Freed Event Base\n", event.log_prefix.c_str());
}
}
/* ***********************************************************************
*
* Name : httpUtil_connect
*
* Description: Allocate memory for a new connection off the supplied
* base with respect to an ip and port.
*
* ************************************************************************/
int httpUtil_connect ( libEvent & event )
{
if ( event.base )
{
hlog ("%s target:%s:%d\n", event.log_prefix.c_str(), event.ip.c_str(), event.port);
/* Open an http connection to specified IP and port */
event.conn = evhttp_connection_base_new ( event.base, NULL,
event.ip.c_str(),
event.port );
/* bind to the correctly-versioned local address */
if ( event.conn )
{
return(PASS) ;
}
else
{
elog ("%s create connection failed (evhttp_connection_base_new)\n", event.log_prefix.c_str());
return (FAIL_CONNECT);
}
}
else
{
slog ("%s Null Event base\n", event.log_prefix.c_str());
return (FAIL_EVENT_BASE);
}
}
/* ***********************************************************************
*
* Name : httpUtil_request
*
* Description: Allocate memory for a new request off the supplied base.
*
* ************************************************************************/
int httpUtil_request ( libEvent & event,
void(*hdlr)(struct evhttp_request *, void *))
{
int rc = PASS ;
/* make a new request and bind the event handler to it */
event.req = evhttp_request_new( hdlr , event.base );
if ( ! event.req )
{
elog ("%s evhttp_request_new returned NULL\n", event.log_prefix.c_str() );
rc = FAIL ;
}
return (rc);
}
/* ***********************************************************************
*
* Name : httpUtil_payload_add
*
* Description: Add the payload to the output buffer.
*
* @returns 0 for success or -1 in error case
*
* ************************************************************************/
int httpUtil_payload_add ( libEvent & event )
{
int rc = PASS ;
/* Returns the output buffer. */
event.buf = evhttp_request_get_output_buffer ( event.req );
/* Check for no buffer */
if ( ! event.buf )
{
elog ("%s evhttp_request_get_output_buffer returned null (%p)\n",
event.log_prefix.c_str(), event.req );
rc = FAIL ;
}
else
{
/* write the body into the buffer */
rc = evbuffer_add_printf ( event.buf, "%s", event.payload.c_str());
if ( rc == -1 )
{
elog ("%s evbuffer_add_printf returned error (-1)\n",
event.log_prefix.c_str());
rc = FAIL ;
}
else if ( rc == 0 )
{
elog ("%s no data added to output buffer (len=0)\n",
event.log_prefix.c_str());
rc = FAIL ;
}
else
{
rc = PASS ;
}
}
return (rc);
}
/* ***********************************************************************
*
* Name : httpUtil_payload_len
*
* Description: Calculate payload length from the output buffer
* and return a string representing that length value.
*
* ************************************************************************/
string httpUtil_payload_len ( libEvent * ptr )
{
string body_len ;
char len_str[10] ;
int len = evbuffer_get_length ( ptr->req->output_buffer ) ;
if (( len == -1 ) || ( len == 0 ))
{
body_len = "" ;
}
else
{
memset ( &len_str[0], 0 , 10 );
sprintf ( &len_str[0], "%d", len );
body_len = len_str ;
hlog2 ("%s Payload Length: %s\n", ptr->log_prefix.c_str(), body_len.c_str() );
}
return ( body_len );
}
/* ***********************************************************************
*
* Name : httpUtil_header_add
*
* Description: Add the supplied list of headers to the http request
* headers section.
*
* ************************************************************************/
int httpUtil_header_add ( libEvent * ptr, http_headers_type * hdrs_ptr )
{
int rc = PASS ;
if ( hdrs_ptr->entries > MAX_HEADERS )
{
elog ("%s Too many headers (%d:%d)\n",
ptr->log_prefix.c_str(), MAX_HEADERS, hdrs_ptr->entries );
return FAIL ;
}
for ( int i = 0 ; i < hdrs_ptr->entries ; i++ )
{
/* Add the header */
rc = evhttp_add_header( ptr->req->output_headers,
hdrs_ptr->entry[i].key.c_str() ,
hdrs_ptr->entry[i].value.c_str());
if ( rc )
{
elog ("%s evhttp_add_header returned failure (%d:%s:%s)\n",
ptr->log_prefix.c_str(), rc,
hdrs_ptr->entry[i].key.c_str(),
hdrs_ptr->entry[i].value.c_str());
rc = FAIL ;
break ;
}
}
return (rc);
}
/* ***********************************************************************
*
* Name : httpUtil_get_length
*
* Description: Loads libEvent.response_len with the length of the
* input buffer so we can allocate enough memory to
* copy it into.
*
* Get the length of the json response.
* Deal with oversized messages.
*
* @param event is a reference to the callers libEvent struct
* where it inds the input buffer pointer
*
* @return integer value representing the length of the input buffer
*
* ************************************************************************/
int httpUtil_get_length ( libEvent & event )
{
event.response_len = evbuffer_get_length (event.req->input_buffer);
if ( event.response_len == 0 )
{
hlog ("%s Request - Response has not content\n",
event.log_prefix.c_str());
event.status = FAIL_JSON_ZERO_LEN ;
}
return ( event.response_len );
}
/* Load the response string into the event struct */
int httpUtil_get_response ( libEvent & event )
{
if ( httpUtil_get_length ( event ) )
{
size_t real_len ;
/* Get a stack buffer, zero it, copy to it and terminate it */
char * stack_buf_ptr = (char*)malloc (event.response_len+1);
memset ( stack_buf_ptr, 0, event.response_len+1 );
real_len = evbuffer_remove( event.req->input_buffer, stack_buf_ptr,
event.response_len);
if ( real_len != event.response_len )
{
wlog ("%s Length differs from removed length (%ld:%ld)\n",
event.log_prefix.c_str(),
event.response_len,
real_len );
}
if ( real_len == 0 )
{
hlog1 ("%s has no response data\n", event.log_prefix.c_str() );
}
/* Terminate the buffer , this is where the +1 above is required.
* Without it there is memory corruption reported by Linux */
*(stack_buf_ptr+event.response_len) = '\0';
/* Store the response */
event.response = stack_buf_ptr ;
free (stack_buf_ptr);
}
return ( event.status );
}
/* ***********************************************************************
*
* Name : mtcHttpUtil_status
*
* Description: Extracts and returns the HTTP execution status
*
* ************************************************************************/
int httpUtil_status ( libEvent & event )
{
int rc = PASS ;
if ( !event.req )
{
elog ("%s Invalid request\n", event.hostname.length() ? event.hostname.c_str() : "unknown" );
return (FAIL_UNKNOWN_HOSTNAME);
}
event.status = event.http_status = evhttp_request_get_response_code (event.req);
switch (event.status)
{
case HTTP_OK:
case 201:
case 202:
case 203:
case 204:
{
hlog ("%s HTTP_OK (%d)\n", event.hostname.c_str(), event.status );
event.status = PASS ;
break;
}
/* Authentication error - refresh the token */
case 401:
{
keyToken_type * token_ptr = tokenUtil_get_ptr() ;
rc = FAIL_AUTHENTICATION ;
token_ptr->delay = true ; /* force delayed token renewal on authentication error */
break ;
}
case 0:
{
wlog ("%s failed to maintain connection to '%s:%d' for '%s'\n",
event.hostname.c_str(), event.ip.c_str(), event.port, event.log_prefix.c_str() );
event.status = FAIL_HTTP_ZERO_STATUS ;
rc = FAIL_HTTP_ZERO_STATUS ;
break ;
}
default:
{
hlog2 ("%s Status: %d\n", event.hostname.c_str(), event.status );
rc = event.status ;
break;
}
}
return (rc);
}
void httpUtil_handler ( struct evhttp_request *req, void *arg )
{
unsigned long temp ;
int rc = PASS ;
UNUSED(req);
libEvent * event_ptr ;
if ( arg == NULL )
{
elog ("null base pointer\n");
return ;
}
/* find the event for this base */
if ( keyValObject.get_key ((unsigned long)arg, temp ) != PASS )
{
wlog ("get_key value 'event' lookup from base (%p) key failed\n", arg );
return ;
}
event_ptr = (libEvent*)temp;
if (( event_ptr->request >= SERVICE_LAST ) || ( event_ptr->request == SERVICE_NONE ))
{
slog ("HTTP Event Lookup Failed for http base (%p) <------\n", arg);
return ;
}
/* Check the HTTP Status Code */
event_ptr->status = httpUtil_status ( (*event_ptr) ) ;
if ( event_ptr->status == HTTP_NOTFOUND )
{
elog ("%s returned (Not-Found) (%d)\n",
event_ptr->log_prefix.c_str(),
event_ptr->status);
if ( event_ptr->type != EVHTTP_REQ_POST )
event_ptr->status = PASS ;
goto httpUtil_handler_done ;
}
else if (( event_ptr->status != PASS ) && ( ! req ))
{
elog ("%s Request Timeout (%d)\n",
event_ptr->log_prefix.c_str(),
event_ptr->timeout);
event_ptr->status = FAIL_TIMEOUT ;
goto httpUtil_handler_done ;
}
else if ( event_ptr->status != PASS )
{
goto httpUtil_handler_done ;
}
/* Delete commands don't have a response unless there is an error.
* Deal with this as a special case -
* Currently only Neutron uses the delete */
if ( event_ptr->type == EVHTTP_REQ_DELETE )
{
if ( httpUtil_get_length ( (*event_ptr) ) != 0 )
{
/* Preserve the incoming status over the get response */
rc = event_ptr->status ;
httpUtil_get_response ( (*event_ptr) ) ;
event_ptr->status = rc ;
}
if (event_ptr->status == FAIL_JSON_ZERO_LEN )
event_ptr->status = PASS ;
}
else if ( httpUtil_get_response ( (*event_ptr) ) != PASS )
{
elog ("%s failed to get response\n", event_ptr->log_prefix.c_str());
goto httpUtil_handler_done ;
}
if ( event_ptr->handler )
{
// ilog ("%s calling event specific handler\n", event_ptr->log_prefix.c_str() );
rc = event_ptr->handler ( (*event_ptr) ) ;
}
else
{
slog ("%s no event handler bound in\n", event_ptr->log_prefix.c_str() );
rc = event_ptr->status = FAIL_NULL_POINTER ;
}
httpUtil_handler_done:
// hlog2 ("%s Base:%p:%p Event:%p\n", event_ptr->log_prefix.c_str(), event_ptr->base, arg, event_ptr );
keyValObject.del_key ((unsigned long)arg );
event_ptr->active = false ;
gettime ( event_ptr->done_time );
timedelta ( event_ptr->send_time, event_ptr->done_time, event_ptr->diff_time );
if ( event_ptr->status )
{
elog ( "%s Failed (rc:%d)\n",
event_ptr->log_prefix.c_str(),
event_ptr->status );
}
httpUtil_log_event ( event_ptr );
}
/* ***********************************************************************
*
* Name : httpUtil_api_request
*
* Description: Makes an HTTP request based on all the info
* in the supplied libEvent.
*
* This is the primary external interface in this module.
*
* Both blocking and non-blocking request type are supported.
*
* ************************************************************************/
/***************************************************************************
*
* Name : httpUtil_latency_log
*
* Description: Measures command handling time and creates a Latency log
* if that time exceeds the specified threshold (msecs).
*
* Parms:
* event - the event in context
*
* label_ptr - "start" to init the prev_timer or
* - "some label" to identify the point in the code and to
* measure time against the previous call.
*
* msecs - the latency log threshold
*
* Usage:
*
* httpUtil_latency_log ( event, HTTPUTIL_SCHED_MON_START, 0 );
*
* [ timed code ]
*
* httpUtil_latency_log ( event, "label 1" , msecs );
*
* [ timed code ]
*
* httpUtil_latency_log ( event, "label 2", msecs );
*
* ...
*
*****************************************************************************/
#define HTTPUTIL_SCHED_MON_START ((const char *)"start")
#define MAX_DELAY_B4_LATENCY_LOG (1700)
void httpUtil_latency_log ( libEvent & event, const char * label_ptr, int line , int msecs )
{
event.this_time = gettime_monotonic_nsec () ;
/* If label_ptr is != NULL and != start then take the measurement */
if ( label_ptr && strncmp ( label_ptr, HTTPUTIL_SCHED_MON_START, strlen(HTTPUTIL_SCHED_MON_START)))
{
if ( event.this_time > (event.prev_time + (NSEC_TO_MSEC*(msecs))))
{
llog ("%s ... %4llu.%-4llu msec - %s (%d)\n", event.hostname.c_str(),
((event.this_time-event.prev_time) > NSEC_TO_MSEC) ? ((event.this_time-event.prev_time)/NSEC_TO_MSEC) : 0,
((event.this_time-event.prev_time) > NSEC_TO_MSEC) ? ((event.this_time-event.prev_time)%NSEC_TO_MSEC) : 0,
label_ptr, line );
}
}
/* reset to be equal for next round */
event.prev_time = event.this_time ;
}
bool token_recursion = false ;
int httpUtil_api_request ( libEvent & event )
{
http_headers_type hdrs ;
int hdr_entry = 0 ;
string path = "" ;
bool free_key = true ;
event.status = PASS ;
event.log_prefix = event.hostname ;
event.log_prefix.append (" ");
event.log_prefix.append (event.service) ;
event.log_prefix.append (" '");
event.log_prefix.append (event.operation) ;
event.log_prefix.append ("'");
hlog ("%s '%s' request\n", event.log_prefix.c_str(), getHttpCmdType_str(event.type));
if (( event.request == SERVICE_NONE ) ||
( event.request >= SERVICE_LAST ))
{
slog ("%s Invalid request %d\n", event.log_prefix.c_str(), event.request);
event.status = FAIL_BAD_PARM ;
return (event.status);
}
/* Check for memory leaks */
if ( event.base )
{
slog ("%s http base memory leak avoidance (%p)\n",
event.log_prefix.c_str(), event.base );
// Be sure to free the key
keyValObject.del_key ((unsigned long)event.base );
// event_base_free(event.base);
}
/* Allocate the base */
event.base = event_base_new();
if ( event.base == NULL )
{
elog ("%s No Memory for Request\n", event.log_prefix.c_str());
event.status = FAIL_EVENT_BASE ;
return (event.status) ;
}
else
{
if ( keyValObject.add_key ((unsigned long)event.base, (unsigned long)&event) != PASS )
{
slog ("%s failed to store base:event as key (%p) value pair\n",
event.log_prefix.c_str(), event.base );
/* lets try and recover from this */
keyValObject.del_key ((unsigned long)event.base);
if ( keyValObject.add_key ((unsigned long)event.base, (unsigned long)&event) != PASS )
{
slog ("%s still cannot store base:event after key_del\n", event.log_prefix.c_str());
event.status = FAIL_LOCATE_KEY_VALUE ;
goto httpUtil_api_request_done ;
}
}
}
if ( event.request == KEYSTONE_GET_TOKEN )
{
event.payload = "" ;
/* create the json string that can request an authority
* token and write that string to 'payload' */
event.status = jsonApi_auth_request ( event.hostname, event.payload );
if ( event.status != PASS )
{
elog ("%s unable to perform get token request (rc:%d)\n", event.hostname.c_str(), event.status );
goto httpUtil_api_request_done ;
}
}
else if (( event.request == KEYSTONE_GET_ENDPOINT_LIST ) ||
( event.request == KEYSTONE_GET_SERVICE_LIST ))
{
;
}
else if (( event.request == SYSINV_SENSOR_ADD ) ||
( event.request == SYSINV_SENSOR_DEL ) ||
( event.request == SYSINV_SENSOR_LOAD ) ||
( event.request == SYSINV_SENSOR_MOD ) ||
( event.request == SYSINV_SENSOR_MOD_GROUP ) ||
( event.request == SYSINV_SENSOR_ADD_GROUP ) ||
( event.request == SYSINV_SENSOR_DEL_GROUP ) ||
( event.request == SYSINV_SENSOR_LOAD_GROUPS ) ||
( event.request == SYSINV_SENSOR_LOAD_GROUP ) ||
( event.request == SYSINV_SENSOR_GROUP_SENSORS ))
{
;
}
else
{
slog ("%s Unsupported Request (%d)\n", event.hostname.c_str(), event.request);
event.status = FAIL_BAD_CASE ;
goto httpUtil_api_request_done ;
}
/* Establish connection */
if ( httpUtil_connect ( event ))
{
event.status = FAIL_CONNECT ;
goto httpUtil_api_request_done ;
}
if ( httpUtil_request ( event, &httpUtil_handler ))
{
event.status = FAIL_REQUEST_NEW ;
goto httpUtil_api_request_done ;
}
if ( event.request != KEYSTONE_GET_TOKEN )
{
jlog ("%s Address : %s\n", event.hostname.c_str(), event.address.c_str());
}
if (( event.type != EVHTTP_REQ_GET ) &&
( event.type != EVHTTP_REQ_DELETE ))
{
/* Add payload to the output buffer but only for PUT, POST and PATCH requests */
if ( httpUtil_payload_add ( event ))
{
event.status = FAIL_PAYLOAD_ADD ;
goto httpUtil_api_request_done ;
}
if ( daemon_get_cfg_ptr()->debug_json )
{
if ((!string_contains(event.payload,"token")) &&
(!string_contains(event.payload,"assword")))
{
jlog ("%s Payload : %s\n", event.hostname.c_str(),
event.payload.c_str() );
}
else
{
jlog ("%s Payload : ... contains private content ...\n",
event.hostname.c_str());
}
}
}
/* Build the HTTP Header */
hdrs.entry[hdr_entry].key = "Host" ;
hdrs.entry[hdr_entry].value = event.ip ;
hdr_entry++;
hdrs.entry[hdr_entry].key = "X-Auth-Project-Id" ;
hdrs.entry[hdr_entry].value = "admin";
hdr_entry++;
if (( event.type != EVHTTP_REQ_GET ) &&
( event.type != EVHTTP_REQ_DELETE ))
{
hdrs.entry[hdr_entry].key = "Content-Length" ;
hdrs.entry[hdr_entry].value = httpUtil_payload_len ( &event );
hdr_entry++;
}
hdrs.entry[hdr_entry].key = "User-Agent" ;
hdrs.entry[hdr_entry].value = event.user_agent ;
hdr_entry++;
hdrs.entry[hdr_entry].key = "Content-Type" ;
hdrs.entry[hdr_entry].value = "application/json" ;
hdr_entry++;
hdrs.entry[hdr_entry].key = "Accept" ;
hdrs.entry[hdr_entry].value = "application/json" ;
hdr_entry++;
if ( event.request != KEYSTONE_GET_TOKEN )
{
hdrs.entry[hdr_entry].key = "X-Auth-Token" ;
hdrs.entry[hdr_entry].value = tokenUtil_get_ptr()->token ;
hdr_entry++;
}
hdrs.entry[hdr_entry].key = "Connection" ;
hdrs.entry[hdr_entry].value = "close" ;
hdr_entry++;
hdrs.entries = hdr_entry ;
/* Add the headers */
if ( httpUtil_header_add ( &event, &hdrs ))
{
event.status = FAIL_HEADER_ADD ;
goto httpUtil_api_request_done ;
}
/* get some timestamps and log the request */
snprintf (&event.req_str[0], MAX_API_LOG_LEN-1,
"\n%s [%5d] %s %s '%s' seq:%d -> Address : %s:%d %s %s ... %s",
pt(), getpid(),
event.hostname.c_str(),
event.service.c_str(),
event.operation.c_str(),
event.sequence, event.ip.c_str(), event.port,
getHttpCmdType_str( event.type ),
event.address.c_str(),
event.information.c_str());
gettime ( event.send_time );
gettime ( event.done_time ); /* create a valid done value */
if ( event.request == KEYSTONE_GET_TOKEN )
{
path = MTC_POST_KEY_LABEL ;
event.address = path ;
event.prefix_path += path;
hlog ("%s Keystone Internal Address : %s\n", event.hostname.c_str(), event.prefix_path.c_str());
event.status = evhttp_make_request ( event.conn, event.req, event.type, event.prefix_path.data());
}
else
{
event.status = evhttp_make_request ( event.conn, event.req, event.type, event.address.data());
}
daemon_signal_hdlr ();
if ( event.status == PASS )
{
string label = event.log_prefix ;
label.append (" - ");
label.append (event.operation);
/* O.K we are commited to making the request */
free_key = false ;
evhttp_connection_set_timeout(event.conn, event.timeout);
httpUtil_latency_log ( event, HTTPUTIL_SCHED_MON_START,__LINE__, 0 );
/* Default to retry for both blocking and non-blocking command */
event.status = RETRY ;
if ( event.blocking == true )
{
hlog ("%s Requested (blocking) (timeout:%d secs)\n", event.log_prefix.c_str(), event.timeout);
/* Send the message with timeout */
event_base_dispatch(event.base);
httpUtil_latency_log ( event, label.c_str(), __LINE__, MAX_DELAY_B4_LATENCY_LOG );
goto httpUtil_api_request_done ;
}
else if ( event.request == KEYSTONE_GET_TOKEN )
{
hlog ("%s Requested (non-blocking) (timeout:%d secs)\n", event.log_prefix.c_str(), event.timeout);
event.active = true ;
event.status = event_base_loop(event.base, EVLOOP_NONBLOCK);
httpUtil_latency_log ( event, label.c_str(), __LINE__, MAX_DELAY_B4_LATENCY_LOG ); /* Should be immediate ; non blocking */
return (event.status);
// goto httpUtil_api_request_done ;
}
else
{
hlog ("%s Requested (blocking) (timeout:%d secs)\n", event.log_prefix.c_str(), event.timeout );
event_base_dispatch(event.base);
httpUtil_latency_log ( event, label.c_str(), __LINE__, MAX_DELAY_B4_LATENCY_LOG ) ;
goto httpUtil_api_request_done ;
}
}
else
{
elog ("%s Call to 'evhttp_make_request' failed (rc:%d)\n",
event.hostname.c_str(), event.status);
}
httpUtil_api_request_done:
httpUtil_free_conn ( event );
httpUtil_free_base ( event );
/* If the request fails then delete the key here */
if ( free_key )
{
keyValObject.del_key ((unsigned long)event.base) ;
}
return (event.status);
}
void httpUtil_event_info ( libEvent & event )
{
ilog ("%s request to %s.%d Status:%d \n",
event.log_prefix.c_str(),
event.ip.c_str(),
event.port,
event.status);
if ( event.request == KEYSTONE_GET_TOKEN )
{
ilog ("--- Address : %s\n", event.prefix_path.c_str());
}
else
{
ilog ("--- Address : %s\n", event.address.c_str());
}
ilog ("--- Payload : %s\n", event.payload.c_str());
ilog ("--- Response: %s\n", event.response.c_str());
ilog ("--- TokenUrl: %s\n", event.token.url.c_str());
}
void httpUtil_log_event ( libEvent * event_ptr )
{
string event_sig = daemon_get_cfg_ptr()->debug_event ;
msgSock_type * mtclogd_ptr = get_mtclogd_sockPtr ();
send_log_message ( get_mtclogd_sockPtr(), event_ptr->hostname.data(), &rest_api_filename[0], &event_ptr->req_str[0] );
if ( event_ptr->request == KEYSTONE_GET_TOKEN )
{
jlog1 ("%s seq:%d -> %s:%d %s %s http status: %d ... %s\n",
event_ptr->log_prefix.c_str(),
event_ptr->sequence,
event_ptr->ip.c_str(),
event_ptr->port,
getHttpCmdType_str( event_ptr->type ),
event_ptr->prefix_path.c_str(),
event_ptr->http_status,
event_ptr->information.c_str());
}
else
{
jlog1 ("%s seq:%d -> %s:%d %s %s http status: %d ... %s\n",
event_ptr->log_prefix.c_str(),
event_ptr->sequence,
event_ptr->ip.c_str(),
event_ptr->port,
getHttpCmdType_str( event_ptr->type ),
event_ptr->address.c_str(),
event_ptr->http_status,
event_ptr->information.c_str());
}
if (!event_ptr->payload.empty())
{
if ((!string_contains(event_ptr->payload,"token")) &&
(!string_contains(event_ptr->payload,"assword")))
{
snprintf (&rest_api_log_str[0], MAX_API_LOG_LEN-1,
"%s [%5d] %s seq:%d -> Payload : %s",
pt(), getpid(), event_ptr->log_prefix.c_str(), event_ptr->sequence, event_ptr->payload.c_str() );
}
else
{
snprintf (&rest_api_log_str[0], MAX_API_LOG_LEN-1,
"%s [%5d] %s seq:%d -> Payload : ... contains private content ...",
pt(), getpid(), event_ptr->log_prefix.c_str(), event_ptr->sequence );
}
send_log_message ( mtclogd_ptr, event_ptr->hostname.data(), &rest_api_filename[0], &rest_api_log_str[0] );
}
if ( !event_ptr->response.empty() )
{
if ((!string_contains(event_ptr->response,"token")) &&
(!string_contains(event_ptr->response,"assword")))
{
snprintf (&rest_api_log_str[0], MAX_API_LOG_LEN-1,
"%s [%5d] %s seq:%d -> Response: %s",
pt(), getpid(), event_ptr->log_prefix.c_str(), event_ptr->sequence, event_ptr->response.c_str() );
}
else
{
snprintf (&rest_api_log_str[0], MAX_API_LOG_LEN-1,
"%s [%5d] %s seq:%d -> Response: ... contains private content ...",
pt(), getpid(), event_ptr->log_prefix.c_str(), event_ptr->sequence );
}
send_log_message ( mtclogd_ptr, event_ptr->hostname.data(), rest_api_filename, &rest_api_log_str[0] );
}
snprintf (&rest_api_log_str[0], MAX_API_LOG_LEN-1,
"%s [%5d] %s %s '%s' seq:%d -> Status : %d {execution time %ld.%06ld secs}\n",
pt(), getpid(),
event_ptr->hostname.c_str(),
event_ptr->service.c_str(),
event_ptr->operation.c_str(),
event_ptr->sequence,
event_ptr->http_status,
event_ptr->diff_time.secs,
event_ptr->diff_time.msecs );
if (( event_ptr->diff_time.secs > 2 ) || (event_ptr->http_status != HTTP_OK ) )
{
int len = strlen (rest_api_log_str) ;
snprintf (&rest_api_log_str[len-1], 20, " <---------");
}
send_log_message ( mtclogd_ptr, event_ptr->hostname.data(), &rest_api_filename[0], &rest_api_log_str[0] );
}
/*****************************************************************
*
* Name : httpUtil_bind
*
* Description : Setup the HTTP server socket
*
*****************************************************************/
int httpUtil_bind ( libEvent & event )
{
int one = 1;
event.fd = socket(AF_INET, SOCK_STREAM, 0);
if (event.fd < 0)
{
elog ("failed to create http server socket (%d:%m)\n", errno );
return FAIL_SOCKET_CREATE ;
}
/* make socket reusable */
if ( 0 > setsockopt(event.fd, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(int)))
{
elog ("failed to set http server socket to reusable (%d:%m)\n", errno );
return FAIL_SOCKET_OPTION ;
}
memset(&event.addr, 0, sizeof(struct sockaddr_in));
event.addr.sin_family = AF_INET;
/* ERIK: INADDR_ANY; TODO: Refine this if we can */
event.addr.sin_addr.s_addr = inet_addr(LOOPBACK_IP);
// event.addr.sin_addr.s_addr = INADDR_ANY;
event.addr.sin_port = htons(event.port);
/* bind port */
if ( 0 > bind ( event.fd, (struct sockaddr*)&event.addr, sizeof(struct sockaddr_in)))
{
elog ("failed to bind to http server port %d (%d:%m)\n", event.port, errno );
return FAIL_SOCKET_BIND ;
}
/* Listen for events */
if ( 0 > listen(event.fd, 10 ))
{
elog ("failed to listen to http server socket (%d:%m)\n", errno );
return FAIL_SOCKET_LISTEN;
}
/* make non-blocking */
int flags = fcntl ( event.fd, F_GETFL, 0) ;
if ( flags < 0 || fcntl(event.fd, F_SETFL, flags | O_NONBLOCK) < 0)
{
elog ("failed to set http server socket to non-blocking (%d:%m)\n", errno );
return FAIL_SOCKET_OPTION;
}
return PASS;
}
/* Setup the http server */
int httpUtil_setup ( libEvent & event,
int supported_methods,
void(*hdlr)(struct evhttp_request *, void *) )
{
int rc = PASS ;
if ( ( rc = httpUtil_bind ( event )) != PASS )
{
return rc ;
}
else if (event.fd < 0)
{
wlog ("failed to get http server socket file descriptor\n");
return RETRY ;
}
event.base = event_base_new();
if (event.base == NULL)
{
elog ("failed to get http server event base\n");
return -1;
}
event.httpd = evhttp_new(event.base);
if (event.httpd == NULL)
{
elog ("failed to get httpd server handle\n");
return -1;
}
/* api is a void return */
evhttp_set_allowed_methods (event.httpd, supported_methods );
rc = evhttp_accept_socket(event.httpd, event.fd);
if ( rc == -1)
{
elog ("failed to accept on http server socket\n");
return -1;
}
/* api is a void return */
evhttp_set_gencb(event.httpd, hdlr, NULL);
ilog ("Listening On: 'http server' socket %s:%d\n",
inet_ntoa(event.addr.sin_addr), event.port );
return PASS ;
}
void httpUtil_fini ( libEvent & event )
{
if ( event.fd )
{
if ( event.base )
{
event_base_free( event.base);
}
close ( event.fd );
event.fd = 0 ;
}
}
void httpUtil_look ( libEvent & event )
{
/* Look for Events */
if ( event.base )
{
// rc = event_base_loopexit( mtce_event.base, NULL ) ; // EVLOOP_NONBLOCK );
event_base_loop(event.base, EVLOOP_NONBLOCK );
}
}