Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
InterceptorChain.h
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#ifndef _Stroika_Framework_WebServer_InterceptorChain_h_
5#define _Stroika_Framework_WebServer_InterceptorChain_h_ 1
6
7#include "Stroika/Frameworks/StroikaPreComp.h"
8
9#include "Stroika/Foundation/Common/Property.h"
10#include "Stroika/Foundation/Containers/Sequence.h"
12
13#include "Stroika/Frameworks/WebServer/Interceptor.h"
14
15/*
16 * \note Code-Status: <a href="Code-Status.md#Beta">Beta</a>
17 */
18
20
21 using namespace Stroika::Foundation;
22
24
25 /**
26 * The InterceptorChain hands a message to each interceptor in its list in order, from front to back.
27 *
28 * Typically early interceptors quickly check and process results and then throw/abort if there is a problem (so auth
29 * would be handled early in the interceptor chain)
30 *
31 * And typically - your message router, or final handlers that return data - would be at the end of the interceptor chain.
32 *
33 * Note - exceptions are handled in the reverse order - passed backwards through the chain.
34 *
35 * The InterceptorChain is internally synchronized. But - it assumes each Interceptor its given
36 * is itself internally synchronized.
37 *
38 * So we can copy an Interceptor chain quickly and cheaply without (external) locks. Updating it
39 * maybe slower, but also can be done freely without locks.
40 *
41 * InterceptorChains are 'by value' objects, so updating one in a Connection object - for example - doesn't affect
42 * other connections (update the one in the ConnectionManager for use in future connections).
43 *
44 * \note Use the ConnectionManager to more easily manage the Interceptors list
45 *
46 * \note Inspired by, but quite different from
47 * @see https://cxf.apache.org/javadoc/latest/org/apache/cxf/phase/PhaseInterceptorChain.html
48 *
49 * \note A draft of the implementation to clarify
50 * \code
51 * void InterceptorChain::HandleMessage (Message& m)
52 * {
53 * size_t i = 0;
54 * for (; i < fInterceptors_.size (); ++i) {
55 * try {
56 * fInterceptors_[i].HandleMessage (m);
57 * }
58 * catch (...) {
59 * exception_ptr e = current_exception ();
60 * do {
61 * fInterceptors_[i].HandleFault (m, e);
62 * } while (i-- != 0);
63 * Execution::ReThrow ();
64 * }
65 * }
66 * for (; i > 0; --i) {
67 * fInterceptors_[i-1].CompleteNormally (m);
68 * }
69 * }
70 * \endcode
71 *
72 * \note \em Thread-Safety <a href="Thread-Safety.md#C++-Standard-Thread-Safety-For-Envelope-Letter-Internally-Synchronized">C++-Standard-Thread-Safety-For-Envelope-Letter-Internally-Synchronized</a>
73 * But note that HandleMessage() is a const method, so it can safely be called from any number of threads
74 * simultaneously.
75 */
77 protected:
78 class _IRep;
79
80 public:
81 /**
82 */
83 InterceptorChain (const Sequence<Interceptor>& interceptors = {});
86
87 protected:
88 InterceptorChain (const shared_ptr<_IRep>& rep);
89
90 public:
91 nonvirtual InterceptorChain& operator= (const InterceptorChain&) = default;
92
93 public:
95
96 public:
97 /**
98 * The 'before' interceptor must be in the list. The new interceptor is added just before it.
99 */
100 nonvirtual void AddBefore (const Interceptor& interceptor2Add, const Interceptor& before);
101
102 public:
103 /**
104 * The 'after' interceptor must be in the list. The new interceptor is added just after it.
105 */
106 nonvirtual void AddAfter (const Interceptor& interceptor2Add, const Interceptor& after);
107
108 public:
109 /**
110 *
111 * HandleMessage() sends a HandleMessage() call to each interceptor in turn.
112 * For each interceptor in the chain, if it succeeds (doesn't throw) - it will THEN get a CompleteNormally () message.
113 * If it faulted (or a fault occurred later in the chain) - then it will INSTEAD get a HandleFault () message.
114 * BUT - no matter what - each interceptor called with HandleMessage () with EITHER:
115 * o Get its HandleFault() called
116 * o OR get its HandleComplete() called
117 * but not both and not neither.
118 *
119 * Send the given message to each interceptor in the chain. Each interceptor from start to end of the sequence
120 * is sent the HandleMessage () call.
121 *
122 * Interceptors must not throw during the HandleFault. InterceptorChain::HandleMessage() simply rethrows the original
123 * fault (exception) that triggered the unwind of the message.
124 */
125 nonvirtual void HandleMessage (Message& m) const;
126
127 public:
128 /**
129 * Two interceptors chains are equal if they have the same addresses, or are copies of one another by copy constructor or assignment.
130 */
131 nonvirtual bool operator== (const InterceptorChain& rhs) const;
132
133 public:
134 nonvirtual Characters::String ToString () const;
135
136 private:
138
139 private:
140 struct Rep_;
141 };
142
143 /**
144 */
145 class InterceptorChain::_IRep {
146 public:
147 virtual ~_IRep () = default;
148
149 virtual Sequence<Interceptor> GetInterceptors () const = 0;
150
151 // note - this is const and returns a new _IRep - so that the actual rep can be immutable.
152 virtual shared_ptr<_IRep> SetInterceptors (const Sequence<Interceptor>& interceptors) const = 0;
153
154 // Intercepts a message, and handles exception logic - distributing to interceptors already called
155 virtual void HandleMessage (Message& m) const = 0;
156 };
157
158}
159
160/*
161 ********************************************************************************
162 ***************************** Implementation Details ***************************
163 ********************************************************************************
164 */
165#include "InterceptorChain.inl"
166
167#endif /*_Stroika_Framework_WebServer_Interceptor_h_*/
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
A generalization of a vector: a container whose elements are keyed by the natural numbers.
Definition Sequence.h:187
Wrap any object with Synchronized<> and it can be used similarly to the base type,...
nonvirtual void AddBefore(const Interceptor &interceptor2Add, const Interceptor &before)
nonvirtual void AddAfter(const Interceptor &interceptor2Add, const Interceptor &after)
nonvirtual void HandleMessage(Message &m) const
nonvirtual bool operator==(const InterceptorChain &rhs) const