.. Licensed to the Apache Software Foundation (ASF) under one
   or more contributor license agreements.  See the NOTICE file
   distributed with this work for additional information
   regarding copyright ownership.  The ASF licenses this file
   to you under the Apache License, Version 2.0 (the
   "License"); you may not use this file except in compliance
   with the License.  You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing,
   software distributed under the License is distributed on an
   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   KIND, either express or implied.  See the License for the
   specific language governing permissions and limitations
   under the License.

.. include:: ../../common.defs

.. highlight:: cpp
.. default-domain:: cpp

.. _cripts-global:

Cripts for global plugin
************************

In addition to being a scripting language for per-property, or remap rules, Cripts can
also be used to write global ATS plugins. This is a more advanced use case, and requires
some knowledge of how ATS works.

The automatic building of plugins that Cripts supports as a remap rule is not available
(yet) to global plugins. As such, you must compile any global plugin manually. You can
see this as an alternative to writing a plugin in regular C++, in fact you can still
combine a global plugin with a remap rule plugin in the same Cript file.

Usage
=====

As with remap rules, global Cripts still requires both the preamble as well as the epilogue.
However, all callbacks are prefixed with ``glb_`` to indicate that they are global hooks.
See the :ref:`hooks <cripts-global-hooks>` below for more details.

Global plugins must be compiled manually and loaded through the standard ATS plugin
configuration. Here's a complete example:

.. code-block:: cpp

   #include <cripts/Preamble.hpp>

   glb_init()
   {
     CDebug("Global Cript plugin initialized");
   }

   glb_read_request()
   {
     borrow url = cripts::Client::URL::Get();
     borrow req = cripts::Client::Request::Get();

     // Example: Add a custom header to all requests
     req["X-Global-Plugin"] = "active";

     // Example: Filter query parameters globally
     url.query.Keep({"foo", "bar"});
   }

   glb_send_response()
   {
     borrow resp = cripts::Client::Response::Get();

     // Example: Add response header to all responses
     resp["X-Processed-By"] = "Cripts-Global";
   }

   #include <cripts/Epilogue.hpp>

.. _cripts-global-hooks:

Hooks
=====

Hooks are the main way to interact with ATS. The hooks are the same as the ATS hooks,
but with a few differences. The hooks are all in the global namespace, and the hooks
are all functions. Cripts provides a core set of hooks which are always available,
but they are not required to be used.

Not all ATS hooks are available in Cripts, but the most common ones are. Hooks are
implicitly called if they are defined in the Cript file. The Cript will never explicitly
setup the hooks, as this is done by the ATS process.

Normal Hooks
------------

Let's look at the normal hooks that are available in Cripts. Note here that the name
of the function dictates the underlying ATS hook.

.. _cripts-global-hooks-init:

glb_init()
^^^^^^^^^^

This callback is called when the plugin is loaded. This is where you can setup any
global state that you need, initialize metrics, or perform other one-time setup tasks.

Example:

.. code-block:: cpp

   glb_init()
   {
     CDebug("Initializing global Cript plugin");
     // Setup global metrics, configurations, etc.
   }

.. _cripts-global-hooks-txn-start:

glb_txn_start()
^^^^^^^^^^^^^^^

The ``glb_txn_start()`` hook is called at the beginning of a transaction. This is also
where Cripts will setup other HTTP hooks as necessary. Note that in this hook, the
client request has not yet been read, so you cannot access the request headers.

This hook is useful for initializing per-transaction data or setting up transaction-specific
state.

Example:

.. code-block:: cpp

   glb_txn_start()
   {
     // Initialize transaction data
     txn_data[0] = integer(0); // Request counter
     txn_data[1] = cripts::Time::Local::Now().Epoch();
   }

.. _cripts-global-hooks-read-request:

glb_read_request()
^^^^^^^^^^^^^^^^^^

The ``glb_read_request()`` hook is called after the client request has been read. This
means that you can access the request headers, and the request URL. However, remap rules
have not yet been applied, so the URL may not be the final URL, or even complete.

This is the earliest point where you can examine and modify the client request.

Example:

.. code-block:: cpp

   glb_read_request()
   {
     borrow req = cripts::Client::Request::Get();
     borrow url = cripts::Client::URL::Get();

     // Log all incoming requests
     CDebug("Request: {} {}", req.method, url.path);

     // Add tracking headers
     req["X-Request-ID"] = cripts::UUID::Request::Get();
   }

.. _cripts-global-hooks-pre-remap:

glb_pre_remap()
^^^^^^^^^^^^^^^

The ``glb_pre_remap()`` hook is called just before remap rules are processed. This hook
may not be particularly useful in most Cripts, as remap rules have not yet been applied.
We've added it for completeness and advanced use cases.

.. _cripts-global-hooks-post-remap:

glb_post_remap()
^^^^^^^^^^^^^^^^

The ``glb_post_remap()`` hook is called after remap rules have been applied. This is the
closest equivalent to the ``do_remap()`` hook in remap rules, but operates globally
across all transactions.

At this point, the request URL has been finalized and you can make decisions based on
the final destination.

Example:

.. code-block:: cpp

   glb_post_remap()
   {
     borrow url = cripts::Client::URL::Get();

     // Apply global policies after remap
     if (url.host == "sensitive.example.com") {
       borrow req = cripts::Client::Request::Get();
       req["X-Security-Level"] = "high";
     }
   }

