On this page:

22.0HTTP Server Setup

 

This page provides information on setting up an HTTP server module, such as a FHIR REST endpoint, or the Web Admin Console.

22.0.1Respecting Forward Headers

 

If the server is behind a reverse proxy, load balancer, or other network infrastructure, this may obscure the originating network details of clients making requests. For example, if all requests are routed through a load balancer, by default all requests will appear to come from that load balancer. This means that audit/transaction logs may show incorrect information, and cause other similar issues.

In order to avoid this issue, you can configure your load balancer to provide a Forwarded header to Smile CDR containing details about the original client's location and connection. In order for Smile CDR to respect this header, you will need to enable the Respect Forward Headers setting.

You can learn more about the Forwarded header (and its legacy equivalents, which are also supported) on the MDN Forwarded page.

The following headers are supported:

  • Forwarded
  • X-Forwarded-Host
  • X-Forwarded-Server
  • X-Forwarded-For
  • X-Forwarded-Proto
  • X-Proxied-Https
  • Proxy-auth-cert
  • Proxy-ssl-id

Reverse Proxying with NGINX

A typical configuration for setting up a reverse proxy using NGINX is shown below. In this example, port 443 (the default https port) is being forwarded to a Smile CDR instance listening on port 8000 (the typical FHIR listener port).

server {
    listen 443 ssl default_server;
    location / {
        proxy_set_header    Host                        $host;
        proxy_set_header    X-Real-IP                   $remote_addr;
        proxy_set_header    X-Forwarded-For             $proxy_add_x_forwarded_for;
        proxy_set_header    X-Forwarded-Host   $host:443;
        proxy_set_header    X-Forwarded-Server $host;
        proxy_set_header    X-Forwarded-Port   443;
        proxy_set_header    X-Forwarded-Proto  https;
        proxy_pass          http://localhost:8000/;
    }
}

22.0.2Specifying a Custom Context Path

 

In some cases, it is desirable to have an HTTP server module not serve its contents from the root URL on the server.

For example, suppose a Smile CDR instance is set up with two modules, each providing an HTTP server. The first is a JSON Admin Endpoint listening on port 9000 and the second is the Web Web Console listening on port 9100.

A reverse proxy such as NGINX can be configured to listen on a single port and then to serve both Smile CDR modules through this same proxy on the same port.

An example of this configuration is shown in the following diagram.

Alternate Context Root

In order to support this configuration, the reverse proxy could be configured to route requests to path /adminjson on port 443 to port 9000 and to route requests to path /adminweb on port 443 to port 9100.

In order for this setup to work correctly however, the two modules need to be configured to understand that a request to (for example) /adminweb/index.html is actually only a request for /index.html, and that any absolute links served to the client need to include the initial part of the path.

This is accomplished by setting the context_path configuration property.

Reverse Proxying with NGINX

A typical configuration for setting up a reverse proxy using NGINX is shown below. In this example, port 443 (the default https port) is being forwarded to a Smile CDR instance listening on ports 9000 and 9100 (the typical JSON Admin API, and Web Admin Console ports respectively).

server {
    listen 443 ssl default_server;
    location /adminjson {
        proxy_set_header    Host                        $host;
        proxy_set_header    X-Real-IP                   $remote_addr;
        proxy_set_header    X-Forwarded-For             $proxy_add_x_forwarded_for;
        proxy_set_header    X-Forwarded-Host   $host:443;
        proxy_set_header    X-Forwarded-Server $host;
        proxy_set_header    X-Forwarded-Port   443;
        proxy_set_header    X-Forwarded-Proto  https;
        proxy_pass          http://localhost:9000/admin;
    }
    location /adminweb {
        proxy_set_header    Host                        $host;
        proxy_set_header    X-Real-IP                   $remote_addr;
        proxy_set_header    X-Forwarded-For             $proxy_add_x_forwarded_for;
        proxy_set_header    X-Forwarded-Host   $host:443;
        proxy_set_header    X-Forwarded-Server $host;
        proxy_set_header    X-Forwarded-Port   443;
        proxy_set_header    X-Forwarded-Proto  https;
        proxy_pass          http://localhost:9100/fhir;
    }
}

22.0.3Access Logs

 

HTTP servers provided by Smile CDR may be configured to create one or more access logs. These logs can log every HTTP request that is processed by the system at a low level.

Access logs are named appenders that are processed through the standard Logback logging facility. See System Logging for information on how to configure Logback.

To create logs, an access log definition should be placed in the access_log.appenders property.

The value for this property represents the complete collection of appenders, where each line is a new appender. Each line in the property value should take the format:

[appender.name]=[access log line format]

For example, a simple access log could be created by using the following property value:

customer.fhir_endpoint.access=Request: $(request_time_iso8601) $(method) $(request_uri_original) $(request_protocol)

