1 | /********** |
2 | This library is free software; you can redistribute it and/or modify it under |
3 | the terms of the GNU Lesser General Public License as published by the |
4 | Free Software Foundation; either version 3 of the License, or (at your |
5 | option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.) |
6 | |
7 | This library is distributed in the hope that it will be useful, but WITHOUT |
8 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
9 | FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for |
10 | more details. |
11 | |
12 | You should have received a copy of the GNU Lesser General Public License |
13 | along with this library; if not, write to the Free Software Foundation, Inc., |
14 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
15 | **********/ |
16 | // "liveMedia" |
17 | // Copyright (c) 1996-2020 Live Networks, Inc. All rights reserved. |
18 | // A generic media server class, used to implement a RTSP server, and any other server that uses |
19 | // "ServerMediaSession" objects to describe media to be served. |
20 | // C++ header |
21 | |
22 | #ifndef _GENERIC_MEDIA_SERVER_HH |
23 | #define _GENERIC_MEDIA_SERVER_HH |
24 | |
25 | #ifndef _MEDIA_HH |
26 | #include "Media.hh" |
27 | #endif |
28 | #ifndef _SERVER_MEDIA_SESSION_HH |
29 | #include "ServerMediaSession.hh" |
30 | #endif |
31 | |
32 | #ifndef REQUEST_BUFFER_SIZE |
33 | #define REQUEST_BUFFER_SIZE 20000 // for incoming requests |
34 | #endif |
35 | #ifndef RESPONSE_BUFFER_SIZE |
36 | #define RESPONSE_BUFFER_SIZE 20000 |
37 | #endif |
38 | |
39 | class GenericMediaServer: public Medium { |
40 | public: |
41 | void addServerMediaSession(ServerMediaSession* serverMediaSession); |
42 | |
43 | virtual ServerMediaSession* |
44 | lookupServerMediaSession(char const* streamName, Boolean isFirstLookupInSession = True); |
45 | |
46 | void removeServerMediaSession(ServerMediaSession* serverMediaSession); |
47 | // Removes the "ServerMediaSession" object from our lookup table, so it will no longer be accessible by new clients. |
48 | // (However, any *existing* client sessions that use this "ServerMediaSession" object will continue streaming. |
49 | // The "ServerMediaSession" object will not get deleted until all of these client sessions have closed.) |
50 | // (To both delete the "ServerMediaSession" object *and* close all client sessions that use it, |
51 | // call "deleteServerMediaSession(serverMediaSession)" instead.) |
52 | virtual void removeServerMediaSession(char const* streamName); |
53 | // ditto |
54 | |
55 | void closeAllClientSessionsForServerMediaSession(ServerMediaSession* serverMediaSession); |
56 | // Closes (from the server) all client sessions that are currently using this "ServerMediaSession" object. |
57 | // Note, however, that the "ServerMediaSession" object remains accessible by new clients. |
58 | virtual void closeAllClientSessionsForServerMediaSession(char const* streamName); |
59 | // ditto |
60 | |
61 | void deleteServerMediaSession(ServerMediaSession* serverMediaSession); |
62 | // Equivalent to: |
63 | // "closeAllClientSessionsForServerMediaSession(serverMediaSession); removeServerMediaSession(serverMediaSession);" |
64 | virtual void deleteServerMediaSession(char const* streamName); |
65 | // Equivalent to: |
66 | // "closeAllClientSessionsForServerMediaSession(streamName); removeServerMediaSession(streamName); |
67 | |
68 | unsigned numClientSessions() const { return fClientSessions->numEntries(); } |
69 | |
70 | protected: |
71 | GenericMediaServer(UsageEnvironment& env, int ourSocket, Port ourPort, |
72 | unsigned reclamationSeconds); |
73 | // If "reclamationSeconds" > 0, then the "ClientSession" state for each client will get |
74 | // reclaimed if no activity from the client is detected in at least "reclamationSeconds". |
75 | // we're an abstract base class |
76 | virtual ~GenericMediaServer(); |
77 | void cleanup(); // MUST be called in the destructor of any subclass of us |
78 | |
79 | static int setUpOurSocket(UsageEnvironment& env, Port& ourPort); |
80 | |
81 | static void incomingConnectionHandler(void*, int /*mask*/); |
82 | void incomingConnectionHandler(); |
83 | void incomingConnectionHandlerOnSocket(int serverSocket); |
84 | |
85 | public: // should be protected, but some old compilers complain otherwise |
86 | // The state of a TCP connection used by a client: |
87 | class ClientConnection { |
88 | protected: |
89 | ClientConnection(GenericMediaServer& ourServer, int clientSocket, struct sockaddr_in clientAddr); |
90 | virtual ~ClientConnection(); |
91 | |
92 | UsageEnvironment& envir() { return fOurServer.envir(); } |
93 | void closeSockets(); |
94 | |
95 | static void incomingRequestHandler(void*, int /*mask*/); |
96 | void incomingRequestHandler(); |
97 | virtual void handleRequestBytes(int newBytesRead) = 0; |
98 | void resetRequestBuffer(); |
99 | |
100 | protected: |
101 | friend class GenericMediaServer; |
102 | friend class ClientSession; |
103 | friend class RTSPServer; // needed to make some broken Windows compilers work; remove this in the future when we end support for Windows |
104 | GenericMediaServer& fOurServer; |
105 | int fOurSocket; |
106 | struct sockaddr_in fClientAddr; |
107 | unsigned char fRequestBuffer[REQUEST_BUFFER_SIZE]; |
108 | unsigned char fResponseBuffer[RESPONSE_BUFFER_SIZE]; |
109 | unsigned fRequestBytesAlreadySeen, fRequestBufferBytesLeft; |
110 | }; |
111 | |
112 | // The state of an individual client session (using one or more sequential TCP connections) handled by a server: |
113 | class ClientSession { |
114 | protected: |
115 | ClientSession(GenericMediaServer& ourServer, u_int32_t sessionId); |
116 | virtual ~ClientSession(); |
117 | |
118 | UsageEnvironment& envir() { return fOurServer.envir(); } |
119 | void noteLiveness(); |
120 | static void noteClientLiveness(ClientSession* clientSession); |
121 | static void livenessTimeoutTask(ClientSession* clientSession); |
122 | |
123 | protected: |
124 | friend class GenericMediaServer; |
125 | friend class ClientConnection; |
126 | GenericMediaServer& fOurServer; |
127 | u_int32_t fOurSessionId; |
128 | ServerMediaSession* fOurServerMediaSession; |
129 | TaskToken fLivenessCheckTask; |
130 | }; |
131 | |
132 | protected: |
133 | virtual ClientConnection* createNewClientConnection(int clientSocket, struct sockaddr_in clientAddr) = 0; |
134 | virtual ClientSession* createNewClientSession(u_int32_t sessionId) = 0; |
135 | |
136 | ClientSession* createNewClientSessionWithId(); |
137 | // Generates a new (unused) random session id, and calls the "createNewClientSession()" |
138 | // virtual function with this session id as parameter. |
139 | |
140 | // Lookup a "ClientSession" object by sessionId (integer, and string): |
141 | ClientSession* lookupClientSession(u_int32_t sessionId); |
142 | ClientSession* lookupClientSession(char const* sessionIdStr); |
143 | |
144 | // An iterator over our "ServerMediaSession" objects: |
145 | class ServerMediaSessionIterator { |
146 | public: |
147 | ServerMediaSessionIterator(GenericMediaServer& server); |
148 | virtual ~ServerMediaSessionIterator(); |
149 | ServerMediaSession* next(); |
150 | private: |
151 | HashTable::Iterator* fOurIterator; |
152 | }; |
153 | |
154 | protected: |
155 | friend class ClientConnection; |
156 | friend class ClientSession; |
157 | friend class ServerMediaSessionIterator; |
158 | int fServerSocket; |
159 | Port fServerPort; |
160 | unsigned fReclamationSeconds; |
161 | |
162 | private: |
163 | HashTable* fServerMediaSessions; // maps 'stream name' strings to "ServerMediaSession" objects |
164 | HashTable* fClientConnections; // the "ClientConnection" objects that we're using |
165 | HashTable* fClientSessions; // maps 'session id' strings to "ClientSession" objects |
166 | u_int32_t fPreviousClientSessionId; |
167 | }; |
168 | |
169 | // A data structure used for optional user/password authentication: |
170 | |
171 | class UserAuthenticationDatabase { |
172 | public: |
173 | UserAuthenticationDatabase(char const* realm = NULL, |
174 | Boolean passwordsAreMD5 = False); |
175 | // If "passwordsAreMD5" is True, then each password stored into, or removed from, |
176 | // the database is actually the value computed |
177 | // by md5(<username>:<realm>:<actual-password>) |
178 | virtual ~UserAuthenticationDatabase(); |
179 | |
180 | virtual void addUserRecord(char const* username, char const* password); |
181 | virtual void removeUserRecord(char const* username); |
182 | |
183 | virtual char const* lookupPassword(char const* username); |
184 | // returns NULL if the user name was not present |
185 | |
186 | char const* realm() { return fRealm; } |
187 | Boolean passwordsAreMD5() { return fPasswordsAreMD5; } |
188 | |
189 | protected: |
190 | HashTable* fTable; |
191 | char* fRealm; |
192 | Boolean fPasswordsAreMD5; |
193 | }; |
194 | |
195 | #endif |
196 | |