1/**********
2This library is free software; you can redistribute it and/or modify it under
3the terms of the GNU Lesser General Public License as published by the
4Free Software Foundation; either version 3 of the License, or (at your
5option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
6
7This library is distributed in the hope that it will be useful, but WITHOUT
8ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
9FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
10more details.
11
12You should have received a copy of the GNU Lesser General Public License
13along with this library; if not, write to the Free Software Foundation, Inc.,
1451 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
39class GenericMediaServer: public Medium {
40public:
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
70protected:
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
85public: // 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
132protected:
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
154protected:
155 friend class ClientConnection;
156 friend class ClientSession;
157 friend class ServerMediaSessionIterator;
158 int fServerSocket;
159 Port fServerPort;
160 unsigned fReclamationSeconds;
161
162private:
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
171class UserAuthenticationDatabase {
172public:
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
189protected:
190 HashTable* fTable;
191 char* fRealm;
192 Boolean fPasswordsAreMD5;
193};
194
195#endif
196