.. _cripts-global-hooks-cache-lookup:

glb_cache_lookup()
^^^^^^^^^^^^^^^^^^

The ``glb_cache_lookup()`` hook is called when a cache lookup is performed. This allows
you to take action based on whether the content was found in cache or not, and to
modify cache behavior globally.

This is equivalent to the ``do_cache_lookup()`` hook in remap rules.

Example:

.. code-block:: cpp

   glb_cache_lookup()
   {
     auto status = transaction.LookupStatus();
     CDebug("Cache lookup status: {}", static_cast<int>(status));

     // Global cache policy decisions can be made here
   }

.. _cripts-global-hooks-send-request:

glb_send_request()
^^^^^^^^^^^^^^^^^^

The ``glb_send_request()`` hook is called when the request is ready to be sent to the
origin server. This is the main hook for globally modifying requests to origin servers.

This is equivalent to the ``do_send_request()`` hook in remap rules.

Example:

.. code-block:: cpp

   glb_send_request()
   {
     borrow req = cripts::Server::Request::Get();

     // Add global origin request headers
     req["X-Forwarded-By"] = "ATS-Cripts";
     req["X-Request-Time"] = cripts::Time::Local::Now().Epoch();
   }

.. _cripts-global-hooks-read-response:

glb_read_response()
^^^^^^^^^^^^^^^^^^^

The ``glb_read_response()`` hook is called when the response is being read from the origin
server. This is the main hook for globally modifying responses from origin servers before
they are cached or sent to clients.

This is equivalent to the ``do_read_response()`` hook in remap rules.

Example:

.. code-block:: cpp

   glb_read_response()
   {
     borrow resp = cripts::Server::Response::Get();

     // Global response processing
     if (resp.status == 500) {
       CDebug("Server error detected from origin");
       // Could implement global error handling here
     }
   }

.. _cripts-global-hooks-send-response:

glb_send_response()
^^^^^^^^^^^^^^^^^^^

The ``glb_send_response()`` hook is called when the response is ready to be sent to the
client. This allows you to make final modifications to the response headers and implement
global response policies.

This is equivalent to the ``do_send_response()`` hook in remap rules.

Example:

.. code-block:: cpp

   glb_send_response()
   {
     borrow resp = cripts::Client::Response::Get();

     // Add global response headers
     resp["X-Served-By"] = "ATS-Cripts-Global";
     resp["X-Response-Time"] = cripts::Time::Local::Now().Epoch();

     // Global security headers
     resp["X-Content-Type-Options"] = "nosniff";
     resp["X-Frame-Options"] = "DENY";
   }

.. _cripts-global-hooks-txn-close:

glb_txn_close()
^^^^^^^^^^^^^^^

The ``glb_txn_close()`` hook is called when the transaction is closed. This is useful
for cleanup, logging, and metrics collection.

This is equivalent to the ``do_txn_close()`` hook in remap rules.

Example:

.. code-block:: cpp

   glb_txn_close()
   {
     // Log transaction completion
     auto start_time = AsInteger(txn_data[1]);
     auto duration = cripts::Time::Local::Now().Epoch() - start_time;

     CDebug("Transaction completed in {} seconds", duration);

     // Update global metrics
     // Cleanup transaction-specific resources
   }

Hook Execution Order
====================

The global hooks are executed in the following order during a typical transaction:

1. ``glb_init()`` - Called once when plugin loads
2. ``glb_txn_start()`` - Transaction begins
3. ``glb_read_request()`` - Client request is read
4. ``glb_pre_remap()`` - Before remap processing
5. ``glb_post_remap()`` - After remap processing
6. ``glb_cache_lookup()`` - Cache lookup performed
7. ``glb_send_request()`` - Request sent to origin (if cache miss)
8. ``glb_read_response()`` - Response read from origin
9. ``glb_send_response()`` - Response sent to client
10. ``glb_txn_close()`` - Transaction cleanup

.. note::
    Not all hooks are called for every transaction. For example, ``glb_send_request()``
    and ``glb_read_response()`` are only called on cache misses when ATS needs to
    contact the origin server.

Best Practices
==============

When writing global Cripts plugins:

1. **Performance Considerations**: Global hooks affect all traffic, so keep processing
   lightweight and efficient.

2. **Conditional Logic**: Use conditional logic to apply policies only where needed:

   .. code-block:: cpp

      glb_read_request()
      {
        borrow url = cripts::Client::URL::Get();

        // Only process specific hosts
        if (url.host.find("api.") == 0) {
          // API-specific processing
        }
      }

3. **Error Handling**: Implement proper error handling to avoid affecting other traffic:

   .. code-block:: cpp

      glb_send_response()
      {
        if (something_went_wrong) {
          CDebug("Error in global response processing");
          // Don't let errors affect the response
        } else {
        // Continue with normal processing
          borrow resp = cripts::Client::Response::Get();
          resp["X-Processed-By"] = "Cripts-Global";
        }
      }

4. **Metrics and Monitoring**: Use global plugins for comprehensive monitoring:

   .. code-block:: cpp

      glb_init()
      {
        instance.metrics[0] = cripts::Metrics::Counter::Create("global.requests.total");
        instance.metrics[1] = cripts::Metrics::Counter::Create("global.errors.total");
      }

5. **Resource Management**: Clean up any resources in ``glb_txn_close()`` to prevent leaks.
