E12020: application registers services but program has no HTTP server

Category: E12xxx Security constraints · Reported in phase: PRE_IR_CHECKS · Classification: APPLICATION_SERVICES_WITHOUT_HTTP · All error codes

What went wrong

The linked application registers one or more services, but the program never creates an HTTP instance. Nothing will serve those services: the program runs its body and exits, so every registered route is unreachable for the whole life of the process.

Worked examples

The [EXAMPLE] section below shows an application that registers a service, and a program linked to it that never creates an HTTP instance (so it runs its body and exits); then (a) the program serving them with 'HTTP(props).serve()', and (b) the wiring split, so a program that only needs the components links an application that registers only those.

Rationale

A registered service that is never served is dead wiring. It is constructed, it consumes its dependencies, and it can never receive a request — the program terminates as soon as its body completes. Nothing at runtime reports this: the process simply exits successfully, so the mistake is invisible until someone notices the endpoint was never live.

This is the mirror of E12021. Both directions of the same wiring mistake are caught here, because the application's registrations and the program's variable declarations are both known at compile time.

Splitting the application is usually the right answer when a program genuinely needs only the components. Conflating serving wiring with component wiring is what leaves a REST service registered and permanently unreachable.

How to fix it

Either:

  1. Create and serve an HTTP instance in the program (most likely fix): 'HTTP(props).serve()' with the NetworkProperties for its port (good.ek9 in the [EXAMPLE] section below)
  2. Split the application, so this program uses one that registers only the components it needs (good_split.ek9 below), or
  3. Remove the service registration if the service is not wanted.

See also

  • HTTP without service registrations (the opposite direction): E12021
  • HTTP without application linkage: E12022
  • Duplicate HTTP instance: E12023
  • Service security posture: E12050

Examples

Every example is compiled by the EK9 compiler: the first fails exactly as captioned, the others fix it.

Fails with E12020: application registers services but program has no HTTP server

#!ek9
defines module error.e12020.bad

  defines constant

    plainText <- "text/plain"

  defines service

    StatusService for :/status open

      health() as GET for :/health
        <- response as HTTPResponse: () with trait of HTTPResponse
          override status() as pure
            <- rtn as Integer: 200
          override content()
            <- rtn as Bytes: Bytes().withUtf8("ok")
          override contentType() as pure
            <- rtn as String: plainText
          default operator ?

  defines application

    StatusApp
      register StatusService()

  defines program

    ServicesWithoutHttpDemo() with application of StatusApp
      stdout <- Stdout()
      stdout.println("this program exits — StatusService can never be reached")

Fixed: Serve the registered services over HTTP

#!ek9
defines module error.e12020.good

  defines service

    StatusService for :/status open

      health() as GET for :/status
        <- response as HTTPResponse: () with trait of HTTPResponse
          override status() as pure
            <- rtn as Integer: 200
          override content()
            <- rtn as Bytes: Bytes().withUtf8("ok")
          override contentType() as pure
            <- rtn as String: "text/plain"
          default operator ?

  defines application

    StatusApp
      register StatusService()

  defines program

    ServicesWithHttpDemo() with application of StatusApp
      props <- NetworkProperties(9090)
      HTTP(props).serve()

Fixed: Or give a program that does not serve an application with no services

#!ek9
defines module error.e12020.good.split

  defines component

    Repository as abstract
      size() as abstract
        <- rtn as Integer?
      default operator ?

    InMemoryRepository is Repository
      override size()
        <- rtn as Integer: 0
      default operator ?

  defines service

    StatusService for :/status open

      health() as GET for :/status
        <- response as HTTPResponse: () with trait of HTTPResponse
          override status() as pure
            <- rtn as Integer: 200
          override content()
            <- rtn as Bytes: Bytes().withUtf8("ok")
          override contentType() as pure
            <- rtn as String: "text/plain"
          default operator ?

  defines application

    StatusApp
      register InMemoryRepository() as Repository
      register StatusService()

    RepositoryOnly
      register InMemoryRepository() as Repository

  defines program

    Report() with application of RepositoryOnly
      stdout <- Stdout()
      repository as Repository!
      stdout.println($repository.size())

Questions and answers

These Q&A entries show E12020 and its fix: