1 /*
2 * Copyright 2011 The WebRTC Project Authors. All rights reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11 #include <stdio.h>
12 #include <stdlib.h>
13 #if defined(WEBRTC_POSIX)
14 #include <sys/select.h>
15 #endif
16 #include <time.h>
17
18 #include <string>
19 #include <vector>
20
21 #include "absl/flags/flag.h"
22 #include "absl/flags/parse.h"
23 #include "absl/flags/usage.h"
24 #include "examples/peerconnection/server/data_socket.h"
25 #include "examples/peerconnection/server/peer_channel.h"
26 #include "rtc_base/checks.h"
27 #include "system_wrappers/include/field_trial.h"
28 #include "test/field_trial.h"
29
30 ABSL_FLAG(
31 std::string,
32 force_fieldtrials,
33 "",
34 "Field trials control experimental features. This flag specifies the field "
35 "trials in effect. E.g. running with "
36 "--force_fieldtrials=WebRTC-FooFeature/Enabled/ "
37 "will assign the group Enabled to field trial WebRTC-FooFeature. Multiple "
38 "trials are separated by \"/\"");
39 ABSL_FLAG(int, port, 8888, "default: 8888");
40
41 static const size_t kMaxConnections = (FD_SETSIZE - 2);
42
HandleBrowserRequest(DataSocket * ds,bool * quit)43 void HandleBrowserRequest(DataSocket* ds, bool* quit) {
44 RTC_DCHECK(ds && ds->valid());
45 RTC_DCHECK(quit);
46
47 const std::string& path = ds->request_path();
48
49 *quit = (path.compare("/quit") == 0);
50
51 if (*quit) {
52 ds->Send("200 OK", true, "text/html", "",
53 "<html><body>Quitting...</body></html>");
54 } else if (ds->method() == DataSocket::OPTIONS) {
55 // We'll get this when a browsers do cross-resource-sharing requests.
56 // The headers to allow cross-origin script support will be set inside
57 // Send.
58 ds->Send("200 OK", true, "", "", "");
59 } else {
60 // Here we could write some useful output back to the browser depending on
61 // the path.
62 printf("Received an invalid request: %s\n", ds->request_path().c_str());
63 ds->Send("500 Sorry", true, "text/html", "",
64 "<html><body>Sorry, not yet implemented</body></html>");
65 }
66 }
67
main(int argc,char * argv[])68 int main(int argc, char* argv[]) {
69 absl::SetProgramUsageMessage(
70 "Example usage: ./peerconnection_server --port=8888\n");
71 absl::ParseCommandLine(argc, argv);
72
73 // InitFieldTrialsFromString stores the char*, so the char array must outlive
74 // the application.
75 const std::string force_field_trials = absl::GetFlag(FLAGS_force_fieldtrials);
76 webrtc::field_trial::InitFieldTrialsFromString(force_field_trials.c_str());
77
78 int port = absl::GetFlag(FLAGS_port);
79
80 // Abort if the user specifies a port that is outside the allowed
81 // range [1, 65535].
82 if ((port < 1) || (port > 65535)) {
83 printf("Error: %i is not a valid port.\n", port);
84 return -1;
85 }
86
87 ListeningSocket listener;
88 if (!listener.Create()) {
89 printf("Failed to create server socket\n");
90 return -1;
91 } else if (!listener.Listen(port)) {
92 printf("Failed to listen on server socket\n");
93 return -1;
94 }
95
96 printf("Server listening on port %i\n", port);
97
98 PeerChannel clients;
99 typedef std::vector<DataSocket*> SocketArray;
100 SocketArray sockets;
101 bool quit = false;
102 while (!quit) {
103 fd_set socket_set;
104 FD_ZERO(&socket_set);
105 if (listener.valid())
106 FD_SET(listener.socket(), &socket_set);
107
108 for (SocketArray::iterator i = sockets.begin(); i != sockets.end(); ++i)
109 FD_SET((*i)->socket(), &socket_set);
110
111 struct timeval timeout = {10, 0};
112 if (select(FD_SETSIZE, &socket_set, NULL, NULL, &timeout) == SOCKET_ERROR) {
113 printf("select failed\n");
114 break;
115 }
116
117 for (SocketArray::iterator i = sockets.begin(); i != sockets.end(); ++i) {
118 DataSocket* s = *i;
119 bool socket_done = true;
120 if (FD_ISSET(s->socket(), &socket_set)) {
121 if (s->OnDataAvailable(&socket_done) && s->request_received()) {
122 ChannelMember* member = clients.Lookup(s);
123 if (member || PeerChannel::IsPeerConnection(s)) {
124 if (!member) {
125 if (s->PathEquals("/sign_in")) {
126 clients.AddMember(s);
127 } else {
128 printf("No member found for: %s\n", s->request_path().c_str());
129 s->Send("500 Error", true, "text/plain", "",
130 "Peer most likely gone.");
131 }
132 } else if (member->is_wait_request(s)) {
133 // no need to do anything.
134 socket_done = false;
135 } else {
136 ChannelMember* target = clients.IsTargetedRequest(s);
137 if (target) {
138 member->ForwardRequestToPeer(s, target);
139 } else if (s->PathEquals("/sign_out")) {
140 s->Send("200 OK", true, "text/plain", "", "");
141 } else {
142 printf("Couldn't find target for request: %s\n",
143 s->request_path().c_str());
144 s->Send("500 Error", true, "text/plain", "",
145 "Peer most likely gone.");
146 }
147 }
148 } else {
149 HandleBrowserRequest(s, &quit);
150 if (quit) {
151 printf("Quitting...\n");
152 FD_CLR(listener.socket(), &socket_set);
153 listener.Close();
154 clients.CloseAll();
155 }
156 }
157 }
158 } else {
159 socket_done = false;
160 }
161
162 if (socket_done) {
163 printf("Disconnecting socket\n");
164 clients.OnClosing(s);
165 RTC_DCHECK(s->valid()); // Close must not have been called yet.
166 FD_CLR(s->socket(), &socket_set);
167 delete (*i);
168 i = sockets.erase(i);
169 if (i == sockets.end())
170 break;
171 }
172 }
173
174 clients.CheckForTimeout();
175
176 if (FD_ISSET(listener.socket(), &socket_set)) {
177 DataSocket* s = listener.Accept();
178 if (sockets.size() >= kMaxConnections) {
179 delete s; // sorry, that's all we can take.
180 printf("Connection limit reached\n");
181 } else {
182 sockets.push_back(s);
183 printf("New connection...\n");
184 }
185 }
186 }
187
188 for (SocketArray::iterator i = sockets.begin(); i != sockets.end(); ++i)
189 delete (*i);
190 sockets.clear();
191
192 return 0;
193 }
194