Skip to content

rglaue/xinetd_bash_http_service

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 

Repository files navigation

xinetd_bash_http_service

An HTTP service for xinetd written in bash

This is written as a starting framework example for servicing HTTP requests to obtain health of some local service that is desired to be monitored. It can also be used on the command line to obtain the same health information.

If you need a monitoring application that is heavy in client connections, or needs to store stateful information, consider writing a daemon that runs on its own as opposed to an xinetd application. However, if your needs are light, and you want HTTP REST-like capabilities, perhaps this meets your needs.

Version 0.3 with support for HA Proxy HTTP header

Version 0.3 was updated to parse HA Proxy's X-Haproxy-Server-State HTTP header when HAProxy uses the configuration option httpchk with http-check send-state. By default, this xinetd script can send results back to HA Proxy with both option tcp-check and option httpchk without differing configuration. See HA Proxy Use below.

Using for your purposes

Edit the xinetdhttpservice.sh file, modifying the script at the bottom to add your custom code. Look for the section titled "Add your health checking logic below". You can modify or remove the example code that is in this section.

#
# Add your health checking logic below
#
# If --http-status is provided, http_response() function will send the value in
# an HTTP response. Otherwise the value is displayed alone.
#

# If something unhealthy was detected, then:
decrease_health_value

# display health value response, and exit
display_health_value

# send a http_response of 200
http_response 200 "Success"

# End of program

Available functions

  • get_http_req_uri_params_value <param-name>

This function will obtain the value of a paramter provided in the HTTP request.

# if GET Request URI (GET_REQ_URI) is "/?uptime=seconds&format=json"
format_value=$(get_http_req_uri_params_value "format")
# Result: format_value == json
  • get_haproxy_server_state_value <param-name>

This function will obtain the value of a paramter provided in the HTTP request header X-Haproxy-Server-State.

# if X-Haproxy-Server-State: UP; name=backend/server; node=haproxy-name; weight=1/2; scur=0/1; qcur=0; throttle=86%
HA_NAME=$(get_haproxy_server_state_value name)
# Result: HA_NAME == backend/server
HA_WEIGHT=$(get_haproxy_server_state_value weight)
# Result: HA_WEIGHT == 1/2
HA_STATE=$(get_haproxy_server_state_value state)
# Result: HA_STATE == UP
  • http_response <http-code> <message>

This function will return a HTTP response and exit. It will do nothing and return if the --http-response option is not set to 1, or if the request came from the command line and not as a HTTP request.

http_response 301 "I did not find what you were looking for."
  • decrease_health_value

This function will decrease the global health value

decrease_health_value
  • display_health_value

This function displays the global helath value in a HTTP response or standard output for the command line, and then exits.

display_health_value

Runtime parameters

linux$ xinetdhttpservice.sh --help
xinetd_http_service 0.3
https://github.com/rglaue/xinetd_bash_http_service
Copyright (C) 2018 Russell Glaue, CAIT, WIU <http://www.cait.org>

Usage: xinetd_http_service [options]
Description: bash script called by xinetd to service a HTTP request; a farmework for reporting on health

Options:
    -h, --help        show this help message and exit
    --version         show program's version number and exit
    --verbose         Display verbose messages
    --http-status     returns HTTP status for healthiness
                       when called by command line, default is --http-status=0
    --health-value    Show a value consistent with the health of this node
                       100% healthy is 0, value decreases with health status
    --weight-value    Show a weighted value based on the health value
    --max-weight=n    Start with this weight as max, default=100
    --inverse-weight  Inverse the weighted value
    --show-headers    Show the parsed HTTP headers instead of displaying health
    --                Optional, specifically ends reading of options
Examples:
    xinetd_http_service                 # Returns HTTP status
    xinetd_http_service --health-value  # Returns a number based on healthiness
    echo "GET /weight-value?max-weight=200 HTTP/1.1" | xinetd_http_service

Runtime Example Usage

Test to see how HTTP headers are parsed

  • HTTP GET

linux$ echo "GET /test123?var1=val1 HTTP/1.0" | xinetdhttpservice.sh --http-status --show-headers
HTTP/1.1 200 OK
Content-Type: text/plain
Connection: close
Content-Length: 179

HTTP_REQ_VERSION=HTTP/1.0
HTTP_REQUEST=GET /test123?var1=val1 HTTP/1.0
HTTP_REQ_URI=/test123?var1=val1
HTTP_REQ_URI_PATH=/test123
HTTP_REQ_METHOD=GET
HTTP_REQ_URI_PARAMS=var1=val1
  • HTTP POST

