HTTP Server

Enabling the HTTPServer facility makes your web service endpoints accessible to callers via HTTP. The HTTP server that is created is a wrapper over Go’s built-in HTTP serving functionality.

It is agnostic of the content types being accepted or served by your handlers - that is controlled by enabling either the JSONWS or XMLWs facility and/or defining custom content type handling in your endpoints.

This page covers the following:

  • Enabling and configuring the HTTP server
  • Extending functionality by providing components that implement particular interfaces
  • How the HTTP server is affected by application lifecycle events
  • Enabling and configuring access logging

Enabling

The HTTPServer facility is disabled by default. To enable it, you must set the following in your configuration

{
  "Facilities": {
    "HTTPServer": true
  }
}

Configuration

The default configuration for this facility can be found in the Granitic source under facility/config/httpserver.json and is:

{
  "HTTPServer":{
    "Port": 8080,
    "Address": "",
    "AllowEarlyInstrumentation": false,
    "DisableInstrumentationAutoWire": false,
    "MaxConcurrent": 0,
    "TooBusyStatus": 503,
    "AutoFindHandlers": true,
    "RequestID": {
      "Enabled": false,
      "Format": "UUIDV4",
      "UUID":{
        "Encoding": "RFC4122"
      }
    },
    "AccessLogging": false,
    "AccessLog": {
      "LogPath": "./access.log",
      "LogLinePreset": "framework",
      "UtcTimes": true,
      "LineBufferSize": 10,
      "Entry": "TEXT",
      "JSON": {
        "Prefix": "",
        "Fields": [
          ["Remote", "REMOTE"],
          ["ForwardedFor",  "REQ_HEADER", "X-Forwarded-For"],
          ["Received", "RECEIVED", "02/Jan/2006:15:04:05 Z0700"],
          ["Method", "HTTP_METHOD"],
          ["Path", "PATH"],
          ["Query", "QUERY"],
          ["Status", "STATUS"],
          ["BytesReturned", "BYTES_OUT"],
          ["ProcessTimeMicro", "PROCESS_TIME", "MICRO"]
        ],
        "Suffix": "\n"
      }
    }
  }
}

Listening

By default the HTTP server listens on port 8080 on all available IP addresses (including localhost), on IPV4 and IPV6. Setting HTTPServer.Port in your configuration changes the TCP/IP that clients can connect to.

To listen on a specific IP address, you should set HTTPServer.Address to an IP address associated with one of the network interfaces available to your server.

This example:

{
  "HTTPServer": {
    "Port": 80,
    "Address": "192.168.0.142"  
  }
}

Will start an HTTP server that only listens on 192.168.0.142:80

HTTPS

The Granitic HTTP server does not support HTTPS at this time. Your application should be deployed behind a web server or load-balancer with HTTPS support if this is required.

Load management

By default the HTTP server will accept an unlimited number of concurrent requests. This behaviour can be changed by setting HTTPServer.MaxConcurrent to an integer greater than zero.

Any client attempting to connect to the server while it is already handling the maximum concurrent requests will receive an error response with the HTTP Status code defined in TooBusyStatus (deafult 503).

Finding endpoints

By default any component you have created that implements the httpendpoint.Provider interface will automatically found and added to the list of endpoints that can be matched against incoming HTTP requests.

See the web services documentation for more details on creating handler components.

This behaviour can disabled by setting HTTPServer.AutoFindHandlers to false. This is advanced behaviour only generally required when you are running multiple custom instances of the Granitic HTTP server in the same application.

Extending functionality

Handling abnormal statuses

Granitic’s web service processing pipeline delegates the responsibility for writing HTTP responses to the web service handlers, or more specifically the ws.ResponseWriter attached to the handler.

There many circumstances under which the HTTP server will reject an inbound request before it reaches a handler, most commonly when the request cannot be matched to a handler (a 404). In these circumstances, the HTTP server still needs to be able to construct an HTTP response body that is consistent with ‘normal’ responses.

If you are using the JSONWS or XMLWs facility, this is handled automatically. If you not using either of those facilities (or want different behaviour) you must provide the HTTP server with an abnormal status writer by creating a component that implements ws.AbnormalStatusWriter and instructing the HTTP server to use it by providing a framework modifier like:

{
  "frameworkModifiers": {
    "grncHTTPServer": {
      "AbnormalStatusWriter": "myStatusWriter"
    }
  }
}

