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