As you can see, the property value has a number of variable substitutions taking the form $(name). A list of available substitutions appears below.

Also note that the appender name (e.g. customer.fhir_endpoint.access) can be any identifier you want. In order to avoid conflicts with internal Smile CDR log appenders, it is recommended to prefix the appender name with customer. as shown.

HTTP Troubleshooting Logs

Note that as an alternative to defining individual access logs, the HTTP Troubleshooting Log can also be used. This log can be dynamically added to a running server without needing to restart the module, which can be a benefit in some troubleshooting circumstances.

Redirecting Access Logs to a Dedicated File

By default, this appender will output to the same smile.log log file where all other logging appears. This may be redirected to a dedicated access log file by placing an appender definition and a logger definition in the Smile CDR logback.xml file (found in the classes/ directory inside your Smile CDR installation). An example is shown below:

<appender name="FHIR_ENDPOINT_ACCESS" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>${smile.basedir}/log/fhir_endpoint_access.log</file>
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        <fileNamePattern>${smile.basedir}/log/fhir_endpoint_access.%d{yyyy-MM-dd}.log.gz</fileNamePattern>
        <maxHistory>30</maxHistory>
    </rollingPolicy>
    <encoder>
        <pattern>%msg%n</pattern>
    </encoder>
</appender>
<logger name="customer.fhir_endpoint.access" additivity="false">
    <appender-ref ref="FHIR_ENDPOINT_ACCESS"/>
</logger>

Available Substitutions

Name Description
$(n) A newline character.
$(module_id) The ID of the Smile CDR module this HTTP server is running on.
$(node_id) The ID of the Smile CDR node this HTTP server is running on.
$(request_time_strftime) The request date/time in strftime format (e.g. 10/Oct/2000:13:55:36 -0700).
$(request_time_iso8601) The request date/time in ISO 8601 format (e.g. 2018-07-18T21:08:32+00:00).
$(remote_ip) The IP of the requesting client.
$(method) The HTTP verb.
$(request_uri_original) The requested URI (e.g. /Patient/123).
$(request_protocol) The requested protocol (e.g. HTTP/1.0).
$(response_status_code) The response status code (e.g. 200).
$(response_bytes_written) The number of bytes written to the response.
$(request_header_cache_control) The value of the request Cache-Control header.
$(request_headers_all) The complete set of headers received in the request. Note that these headers are newline-separated, and this variable substitution will therefore likely write multiple lines to your log file.
$(request_headers_fhir) All request headers starting with "fhir-". Note that these headers are newline-separated, and this variable substitution will therefore likely write multiple lines to your log file.
$(response_headers_all) The complete set of headers returned by Smile CDR in the response. Note that these headers are newline-separated, and this variable substitution will therefore likely write multiple lines to your log file.
$(response_latency_millis)
$(thread_name) The name of the servicing thread (useful to diagnose multithreaded REST issues).

Example Patterns

NCSA Common Log Format

The following pattern generates an access log using the NCSA Common Log Format.

customer.fhir_endpoint.access=$(remote_ip) - - [$(request_time_strftime)] "$(method) $(request_uri_original) $(request_protocol)" $(response_status_code) $(response_bytes_written)

This pattern produces an output similar to the following:

127.0.0.1 - - [18/Jul/2018:17:25:29 -0400] "GET /Patient HTTP/1.1" 200 9959

Enhanced Access Log Format

customer.fhir_endpoint.access=$(request_time_iso8601) [$(remote_ip) $(thread_name)] $(method) $(request_uri_original) $(request_protocol) -- $(response_status_code) $(response_bytes_written) $(response_latency)

Log Request Headers

The following pattern logs all requests, including the request headers (this will be verbose, but can be useful for troubleshooting).

customer.fhir_endpoint.access=$(request_time_iso8601) $(method) $(request_uri_original)$(n)$(request_headers_all)$(n)

This pattern produces an output similar to the following:

2011-01-01T23:59:59.123-04:00 GET /Patient/123
Host: example.com:8000
Accept-Encoding: gzip,deflate
Accept: application/fhir+json

22.0.4Frame Options

 

By default, Smile CDR HTTP servers use the X-Frame-Options to prevent web content from being embedded in HTML IFrame elements. This is a security measure to prevent Clickjacking attacks.

If you are setting up a system where server content should be available within an IFrame, you can adjust the Frame Options (Allow From) property to allow frame-embedded content.

This setting accepts any of the following values:

  • (Blank)(default) If no value is supplied, responses will be served with an X-Frame-Options value of DENY meaning that content will not be allowed within an IFrame.

  • (Url) – If a URL is supplied, content may be embedded in an IFrame originating from this origin URL.

  • SAMEORIGIN – Content may be served within an IFrame originating at the same origin domain / web server.

  • * – The X-Frame-Options header will not be sent, meaning that content may always be served in an IFrame.