where myStatusWriter is the name of your component that implements ws.AbnormalStatusWriter.

Request identification

If you have created a component that implements httpserver.IdentifiedRequestContextBuilder that component will be automatically be used to create, derive or inherit a ID for the current request using information included in the HTTP request.

See request identification for more information.

You may instruct Granitic to assign a UUID V4 ID to each request by setting HTTPServer.RequestID.Enabled to true. By default these IDs will be standard RFC4122 formatted UUIDs, but you can choose to alter the formatting by setting HTTPServer.RequestID.UUID.Encoding to Base32 or Base64 “RFC4122”:

Instrumentation

The HTTP server supports and coordinates the instrumentation of web service requests automatically finding a component you have registered that implements instrument.RequestInstrumentationManager.

There are two configuration settings that affect this behaviour.

Auto-wiring

Setting HTTPServer.DisableInstrumentationAutoWire to true means that the HTTP server will not automatically look for a component that implements instrument.RequestInstrumentationManager. Instead you will need to explicit provide an instrumentation manager via a framework modifier like:

{
  "frameworkModifiers": {
    "grncHTTPServer": {
      "InstrumentationManager": "myInstrumentationManager"
    }
  }
}

Early instrumentation

By default Granitic does not start instrumentation until after a request has been accepted (e.g. after checks have taken place to make sure the server is not too busy or suspended). This is to provide some protection against deliberate or accidental denial-of-service attacks putting load on your instrumentation implementation at the cost of losing some fine-grained timing information around the very early stages of request processing and losing all visibility of requests that were rejected because the server was busy.

If you want to be able to instrument these types of request, set HTTPServer.AllowEarlyInstrumentation to true.

Access logging

Granitic can be configured to write a summary of each request received to a log file, similar to most web and application servers. This feature is disabled by default and can be enabled by setting HTTPServer.AccessLogging to true in your configuration.

This will create (or append) a UTF-8 encoded file called access.log in the same directory that you started your application (e.g. your application’s working directory). You can change this specifying a full or relative path to a file with HTTPServer.AccessLog.LogPath. Your application must have filesystem permission to create and edit the file at that path.

The format and information recorded for each request is highly customisable and is described in detail below.

STDOUT logging

If you need your application to write access logs to STDOUT, the preferred option is to set HTTPServer.AccessLog.LogPath to whatever file path represents STDOUT on your OS (e.g. /dev/stdout).

If your OS does not support this concept, you can set HTTPServer.AccessLog.LogPath to STDOUT and Granitic will use Go’s os.Stdout file to write access log entries.

Timestamps

You may choose to have the timestamps associated with each request as UTC (recommended and the default), or in the local time of your server. To use local time, set HTTPServer.AccessLog.UtcTimes to false.

Non-blocking writing

Lines are written to the access log asynchronously. You web service calls will not block for log writing as long as there is space in the log line buffer. The size of this buffer is defined at HTTPServer.AccessLog.LineBufferSize with a default value of 10. If your application is writing logs to a slow storage system or handles large numbers of simultaneous requests, you might want to adjust this value.

If you want to force blocking writing (useful for tests), set HTTPServer.AccessLog.LineBufferSize to zero or less.

Text access log line format

The information you want to include in each line of the access log is controlled by a format string comprised of ‘verbs’ and fixed characters similar to fmt.Printf or HTTPD. You may choose from a preset format or define your own.

Preset formats

Granitic currently specifies threee preset formats. You can set HTTPServer.AccessLog.LogLinePreset to framework, common or combined. The default is framework. The common and combined formats are intended to be similar to the Apache HTTPD log formats of the same name.

Name Format Example
framework %h XFF[%{X-Forwarded-For}i] %l %u [%{02/Jan/2006:15:04:05 Z0700}t] “%m %U%q” %s %bB %{us}Tμs [::1]:49574 XFF[-] - - [09/Oct/2019:14:59:55 Z] “POST /test” 200 -B 252μs
common %h %l %u %t “%r” %s %b [::1]:49580 - - [09/Oct/2019:15:01:35 +0000] “POST /test HTTP/1.1” 200 -
combined %h %l %u %t “%r” %s %b “%{Referer}i” “%{User-agent}i” [::1]:49584 - - [09/Oct/2019:15:02:37 +0000] “POST /test HTTP/1.1” 200 - “-” “-”

Available verbs

