E12023: program creates more than one HTTP instance

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

What went wrong

The program creates more than one HTTP server instance. Each program should have at most one HTTP server. Multiple HTTP instances would compete for service bindings and create ambiguity about which server handles which routes.

Every HTTP the program creates counts, whatever type the variable holding it is declared as: an HTTP held as the Server trait is still an HTTP.

Worked examples

The [EXAMPLE] section below shows a program that serves HTTP twice, then a single HTTP instance (which serves every registered service), and two programs each with its own application and port (good_split.ek9).

Rationale

A single application has a single service registry with a single set of route bindings. Creating multiple HTTP servers from the same application would duplicate those bindings across servers, wasting resources and creating confusion about which server is authoritative.

If you need multiple HTTP servers (e.g., different ports for admin vs public traffic), use separate programs with separate applications.

How to fix it

Remove the duplicate HTTP instance. Keep a single HTTP server per program.

If you need multiple servers on different ports, split into separate programs with separate applications - a PublicServer with a PublicApp on port 8080, an AdminServer with an AdminApp on 9090 (good_split.ek9 in the [EXAMPLE] section below).

See also

  • HTTP without service registrations: E12021
  • HTTP without application linkage: E12022
  • 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 E12023: program creates more than one HTTP instance

#!ek9
defines module error.e12023.bad

  defines service

    HelloService for :/api 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

    HelloApp
      register HelloService()

  defines program

    DuplicateHttpDemo() with application of HelloApp
      props <- NetworkProperties(9090)
      HTTP(props).serve()
      HTTP(props).serve()

Fixed: Create one HTTP server - it serves every registered service

#!ek9
defines module error.e12023.good

  defines service

    HelloService for :/api 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

    HelloApp
      register HelloService()

  defines program

    SingleHttpDemo() with application of HelloApp
      props <- NetworkProperties(9090)
      HTTP(props).serve()

Fixed: Servers on different ports: separate programs, each with its own application

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

  defines service

    PublicService for :/api 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 ?

    AdminService for :/admin 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("admin ok")
          override contentType() as pure
            <- rtn as String: "text/plain"
          default operator ?

  defines application

    PublicApp
      register PublicService()

    AdminApp
      register AdminService()

  defines program

    PublicServer() with application of PublicApp
      HTTP(NetworkProperties(8080)).serve()

    AdminServer() with application of AdminApp
      HTTP(NetworkProperties(9090)).serve()