/* * Copyright (c) 2015-2016 Wind River Systems, Inc. * * SPDX-License-Identifier: Apache-2.0 * */ /** * @file * Wind River CGTS Platform Common HTTP utility Module * */ #include 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 ); } }