/* * libjingle * Copyright 2011, Google Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include "talk/base/flags.h" #include "talk/examples/peerconnection/server/data_socket.h" #include "talk/examples/peerconnection/server/peer_channel.h" #include "talk/examples/peerconnection/server/utils.h" DEFINE_bool(help, false, "Prints this message"); DEFINE_int(port, 8888, "The port on which to listen."); static const size_t kMaxConnections = (FD_SETSIZE - 2); void HandleBrowserRequest(DataSocket* ds, bool* quit) { assert(ds && ds->valid()); assert(quit); const std::string& path = ds->request_path(); *quit = (path.compare("/quit") == 0); if (*quit) { ds->Send("200 OK", true, "text/html", "", "Quitting..."); } else if (ds->method() == DataSocket::OPTIONS) { // We'll get this when a browsers do cross-resource-sharing requests. // The headers to allow cross-origin script support will be set inside // Send. ds->Send("200 OK", true, "", "", ""); } else { // Here we could write some useful output back to the browser depending on // the path. printf("Received an invalid request: %s\n", ds->request_path().c_str()); ds->Send("500 Sorry", true, "text/html", "", "Sorry, not yet implemented"); } } int main(int argc, char** argv) { FlagList::SetFlagsFromCommandLine(&argc, argv, true); if (FLAG_help) { FlagList::Print(NULL, false); return 0; } // Abort if the user specifies a port that is outside the allowed // range [1, 65535]. if ((FLAG_port < 1) || (FLAG_port > 65535)) { printf("Error: %i is not a valid port.\n", FLAG_port); return -1; } ListeningSocket listener; if (!listener.Create()) { printf("Failed to create server socket\n"); return -1; } else if (!listener.Listen(FLAG_port)) { printf("Failed to listen on server socket\n"); return -1; } printf("Server listening on port %i\n", FLAG_port); PeerChannel clients; typedef std::vector SocketArray; SocketArray sockets; bool quit = false; while (!quit) { fd_set socket_set; FD_ZERO(&socket_set); if (listener.valid()) FD_SET(listener.socket(), &socket_set); for (SocketArray::iterator i = sockets.begin(); i != sockets.end(); ++i) FD_SET((*i)->socket(), &socket_set); struct timeval timeout = { 10, 0 }; if (select(FD_SETSIZE, &socket_set, NULL, NULL, &timeout) == SOCKET_ERROR) { printf("select failed\n"); break; } for (SocketArray::iterator i = sockets.begin(); i != sockets.end(); ++i) { DataSocket* s = *i; bool socket_done = true; if (FD_ISSET(s->socket(), &socket_set)) { if (s->OnDataAvailable(&socket_done) && s->request_received()) { ChannelMember* member = clients.Lookup(s); if (member || PeerChannel::IsPeerConnection(s)) { if (!member) { if (s->PathEquals("/sign_in")) { clients.AddMember(s); } else { printf("No member found for: %s\n", s->request_path().c_str()); s->Send("500 Error", true, "text/plain", "", "Peer most likely gone."); } } else if (member->is_wait_request(s)) { // no need to do anything. socket_done = false; } else { ChannelMember* target = clients.IsTargetedRequest(s); if (target) { member->ForwardRequestToPeer(s, target); } else if (s->PathEquals("/sign_out")) { s->Send("200 OK", true, "text/plain", "", ""); } else { printf("Couldn't find target for request: %s\n", s->request_path().c_str()); s->Send("500 Error", true, "text/plain", "", "Peer most likely gone."); } } } else { HandleBrowserRequest(s, &quit); if (quit) { printf("Quitting...\n"); FD_CLR(listener.socket(), &socket_set); listener.Close(); clients.CloseAll(); } } } } else { socket_done = false; } if (socket_done) { printf("Disconnecting socket\n"); clients.OnClosing(s); assert(s->valid()); // Close must not have been called yet. FD_CLR(s->socket(), &socket_set); delete (*i); i = sockets.erase(i); if (i == sockets.end()) break; } } clients.CheckForTimeout(); if (FD_ISSET(listener.socket(), &socket_set)) { DataSocket* s = listener.Accept(); if (sockets.size() >= kMaxConnections) { delete s; // sorry, that's all we can take. printf("Connection limit reached\n"); } else { sockets.push_back(s); printf("New connection...\n"); } } } for (SocketArray::iterator i = sockets.begin(); i != sockets.end(); ++i) delete (*i); sockets.clear(); return 0; }