linux$ xinetdhttpservice.sh --show-headers <<HTTP_EOF
POST /weight-value?max-weight=200 HTTP/1.1
User-Agent: noagent/1.0
Host: 127.0.0.1:8080
Accept: */*
Content-Length: 78
Content-Type: application/x-www-form-urlencoded

{
  "type": "json",
  "key": "animal",
  "color": "brown",
  "name": "bear"
}
HTTP_EOF
HTTP/1.1 200 OK
Content-Type: text/plain
Connection: close
Content-Length: 515

HTTP_CONTENT_LENGTH=78
HTTP_USER_AGENT=noagent/1.0
HTTP_REQ_VERSION=HTTP/1.1
HTTP_POST_CONTENT={
HTTP_ACCEPT=*/*
HTTP_CONTENT_TYPE=application/x-www-form-urlencoded
HTTP_REQUEST=POST /weight-value?max-weight=200 HTTP/1.1
HTTP_REQ_URI=/weight-value?max-weight=200
HTTP_REQ_URI_PATH=/weight-value
HTTP_REQ_METHOD=POST
HTTP_REQ_URI_PARAMS=max-weight=200
HTTP_SERVER=127.0.0.1:8080
--BEGIN:HTTP_POST_CONTENT--
{
  "type": "json",
  "key": "animal",
  "color": "brown",
  "name": "bear"
}

--END:HTTP_POST_CONTENT--
  • HTTP POST Config: MAX_HTTP_POST_LENGTH

At the top of the xinetdhttpservice.sh bash script, there is a global variable that define the maximum allowed length of posted data. Posted data that has a length greater than this will be cut off.

MAX_HTTP_POST_LENGTH=200
  • HTTP POST Config: READ_BUFFER_LENGTH

If a non-compliant HTTP client is posting data that is shorter than the Content-Length, then the READ_BUFFER_LENGTH should be set to 1. By default this value is the size of the Content-Length, which is more efficient.

  # If the value of Content-Length is greater than the actual content, then
  # read will timeout and never allow the collection from standard input.
  # This is overcome by reading one character at a time.
  #READ_BUFFER_LENGTH=1
  # If you are sure the value of Content-Length always equals the length of the
  # content, then all of standard input can be read in at one time
  READ_BUFFER_LENGTH=$DATA_LENGTH

Note: The maximum length of posted data that is accepted is the Content-Length or the MAX_HTTP_POST_LENGTH, whichever is shorter. If the HTTP client is posting data, yet provides a Content-Length of 0, no data will be read in.

Test the HTTP output

linux$ echo "GET /test123?var1=val1 HTTP/1.0" | xinetdhttpservice.sh --http-status
HTTP/1.1 200 OK
Content-Type: text/plain
Connection: close
Content-Length: 7

Success

Retrieve output from the command line

linux$ xinetdhttpservice.sh
Success
linux$ echo "GET /weight-value?inverse-weight=0&max-weight=120 HTTP/1.0" | xinetdhttpservice.sh
HTTP/1.1 200 OK
Content-Type: text/plain
Connection: close
Content-Length: 16

WEIGHT_VALUE=119

As an xinetd HTTP service

To configure this script as an xinetd service, add the xinetdhttpservice_config file to the system /etc/xinetd.d/ directory.

Then restart xinetd

CentOS-Flavors$ systemctl restart xinetd

Then query the service via a HTTP call

linux$ curl http://0.0.0.0:8080/weight-value?inverse-weight=0&max-weight=120
WEIGHT_VALUE=119

As a HAProxy server check

First setup the xinetd service, as described previously. Once setup, you should be able to get the service status via TCP or HTTP check in HAProxy. This can be tested as follows. Both TCP and HTTP checks will work without differing configuration in the xinetd_bash_http_service script because the http_response function only delivers output if the client made an HTTP request.

  • Testing the health checks

# TCP Checks
shell$ echo | nc 192.168.2.11 8080
Service OK
# HTTP Checks
shell$ curl http://192.168.2.11:8080
Service OK
  • Ensure xinetd script outputs for both HTTP and TCP

# At the end of your xinetd_bash_http_service script
# send the HTTP response and exit
http_response 200 "Service OK"
# or send a TCP response and exit
echo "Service OK"
exit 0
# end of script
  • Configure HA Proxy

frontend some-fe-server
    bind 192.168.1.1:1234
    mode tcp
    default_backend some-be-server-pool

# Both of these backends will work, but use http checks with the
# 'http-check send-state' if you want to receive the HA Proxy state.

# tcp checks
#backend some-be-server-pool
#    option tcp-check
#    tcp-check expect string "Service OK"
#    server srv1 192.168.2.11:80 check port 8080
#    server srv2 192.168.2.12:80 check port 8080

# http checks
backend some-be-server-pool
    option httpchk GET /
    http-check send-state
    http-check expect string "Service OK"
    server srv1 192.168.2.11:80 check port 8080
    server srv2 192.168.2.12:80 check port 8080

License

Copyright (C) 2018 Center for the Application of Information Technologies, Western Illinois University. All rights reserved.

Apache License 2.0, see LICENSE.

This program is free software.