Formatting Verb Meaning and usage
%% The percent symbol
%b The number of bytes (excluding headers) sent to client or the - symbol if zero
%B The number of bytes (excluding headers) sent to client or the 0 symbol if zero
%D The wall-clock time the service spent processing the request in microseconds
%h The host (as IPV4 or IPV6 address) from which the client is connecting
%{?}i The string value of a header included in the HTTP request where ? is the case insensitive name of the header
%l Prints the - symbol. For compatibility with common log formats always.
%m The HTTP method (GET, POST etc) of the request
%q The query string of the request, including a leading ? character
%r The HTTP request line (method, path and HTTP version)
%s The HTTP status code (200, 404 etc) sent to the client with the response
%{?}t The point in time at which the request was received where ? is a standard Go date/time format string (e.g. 02/Jan/2006:15:04:05 Z0700 ). In UTC or local time according to access log configuration
%{?}T The wall-clock time the service spent processing the request in a unit specified by ? where s gives seconds, ms gives milliseconds and us gives microseconds
%u A string representation of the ID of the user on whose behalf the request is being made. Only available if IAM is configured, otherwise the - symbol is printed
%U The path portion of the HTTP request line
%{?}X A value from a context.Context that has been made available to the access logger via a component you have written implementing logging.ContextFilter where ? is the key to the value

JSON access log line format

As an alternative to the semi-structured lines of plain text described above, Granitic can be configured to write each access log entry as a single line JSON document. This feature can be enabled by setting:

HTTPServer.AccessLog.Entry to "JSON" in your configuration. This will cause your application to log access lines like:

{"BytesReturned":"60","ForwardedFor":"","Method":"GET","Path":"/test","ProcessTimeMicro":"33","Query":"","Received":"28/Apr/2020:13:31:05 +0100","Remote":"[::1]:53003","Status":"200"}

The name of each generated JSON field and the associated value can be changed in configuration. The default can be seen in the configuration sample at the top of this page at HTTPServer.AccessLog.JSON.Fields

Each entry in the Fields array is a string array with two or three elements. * The name of the field as it will be written in JSON. * The type of data (content type) that should be used to set a value for the field * An optional argument if required by the content type

Content Type Meaning and usage Argument
CONTEXT_VALUE A value from a context.Context that has been exposed by a logging.ContextFilter component The string key of the exposed value
REMOTE The equivalent to %h in text log lines N/A
REQ_HEADER The equivalent to %{?}i in text log lines The name of the header
RECEIVED The equivalent to %{?}t in text log lines A standard Go datetime format string
HTTP_METHOD The equivalent to %m in text log lines N/A
REQ_PATH The equivalent to %U in text log lines N/A
STATUS The equivalent to %s in text log lines N/A
BYTES_OUT The equivalent to %B in text log lines N/A
PROCESS_TIME The equivalent to %{?}T in text log lines SECONDS, MILLI or MICRO
REQUEST_LINE The equivalent to %r in text log lines N/A
INSTANCE_ID The ID assigned to the current instance of your application N/A
TEXT The static text specified in the argument The text to use as a value

Line prefix and suffix

Each line of JSON formatted log entry can be prefixed or suffixed with a static string by setting HTTPServer.AccessLog.JSON.Prefix (default empty string) or HTTPServer.AccessLog.JSON.Prefix (default \n)

Lifecycle

The IOC component that represents the HTTP server is integrated with Granitic’s component lifecycle model and has different behaviour depending on which lifecycle state or transition the component is in.

The server can be suspended and resumed using runtime control

Start

  • Finds any other components required to run (handlers, instrumentation, abnormal status writer, request identifier)
  • DOES NOT open or listening on the configured address and port

Allow Access

  • Confirms that it is possible to listen on the configured address and port
  • Starts listening for HTTP requests on the configured address and port

Suspend

  • Keeps listening for requests but sends a ‘too busy’ response (default 503)

Resume

  • Allows requests to be processed again

Prepare to stop

  • Keeps processing existing requests but sends a ‘too busy’ response (default 503) for any new requests

Ready to stop check

  • Returns true if no requests are currently being processed

Stop

  • Shuts down the underlying HTTP server, terminating any requests that are still running

Component reference

The following components are created when this facility is enabled:

Name Type
grncHTTPServer httpserver.HTTPServer
grncAccessLogWriter httpserver.AccessLogWriter

Next: Logger facility

Prev: Facilities index