# HTTP

> Octelium documentation. Canonical page: <https://octelium.com/docs/octelium/latest/management/core/service/http>.

Octelium supports the `HTTP` mode for any HTTP-based upstreams (e.g. HTTP APIs, web-apps, websockets, gRPC, etc...). You can create an `HTTP` *Service* simply as follows:

```yaml
kind: Service
metadata:
  name: svc1
spec:
  mode: HTTP
  port: 8080
  config:
    upstream:
      url: https://example.com
```

> **Note:**
>
> For internal/private upstreams behind NAT, you need to remotely serve them via a connected `octelium` client or container as discussed [here](https://octelium.com/docs/octelium/latest/management/core/service/overview.md#remotely-via-a-connected-user).

## Web App Mode

You can use the `WEB` mode to denote that the *Service* is a web application that can be accessed by `HUMAN` *Users* via their browsers. This mode enables the web [*Portal*](https://octelium.com/docs/octelium/latest/reference/components.md#portal) to include a "Visit" button to open the *Service* homepage in a new tab. Here is an example:

```yaml
kind: Service
metadata:
  name: grafana1
spec:
#!mark
  mode: WEB
  port: 80
  isPublic: true
  config:
    upstream:
      url: https://grafana.mycluster.svc
```

## Access Control

You can control access based on the HTTP request information. Such information are stored in `ctx.request.http` where it contains the method, path, headers map, the request body and the serialized JSON body map if available as well as other information such as the scheme and full URI. Here is a detailed example of a inline *Policy* that controls access based on HTTP-specific information:

```yaml
kind: Service
metadata:
  name: svc1
spec:
  mode: HTTP
  port: 8080
  config:
    upstream:
      url: https://example.com
  authorization:
    inlinePolicies:
      - spec:
          rules:
          - effect: ALLOW
            condition:
              all:
                of:
                - match: ctx.request.http.method in ["GET", "POST", "PUT", "DELETE"]
                - match: ctx.request.http.path.startsWith("/apis")
                - match: ctx.request.http.uri == "/apis/users?name=john"
                - match: ctx.request.http.queryParams.name == "john"
                - match: ctx.request.http.headers["x-custom-header"] == "this-value"
                - match: ctx.request.http.scheme == "http"
                - match: string(ctx.request.http.body).toLower().contains("value1")
                - match: ctx.request.http.bodyMap.key1 == "value1"
```

## Secretless Access

Octelium is capable of supporting secretless access to protected upstreams (read more [here](https://octelium.com/docs/octelium/latest/management/core/service/secretless.md)) by injecting HTTP-based credentials such as API keys, which are stored and represented in the *Cluster* as *Secrets* (read more about *Secrets* [here](https://octelium.com/docs/octelium/latest/management/core/secret.md)), on-the-fly to the protected upstream. This eliminates the need to share, mange, distribute and store such typically long-lived, privileged and prone-to-misuse credentials. Octelium supports the following authentication methods:

- Basic authentication
- Bearer authentication
- Custom header authentication
- OAuth2 client credentials flow

You first need to create a *Secret* that holds your credential via `octeliumctl create secret` (read more [here](https://octelium.com/docs/octelium/latest/management/core/secret.md#creating-a-secret)) as follows:

```bash
octeliumctl create secret my-api-key
```

And then reference that *Secret* depending on your authentication scheme as shown below.

### Basic Authentication

In this scheme, authentication information is injected by the *Service* as `Basic <base64(username:password_secret)>` for the `Authorization` request header when the request is sent to the upstream. Here is an example:

```yaml
kind: Service
metadata:
  name: svc1
spec:
  mode: HTTP
  port: 80
  config:
    upstream:
      url: https://api.example.com
    http:
      #!mark(1:5)
      auth:
        basic:
          username: user1
          password:
            fromSecret: my-api-key
```

### Bearer Authentication

In this scheme, authentication information is injected by the *Service* as `Bearer <bearer_secret>` for the `Authorization` request header when the request is sent to the upstream. Here is an example:

```yaml
kind: Service
metadata:
  name: svc1
spec:
  mode: HTTP
  port: 80
  config:
    upstream:
      url: https://api.example.com
    # !mark(1:4)
    http:
      auth:
        bearer:
          fromSecret: my-api-key
```

### Authentication via a Custom Header

You can also set your *Secret* value to a custom request header to the upstream. Here is an example:

```yaml
kind: Service
metadata:
  name: svc1
spec:
  mode: HTTP
  port: 80
  config:
    upstream:
      url: https://api.example.com
    http:
      # !mark(1:5)
      auth:
        custom:
          header: X-Custom-Auth
          value:
            fromSecret: my-api-key
```

### OAuth2 Client Credentials

You can also use OAuth2 client credentials authentication flow (read more [here](https://oauth.net/2/grant-types/client-credentials/)). Here is an example:

```yaml
kind: Service
metadata:
  name: svc1
spec:
  mode: HTTP
  port: 80
  config:
    upstream:
      url: https://api.example.com
    http:
      # !mark(1:7)
      auth:
        oauth2ClientCredentials:
          clientID: my-oauth2-client-id
          clientSecret:
            fromSecret: my-oauth2-client-key
          tokenURL: https://oauth2.example.com/v1
          scopes: ["scope1", "scope2"]
```

### Sigv4 Authentication

Octelium can also compute and inject [AWS Sigv4 auth](https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-authenticating-requests.html) signatures on the fly for any AWS Sigv4 compliant API for a specific service (e.g. Lambda, S3, etc...) and region. Here is an example for a Lambda [function URL](https://docs.aws.amazon.com/lambda/latest/dg/urls-configuration.html) that's protected by Sigv4:

```yaml
kind: Service
metadata:
  name: lambda-01
spec:
  mode: HTTP
  isPublic: true
  config:
    upstream:
      url: https://abcd...efgh.lambda-url.eu-central-1.on.aws
    http:
    # !mark(1:7)
      auth:
        sigv4:
          accessKeyID: ABCD...EFGH
          region: eu-central-1
          service: lambda
          secretAccessKey:
            fromSecret: lambda-access-key
```

> **Note:**
>
> You can see a detailed example for S3 [here](https://octelium.com/docs/octelium/latest/management/guide/service/http/s3-zero-trust-secretless-access.md). You can also see a detailed example for Lambda [here](https://octelium.com/docs/octelium/latest/management/guide/service/http/lambda-zero-trust-secretless-access.md).

## HTTP/2

### Listening on HTTP/2

By default, the *Service* listens to HTTP 1.1 connections unless TLS is enabled (read more [here](https://octelium.com/docs/octelium/latest/management/core/service/overview.md#tls)). However you can force the *Service* to accept HTTP 2.0 connections as follows:

```yaml
kind: Service
metadata:
  name: svc1
spec:
  mode: HTTP
  port: 80
  config:
    upstream:
      url: https://example.com
    http:
    # !mark
      listenHTTP2: true
```

### HTTP/2 Upstream

A *Service* by default assumes that the upstream accepts HTTP 1.1 connections unless it's serving TLS connections. You can force the *Service* to forward the requests over HTTP/2 as follows:

```yaml
kind: Service
metadata:
  name: svc1
spec:
  mode: HTTP
  port: 80
  config:
    upstream:
      url: https://example.com
    http:
    # !mark
      isUpstreamHTTP2: true
```

## Header Manipulation

You can easily manipulate both request and response HTTP headers as follows:

```yaml
kind: Service
metadata:
  name: svc1
spec:
  mode: HTTP
  port: 80
  config:
    upstream:
      url: https://example.com
    http:
      header:
        addRequestHeaders:
        - key: X-HEADER-1
          value: VALUE-1
        - key: X-HEADER-2
          value: VALUE-2
        removeRequestHeaders:
        - X-HEADER-3
        - X-HEADER-4
        addResponseHeaders:
        - key: X-HEADER-5
          value: VALUE-5
        - key: X-HEADER-6
          value: VALUE-6
        removeResponseHeaders:
        - X-HEADER-7
        - X-HEADER-8
```

By default `addRequestHeaders` and `addResponseHeaders` actually set or "override" any existing header value. To append instead of overriding such values, you can simply use the `append` boolean option as follows:

```yaml
kind: Service
metadata:
  name: svc1
spec:
  mode: HTTP
  port: 80
  config:
    upstream:
      url: https://example.com
    http:
      header:
        addRequestHeaders:
        - key: X-HEADER-1
          value: VALUE-1
          #!mark
          append: true
        addResponseHeaders:
        - key: X-HEADER-2
          value: VALUE-2
          #!mark
          append: true
```

You can also dynamically set the added request/response header's value via a CEL expression. Here is an example:

```yaml
kind: Service
metadata:
  name: my-svc
spec:
  mode: HTTP
  port: 80
  config:
    upstream:
      url: https://example.com
    http:
      header:
        addRequestHeaders:
        - key: X-User-Uid
          # !mark
          eval: ctx.user.metadata.uid
        - key: X-User-Email
          # !mark
          eval: ctx.user.spec.email
        addResponseHeaders:
        - key: X-Service-Uid
          # !mark
          eval: ctx.service.metadata.uid
```

### Host Header

By default, *Vigil* automatically rewrites the Host header to the upstream's real host (e.g. the value in `spec.config.upstream.url`). However, you can override that behavior and explicitly set the host headers using one of the following methods:

- Preserve the host header and use the value of the *Service*'s public FQDN as follows:

```yaml
kind: Service
metadata:
  name: svc1
spec:
  mode: HTTP
  port: 80
  config:
    upstream:
      url: https://example.com
    http:
      header:
        # !mark(1:2)
        host:
          preserve: true
```

- Explicitly set a string value for the header as follows:

```yaml
kind: Service
metadata:
  name: svc1
spec:
  mode: HTTP
  port: 80
  config:
    upstream:
      url: https://example.com
    http:
      header:
        # !mark(1:2)
        host:
          value: my.example.com
```

- Use a CEL expression to evaluate the header value on a per-request basis as follows:

```yaml
kind: Service
metadata:
  name: svc1
spec:
  mode: HTTP
  port: 80
  config:
    upstream:
      url: https://example.com
    http:
      header:
        # !mark(1:2)
        host:
          eval: ctx.service.metadata.name + ".example.com"
```

### Forwarded Header

By default, *Vigil* automatically deletes the Forwarded request header (read more [here](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Forwarded)), if set by the downstream, to protect the downstream's privacy and also to protect the upstream from possible spoofing by a malicious downstream. You can, however, explicitly override that default behavior by using the `OBFUSCATE` mode to obfuscate the `for` and `by` values while using the *Service* public FQDN as the `host` value. Here is an example:

```yaml
kind: Service
metadata:
  name: svc1
spec:
  mode: HTTP
  port: 80
  config:
    upstream:
      url: https://example.com
    http:
      header:
        # !mark
        forwardedMode: OBFUSCATE
```

You can also use `TRANSPARENT` mode to simply pass the Forwarded request header as is, if set by the downstream. You can also use the `TRANSPARENT` mode to set your own `X-Forwarded-*` headers via `addRequestHeaders`. Here is a complete example:

```yaml
kind: Service
metadata:
  name: svc1
spec:
  mode: HTTP
  port: 80
  config:
    upstream:
      url: https://example.com
    http:
      header:
        addRequestHeaders:
          - key: X-Forwarded-For
            value: 10.0.0.100
          - key: X-Forwarded-Proto
            value: https
          - key: X-Forwarded-Host
            value: example.com
        # !mark
        forwardedMode: TRANSPARENT
```

### Authorization Header

By default, the downstream's `Authorization` request header is automatically suppressed/deleted by *Vigil* unless the *Service* is anonymous (read more [here](https://octelium.com/docs/octelium/latest/management/core/service/anonymous-access.md)). You can, however, override that default behavior by explicitly setting the `authorizationMode` to either `PASS` to pass the downstream's `Authorization` request header as is, if exists, or `DELETE` to delete it. Here is an example:

```yaml
kind: Service
metadata:
  name: svc1
spec:
  mode: HTTP
  port: 80
  config:
    upstream:
      url: https://api.example.com
    http:
      header:
        # !mark
        authorizationMode: PASS
```

## Path Manipulation

You can add a prefix to the request's path as follows:

```yaml
kind: Service
metadata:
  name: svc1
spec:
  mode: HTTP
  port: 80
  config:
    upstream:
      url: https://example.com
    http:
      path:
        addPrefix: /prefix
```

Now a request with the path `/v1` will become `/prefix/v1`.

And you can also remove a prefix from the path as follows:

```yaml
kind: Service
metadata:
  name: svc1
spec:
  mode: HTTP
  port: 80
  config:
    upstream:
      url: https://example.com
    http:
      path:
        removePrefix: /prefix
```

Now a request with the path `/prefix/v1` will become `/v1`.

And you can replace a prefix by another by combining both `removePrefix` and `addPrefix` as follows:

```yaml
kind: Service
metadata:
  name: svc1
spec:
  mode: HTTP
  port: 80
  config:
    upstream:
      url: https://example.com
    http:
      path:
        removePrefix: /v1
        addPrefix: /v2
```

Now a request with the path `/v1/path` will become `/v2/path`.

## Request Body

Octelium enables you to buffer the entire request body before forwarding it to the upstream for it to be checked and used in your access *Policies* (read more [here](https://octelium.com/docs/octelium/latest/management/core/policy.md)) and dynamic configuration (read more [here](https://octelium.com/docs/octelium/latest/management/core/service/dynamic-config.md)).

```yaml
kind: Service
metadata:
  name: svc1
spec:
  mode: HTTP
  port: 80
  config:
    upstream:
      url: https://example.com
    http:
      #!mark
      enableRequestBuffering: true
```

This will include your entire body as base64 string inside the `ctx.request.http.body` (read more [here](https://octelium.com/docs/octelium/latest/management/core/service/http.md#access-control)) field used by your *Policies* and dynamic configuration rules.

### Request Body Maximum Size

You can set the limit on the maximum number of bytes for a request body as follows:

```yaml
kind: Service
metadata:
  name: svc1
spec:
  mode: HTTP
  port: 80
  config:
    upstream:
      url: https://example.com
    http:
      #!mark(1:3)
      enableRequestBuffering: true
      body:
        maxRequestSize: 100000
```

### JSON Request Body

In many cases such as in REST APIs, the request body is represented by a JSON format. You can enable the body mode as `JSON` to verify that the request body is a valid JSON as follows:

```yaml
kind: Service
metadata:
  name: svc1
spec:
  mode: HTTP
  port: 80
  config:
    upstream:
      url: https://example.com
    http:
      enableRequestBuffering: true
      #!mark(1:2)
      body:
        mode: JSON
```

The `JSON` modes lets you to directly use the fields of the JSON structure in your *Policies* and dynamic configuration rules inside the `ctx.request.http.bodyMap` field. Here is an example, for a request body with a schema that looks as following:

```json
{
  "key1": "value1",
  "key2": 5,
  "key3:": {
    "key4": "value4"
  }
}
```

You can set your access *Policy* rules as follows:

```yaml
kind: Service
metadata:
  name: svc1
spec:
  mode: HTTP
  port: 8080
  config:
    upstream:
      url: https://example.com
  authorization:
    inlinePolicies:
      - spec:
          rules:
          - effect: ALLOW
            condition:
              all:
                of:
                #!mark(1:3)
                - match: ctx.request.http.bodyMap.key1 == "value1"
                - match: ctx.request.http.bodyMap.key2 > 2
                - match: ctx.request.http.bodyMap.key3.key4 == "value4"
```

## Body Validation

### JSON Schema Validation

You can validate the JSON body via a JSON schema simply as follows:

```yaml
kind: Service
metadata:
  name: svc1
spec:
  mode: HTTP
  port: 80
  config:
    upstream:
      url: https://example.com
    http:
      enableRequestBuffering: true
      body:
        validation:
          jsonSchema:
            inline: |
              {
                "$schema": "http://json-schema.org/draft-07/schema#",
                ...
```

If the body content is not valid according to the schema, then a status code of `400` is returned.

> **Note:**
>
> You can use dynamic configuration (read more [here](#dynamic-configuration)), for example for different API REST paths referring to different API resources, to actually use multiple JSON schemas.

## Direct Response

> **Note:**
>
> Since version `v0.16`, it is more recommended to use direct response plugins. Read more [here](https://octelium.com/docs/octelium/latest/management/core/service/http-plugins.md#direct-response)

You can also return direct response without proxying the request to the upstream. Here is an example of returning a string:

```yaml
kind: Service
metadata:
  name: svc1
spec:
  mode: HTTP
  port: 80
  config:
    upstream:
      url: https://example.com
    http:
      response:
        direct:
          inline: Hello World
```

You can also return a stream of bytes encoded by base64. Here is an example:

```yaml
kind: Service
metadata:
  name: svc1
spec:
  mode: HTTP
  port: 80
  config:
    upstream:
      url: https://example.com
    http:
      response:
        direct:
        #!mark
          inlineBytes: iVBORw0KGgoAAAANS....
```

you can also set the response status code and content type of the direct response as follows:

```yaml
kind: Service
metadata:
  name: svc1
spec:
  mode: HTTP
  port: 80
  config:
    upstream:
      url: https://example.com
    http:
      response:
        direct:
          #!mark(1,2)
          statusCode: 200
          contentType: image/png
          inlineBytes: iVBORw0KGgoAAAANS....
```

> **Note:**
>
> You can use dynamic configuration (read more [here](#dynamic-configuration)) to return different direct responses for different request paths or even different identities or contexts.

## Retry

By default, the *Service* does not retry a failed request (e.g. whose response status code is `503`). You can, however, enable retries with sensible defaults as follows:

```yaml
kind: Service
metadata:
  name: svc1
spec:
  mode: HTTP
  port: 80
  config:
    upstream:
      url: https://example.com
    http:
      # !mark
      retry: {}
```

You can explicitly set your retry-specific configurations. The *Service* uses exponential backoff that starts with an `initialInterval` duration. This duration is multiplied on each retry by a `multiplier` floating point value until it reaches the value of a `maxInterval` duration. The whole process can have a maximum number of retries that can be set via `maxRetries` and a deadline duration that is controlled by `maxElapsedTime`. By default, the *Service* currently retries on `502`, `503` and `504` status codes; however, you can explicitly set the status codes via the `statusCodes` list and additionally use any 5xx error via the `retryOnServerErrors` boolean flag. Here is an example:

```yaml
kind: Service
metadata:
  name: svc1
spec:
  mode: HTTP
  port: 80
  config:
    upstream:
      url: https://example.com
    http:
      retry:
        maxRetries: 10
        initialInterval:
          milliseconds: 500
        maxInterval:
          seconds: 5
        maxElapsedTime:
          seconds: 12
        multiplier: 1.5
        statusCodes: [503, 429]
        retryOnServerErrors: true
```

## HTTPS

By default, the *Service* listens over plaintext TCP acting as a plaintext HTTP server regardless of whether the upstream is an HTTPS or an HTTP server. You can turn the *Service* into an HTTPS *Service*, however, via the `isTLS` flag as follows:

```yaml
kind: Service
metadata:
  name: svc1
spec:
  mode: HTTP
  port: 443
  # !mark
  isTLS: true
  config:
    upstream:
      url: https://example.com
```

> **Note:**
>
> A *Service* is accessed over public HTTPS if the public BeyondCorp mode is enabled via `isPublic` field (read more [here](https://octelium.com/docs/octelium/latest/management/core/service/clientless.md)), regardless of whether the *Service* is using `isTLS` or not. In other words, `isTLS` is only effective for the private client-based mode. Therefore, if `isPublic` is enabled while `isTLS` is not, then the *Service* is accessed publicly over HTTPS while over plaintext via the client-based mode.

## TLS Config

The *Service* can automatically connect to an HTTPS upstream whose certificate is signed by a public CA. If the upstream is using a private CA, however, you will have to manually add that root CA in a list of `trustedCAs`. Here is an example:

```yaml
kind: Service
metadata:
  name: my-api
spec:
  mode: HTTP
  config:
    upstream:
      url: https://my-upstream:6443
    # !mark(1:6)
    tls:
      trustedCAs:
      - |
        -----BEGIN CERTIFICATE-----
        CA
        -----END CERTIFICATE-----
```

You can also skip verifying the upstream's certificate altogether via the `insecureSkipVerify` which is typically recommended only for troubleshooting/testing use cases. Here is an example:

```yaml
kind: Service
metadata:
  name: my-api
spec:
  mode: HTTP
  config:
    upstream:
      url: https://my-upstream:6443
    # !mark(1:2)
    tls:
      insecureSkipVerify: true
```

If the upstream requires mutual TLS (mTLS), you will have to include your client certificate private key as a *Secret* (read more [here](https://octelium.com/docs/octelium/latest/management/core/secret.md#tls-certificate)), and then reference it as follows:

```yaml
kind: Service
metadata:
  name: my-api
spec:
  mode: HTTP
  config:
    upstream:
      url: https://my-upstream:6443
    tls:
      trustedCAs:
      - |
        -----BEGIN CERTIFICATE-----
        CA
        -----END CERTIFICATE-----
      # !mark(1:2)
      clientCertificate:
        fromSecret: client-private-key
```

## Cross-Origin Resource Sharing (CORS)

You can also enforce [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) rules as follows:

```yaml
kind: Service
metadata:
  name: svc1
spec:
  mode: HTTP
  port: 80
  config:
    upstream:
      url: https://example.com
    http:
      cors:
        # The value "*" is also valid
        allowOriginStringMatch: ["https://example.com"]
        allowMethods: "POST, GET, OPTIONS"
        allowHeaders: "X-PINGOTHER, Content-Type"
        exposeHeaders: "Content-Encoding, X-Custom-Header"
        maxAge: "86400"
        allowCredentials: true
```

## Dynamic Configuration

You can use dynamic configuration in order to, for example, route to different upstreams and/or setting different upstream credentials, set different request/response headers, etc... depending on the request's context (read more about dynamic configuration [here](https://octelium.com/docs/octelium/latest/management/core/service/dynamic-config.md)). Here is a simple example for an API gateway (read more [here](https://octelium.com/docs/octelium/latest/management/guide/service/http/api-gateway.md)):

```yaml
kind: Service
metadata:
  name: my-api
spec:
  mode: HTTP
  isPublic: true
  dynamicConfig:
    configs:
    - name: v1
      upstream:
        url: https://apiv1.example.com
      http:
        path:
          removePrefix: /v1
    - name: v2
      upstream:
        url: https://apiv2.example.com
      http:
        path:
          removePrefix: /v2
    rules:
    - condition:
        match: ctx.request.http.path.startsWith("/v1")
      configName: v1
    - condition:
        match: ctx.request.http.path.startsWith("/v2")
      configName: v2
```

## Visibility

The *Service* emits *AccessLogs* in real time to the audit collector. Each *AccessLog* provides application-layer aware information about the request such as the request path, method, etc... as well as the response code and body size. Here is an example:

```json
{
  "apiVersion": "core/v1",
  "kind": "AccessLog",
  "metadata": {
    // Omitted for brevity
  },
  "entry": {
      // Omitted for brevity
    },
    "info": {
      "http": {
        "request": {
          "path": "/",
          "userAgent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36",
          "method": "GET",
          "uri": "/?arg=value"
        },
        "response": {
          "code": 200,
          "bodyBytes": "615",
          "body": "PCFET0NUWV...",
          "contentType": "text/html"
        },
        "httpVersion": "HTTP11"
      }
    }
  }
}
```

## Visibility Options

By default, the *Service* does not log the request and response body content. However, you can explicitly enable capturing the request/response body content as well as the serialized JSON body map as follows:

```yaml
kind: Service
metadata:
  name: my-api
spec:
  mode: HTTP
  port: 80
  config:
    upstream:
      url: https://api.example.com
    http:
    # !mark(1:5)
      visibility:
        enableRequestBody: true
        enableRequestBodyMap: true
        enableResponseBody: true
        enableResponseBodyMap: true
```

You can also record all or certain request/response headers inside *AccessLogs* as follows:

```yaml
kind: Service
metadata:
  name: my-api
spec:
  mode: HTTP
  port: 80
  config:
    upstream:
      url: https://api.example.com
    http:
    # !mark(1:7)
      visibility:
        includeAllRequestHeaders: true
        includeAllResponseHeaders: true
        includeRequestHeaders: ["X-Custom-Header-1", "X-Custom-Header-2"]
        includeResponseHeaders: ["X-Custom-Header-3", "X-Custom-Header-4"]
        excludeRequestHeaders: ["X-Custom-Header-5", "X-Custom-Header-6"]
        excludeResponseHeaders: ["X-Custom-Header-7", "X-Custom-Header-8"]
```

## gRPC Mode

In the `GRPC` mode, the *Service* automatically listens over HTTP/2 as required by gRPC protocol. Furthermore, authorization and visibility take advantage of decoding the requests as gRPC requests.

You can control access based on the gRPC request information. Such information are stored in `ctx.request.grpc` where it contains the service, method and package values. Additionally all the HTTP related information that are exposed in the `HTTP` mode are also exposed in the variable `ctx.request.grpc.http`. Here is a detailed example of a inline *Policy* that controls access based on gRPC-specific information:

```yaml
kind: Service
metadata:
  name: svc1
spec:
  #!mark
  mode: GRPC
  port: 8080
  config:
    upstream:
      url: https://my-grpc-api.example.com
  authorization:
    inlinePolicies:
      - spec:
          rules:
            - effect: ALLOW
              condition:
                all:
                  of:
                    - match: ctx.request.grpc.method == "GetUser"
                    - match: ctx.request.grpc.service == "MainService"
                    - match: ctx.request.grpc.package == "octelium.api.main.core.v1"
                    - match: ctx.request.grpc.serviceFullName == "octelium.api.main.core.v1.MainService"
                    - match: ctx.request.grpc.http.path.startsWith("/octelium.api")
                    - match: ctx.request.grpc.http.headers["x-custom-header"] == "this-value"
```

As for visibility, the access log contains gRPC-specific information such as the package, service and method in addition to the underlying HTTP information. Here is an example:

```json
{
  "apiVersion": "core/v1",
  "kind": "AccessLog",
  "metadata": {
    // Omitted for brevity
  },
  "entry": {
      // Omitted for brevity
    },
    "info": {
      "grpc": {
        "http": {
          "request": {
            "path": "/octelium.api.main.core.v1.MainService/ListService",
            "userAgent": "octelium-cli/v0.17.1 grpc-go/1.71.1",
            "method": "POST",
            "uri": "/octelium.api.main.core.v1.MainService/ListService"
          },
          "response": {
            "code": 200,
            "bodyBytes": "19130",
            "contentType": "application/grpc"
          },
          "httpVersion": "HTTP2"
        },
        "method": "ListService",
        "service": "MainService",
        "serviceFullName": "octelium.api.main.core.v1.MainService",
        "package": "octelium.api.main.core.v1"
      }
    }
  }
}
```
