Updated license
[anna.git] / include / anna / diameter.comm / Session.hpp
1 // ANNA - Anna is Not Nothingness Anymore
2 //
3 // (c) Copyright 2005-2014 Eduardo Ramos Testillano & Francisco Ruiz Rayo
4 //
5 // https://bitbucket.org/testillano/anna
6 //
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions
9 // are met:
10 //
11 //     * Redistributions of source code must retain the above copyright
12 // notice, this list of conditions and the following disclaimer.
13 //     * Redistributions in binary form must reproduce the above
14 // copyright notice, this list of conditions and the following disclaimer
15 // in the documentation and/or other materials provided with the
16 // distribution.
17 //     * Neither the name of Google Inc. nor the names of its
18 // contributors may be used to endorse or promote products derived from
19 // this software without specific prior written permission.
20 //
21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 //
33 // Authors: eduardo.ramos.testillano@gmail.com
34 //          cisco.tierra@gmail.com
35
36
37 #ifndef anna_diameter_comm_Session_hpp
38 #define anna_diameter_comm_Session_hpp
39
40
41 // STL
42 #include <string>
43
44 #include <anna/core/util/SortedVector.hpp>
45 #include <anna/core/util/Millisecond.hpp>
46 #include <anna/core/RuntimeException.hpp>
47 #include <anna/timex/Timer.hpp>
48
49 #include <anna/diameter/defines.hpp>
50 #include <anna/diameter.comm/ClassCode.hpp>
51 #include <anna/diameter.comm/Message.hpp>
52 #include <anna/diameter.comm/Timer.hpp>
53
54
55 namespace anna {
56 class DataBlock;
57 namespace timex {
58 class Engine;
59 }
60 }
61
62
63 namespace anna {
64
65 namespace diameter {
66
67 namespace stack {
68 class Engine;
69 }
70
71 namespace comm {
72
73 class Timer;
74 class Engine;
75 class Response;
76
77
78
79 /**
80    Modela la conexion realizada contra un servidor diameter.
81 */
82 class Session : public anna::timex::Timer {
83
84 public:
85
86   Session(const char *className, const char *timerName);
87   //virtual ~Session();
88
89   /**
90    * Default timeout for application message requests over the session.
91    */
92   static const anna::Millisecond DefaultTimeout;
93
94   /**
95    * Default diameter port
96    */
97   static const int DefaultPort;
98
99
100
101
102   /**
103      Session states
104      \see diameter::comm::Session::getState
105   */
106   struct State {
107     enum _v {
108       /* client + server */ Closed, /**< Closed */
109       /* client          */ WaitingBind, /**< Connection confirmation pending*/
110       /* client + server */ Bound, /**< Connection done included level application */
111       /* client          */ Failover, /**< Last DWR timed out */
112       /*          server */ Suspect, /**< Inactivity detected on session */
113
114 // .......
115
116
117       // Cierre de iniciativa local:
118       // 1. Envio DPR al PCRF y me pongo en estado 'WaitingDPA'. En este estado no habrá keep-alive DWR/DWA.
119       // 2. No dejo pasar nuevas peticiones (BLOCK-SEND).
120       // 3. Cierro al recibir el DPA.
121       // 4. Si expira el DPA, tambien cierro.
122       WaitingDPA, /**< After requesting DPR to server, send is blocked over the session: when DPA arrives (or answer expires) the session is closed */
123
124       // Cierre de iniciativa remota:
125       // 1. Recibo DPR del PCRF y me pongo en estado 'Disconnecting'. En este estado no habrá keep-alive DWR/DWA.
126       // 2. No dejo pasar nuevas peticiones (BLOCK-SEND).
127       // 3. Espero cursar las peticiones pendientes (a más tardar, será una expiracion Tx desde la recepcion del DPR).
128       // 4. Envio DPA y activo un temporizador de cierre local (2*Tx) como proteccion (por si el servidor no cierra).
129       Disconnecting, /**< After receiving DPR from server, send is blocked over the session: when no pending requests, DPA is sent to the server who will close connection */
130
131       // Cierre abrupto con/sin espera de pendings:
132       Closing /** Planned local disconnection without DPR; send is blocked over the session. Immediate unbind is used when IgnorePendings behaviour is configured */
133     };
134   };
135
136   /**
137      Session state.
138      \return State for this session.
139   */
140   State::_v getState() const throw() { return a_state; }
141
142   /**
143    * Defines behaviour on 'Disconnecting' state, about #unbind action (ignore/wait pending answers)
144    */
145   struct OnDisconnect { enum _v { IgnorePendings, WaitPendings /* graceful unbind */ }; };
146
147   /**
148    * Sets behaviour on 'Disconnecting' state, about #unbind action (ignore/wait pending answers).
149    * \param onDisconnect Behaviour on 'Disconnecting' state, about #unbind action (ignore/wait pending answers).
150    */
151   void setOnDisconnect(const OnDisconnect::_v onDisconnect)  throw() { a_onDisconnect = onDisconnect; }
152
153   /**
154    * Returns behaviour on 'Disconnecting' state, about #unbind action (ignore/wait pending answers).
155    * \return behaviour on 'Disconnecting' state, about #unbind action (ignore/wait pending answers).
156    */
157   OnDisconnect::_v getOnDisconnect() const throw() { return a_onDisconnect; }
158
159
160
161   /**
162      Diameter server address, ip or hostname (remote for client-session, local for server-session).
163      \return Diameter server address (remote for client-session, local for server-session).
164   */
165   virtual const std::string& getAddress() const throw() = 0;
166
167   /**
168      Diameter server listen port (remote for client-session, local for server-session).
169      \return Diameter server listen port (remote for client-session, local for server-session).
170   */
171   virtual int getPort() const throw() = 0;
172
173   /**
174      Socket id.
175      \return Socket id.
176   */
177   int getSocketId() const throw() { return a_socketId; }
178
179   /**
180      Returns the next hop-by-hop which will be used over the diameter session to send a message
181      It is recommended to fix the message with this value (or store along with the message),
182      for application context identification purposes
183   */
184   const HopByHop & getNextHopByHop() const throw() { return a_nextHopByHop; }
185
186   /** Returns the next end-to-end which will be used over the diameter session to send a message
187       It is recommended to fix the message with this value (or store along with the message),
188       for application context identification purposes
189   */
190   const EndToEnd & getNextEndToEnd() const throw() { return a_nextEndToEnd; }
191
192
193   /**
194      Set timeout to consider failed a request.
195      \param v Requests class code.
196      \param millisecond Milliseconds wait before considering the requests failed.
197
198      Timers are internally managed and automatically activated.
199   */
200   void setClassCodeTimeout(const ClassCode::_v v, const anna::Millisecond & millisecond) throw() { a_timeouts [v] = millisecond;  }
201
202   /**
203    * Timeout configured for class code \em v requests.
204    * \return Timeout configured for class code \em v requests.
205    */
206   anna::Millisecond getClassCodeTimeout(const ClassCode::_v v) const throw() { return a_timeouts [v]; }
207
208
209   /**
210      Returns \em true when diameter server is connected (application level) \em false in other case.
211      \return \em true when diameter server is connected (application level) \em false in other case.
212   */
213   bool isBound() const throw() { return a_state == State::Bound; }
214
215
216 // Envia el mensaje recibido como parametro al servidor con el que estamos conectados mediante esta
217 // sesion diameter. Dicho mensaje puede ser una peticion o una respuesta (no temporizada).
218 //
219 // En caso de enviar una peticion se activara automaticamente un temporizador. Si este llegara a caducar
220 // se cancelara la busqueda y se invocara al metodo Session::eventResponse indicado que se ha producido
221 // un error de temporización. La duracion del temporizador sera la establecida por
222 // diameter::comm::TimerManager::setTimeout o el valor defecto.
223 //
224 // \param message Mensaje a enviar al servidor diameter con el que estamos conectados.
225 // @return Diameter response reference asociated to a request. NULL if answer sent.
226 // \warning Solo se podra hacer uso de este metodo cuando el metodo #isBound devuelva \em true.
227   virtual const Response* send(const Message* message) throw(anna::RuntimeException) = 0;
228   const Response* send(const Message& message) throw(anna::RuntimeException) { return send(&message); }
229
230 // Desconecta del extremo remoto
231 // Se notifica la terminación de cada una de las peticiones pendientes invocando al método Session::eventResponse
232 // \warning Después de invocar a este método habría que volver a iniciar una sesion.
233   virtual bool unbind(bool forceDisconnect /* se usa en timer, para el actionTimer del tipo SessionUnbind, etc. */ = false) throw(anna::RuntimeException) = 0;
234   // returns true if done at call time (no pendings or ignore pendings, except Disconnecting state by mean DPR/DPA)
235
236
237   /**
238      Gets the timestamp for last incoming activity over the session.
239
240      @return Last incoming activity timestamp.
241   */
242   const anna::Millisecond & getLastIncomingActivityTime() const throw() { return a_lastIncomingActivityTime; }
243
244   /**
245      Gets the timestamp for last outgoing activity over the session.
246
247      @return Last outgoing activity timestamp.
248   */
249   const anna::Millisecond & getLastOutgoingActivityTime() const throw() { return a_lastOutgoingActivityTime; }
250
251   /**
252      Gets the number of requests messages over-the-air.
253
254      @return OTA messages.
255   */
256   int getOTARequests() const throw() { return a_responses.size(); }
257
258
259   /**
260      Returns idle state (no pending answers).
261
262      @return Idle state.
263   */
264   bool idle() const throw() { return (getOTARequests() == 0); }
265
266
267   /**
268      In order to avoid message burst during failover procedures (alternate server forwardings done by application),
269      orphan request notification could be deferred to expiration event. This is the default behaviour, however if
270      need to notice quickly the transport failure for such requets, 'false' should be established. Result code of
271      'OrphanTimeout' will be configured at response instance.
272
273      This only applies to client-sessions because is not usual to send request from server sessions and there wont
274      be impact notifying orphan requests at the moment of the transport failure.
275
276      @defer Delayed notification for orphan request due to transport failures
277   */
278   void notifyOrphansOnExpiration(bool defer = true) throw() { a_notifyOrphansOnExpiration = defer; }
279
280   /**
281      Class string representation
282      \return String with relevant information for this instance.
283   */
284   virtual std::string asString() const throw();
285
286
287   /**
288      Class xml representation
289      \param parent Parent XML node on which hold this instance information.
290      \return XML document with relevant information for this instance.
291   */
292   virtual anna::xml::Node* asXML(anna::xml::Node* parent) const throw();
293
294 protected:
295
296   // Auxiliary messages:
297   Message a_dpr;
298
299   // Internal, traces, etc.
300   const char *a_className;
301
302   // Main session attributes
303   int a_socketId; // multiple connection functionality
304   State::_v a_state;
305   OnDisconnect::_v a_onDisconnect;
306   Engine *a_engine;
307   anna::diameter::comm::Timer *a_actionTimer;
308
309   // Sequencing
310   HopByHop a_nextHopByHop;
311   EndToEnd a_nextEndToEnd;
312   virtual void initialize() throw();
313   void initializeSequences() throw(); // debe invocarse despues de haber asignado el a_parent
314   void generateNextSequences() throw() { a_nextHopByHop++; a_nextEndToEnd++; }
315
316   // Context Responses
317   struct SortById {
318     static HopByHop value(const Response*) throw();
319   };
320   typedef anna::SortedVector <Response, SortById, HopByHop> response_container;
321   typedef response_container::iterator response_iterator;
322   typedef response_container::const_iterator const_response_iterator;
323   response_container a_responses;
324   bool a_notifyOrphansOnExpiration;
325
326   void response_add(Response* response) throw();
327   void response_erase(Response* response) throw();
328   Response* response_find(const HopByHop hopByHop) throw(anna::RuntimeException);
329
330   response_iterator response_begin() throw() { return a_responses.begin(); }
331   response_iterator response_end() throw() { return a_responses.end(); }
332   static Response* response(response_iterator ii) throw() { return response_container::data(ii); }
333
334   const_response_iterator response_begin() const throw() { return a_responses.begin(); }
335   const_response_iterator response_end() const throw() { return a_responses.end(); }
336   static const Response* response(const_response_iterator ii) throw() { return response_container::data(ii); }
337
338   // Activity
339   anna::timex::Engine* a_timeController;
340   anna::Millisecond a_lastIncomingActivityTime;   // last unix timestamp (in milliseconds) when message reception was managed over the session
341   anna::Millisecond a_lastOutgoingActivityTime;   // last unix timestamp (in milliseconds) when message sending was managed over the session
342   virtual void updateIncomingActivityTime() throw();
343   virtual void updateOutgoingActivityTime() throw();
344
345   // Self-timer expiration handler
346   virtual void expire(anna::timex::Engine *timeController) throw(anna::RuntimeException) {;}
347
348   // Timming:
349   anna::Millisecond a_timeouts [ClassCode::Max];
350
351   // Handlers:
352   /**
353      Handler about event break connection over the session.
354
355      When notified, ANNA.diameter.comm generates an diameter::comm::ClientSession::eventResponse for every request with pending answers.
356   */
357   virtual void eventPeerShutdown() throw() = 0;
358
359   /**
360      Handler for diameter session responses
361
362      \param response Answer data block object for corresponding diameter request
363   */
364   virtual void eventResponse(const Response& response) throw(anna::RuntimeException) = 0;
365
366   /**
367      Handler for diameter session requests
368
369      \param request Request container object for corresponding diameter reception
370   */
371   virtual void eventRequest(const anna::DataBlock& request) throw(anna::RuntimeException) = 0;
372   //void eventRequest(const Message& request) throw(anna::RuntimeException);
373
374
375   /**
376      Handler for diameter session responses out of context
377
378      \param response Answer data block object without context match
379   */
380   virtual void eventUnknownResponse(const anna::DataBlock& response) throw(anna::RuntimeException) = 0;
381
382
383
384   /**
385   * Handlers
386   */
387   virtual void receive(const anna::comm::Message& message) throw(anna::RuntimeException) = 0;
388 //PROTOCOL ERRORS
389 //The errors at the protocol level are reported in response messages that contain the \93E\94 bit and the error code in the AVP result-Code (various errors having been produced only the first one of them is reported). Examples of these errors are:
390 //An unrecognized AVP with the \93M\94 bit is received.
391 //An AVP is received with an unrecognized value (in the AVP failed-AVP indicates the attribute that the error caused).
392 //An mandatory AVP is not received.
393 //Length of operation incorrect.
394 //Length of AVP incorrect.
395 //Coding of some AVP incorrect.
396 //Erroneous inclusion of parameters.
397
398
399
400
401
402   virtual void finalize() throw(); // invoked from ClientSessionReceiver::eventBreakConnection()
403
404
405   virtual void expireResponse(Response*) throw();
406   void sendDPA() throw(anna::RuntimeException);
407   void activateActionTimer(const anna::diameter::comm::Timer::Type::_v type) throw();
408   void cancelActionTimer() throw();
409   void activateTimer() throw(); // Session timer
410   void cancelTimer() throw(); // Session timer
411   virtual void timerStopped() throw() {;}
412   virtual void timerStarted() throw() {;}
413
414
415   virtual void setState(State::_v state) throw();
416
417   //anna::diameter::comm::Timer *getActionTimer() const { return (a_actionTimer); }
418
419
420   // helpers
421   static const char* asText(const State::_v) throw();
422   static const char* asText(const OnDisconnect::_v) throw();
423
424
425   friend class anna::diameter::comm::Timer;
426   friend class Engine;
427   friend class Response;
428 };
429
430 }
431 }
432 }
433
434 #endif
435