Home | History | Annotate | Download | only in server
      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 "webrtc/examples/peerconnection/server/data_socket.h"
     12 
     13 #include <ctype.h>
     14 #include <stdio.h>
     15 #include <stdlib.h>
     16 #include <string.h>
     17 #if defined(WEBRTC_POSIX)
     18 #include <unistd.h>
     19 #endif
     20 
     21 #include "webrtc/examples/peerconnection/server/utils.h"
     22 
     23 static const char kHeaderTerminator[] = "\r\n\r\n";
     24 static const int kHeaderTerminatorLength = sizeof(kHeaderTerminator) - 1;
     25 
     26 // static
     27 const char DataSocket::kCrossOriginAllowHeaders[] =
     28     "Access-Control-Allow-Origin: *\r\n"
     29     "Access-Control-Allow-Credentials: true\r\n"
     30     "Access-Control-Allow-Methods: POST, GET, OPTIONS\r\n"
     31     "Access-Control-Allow-Headers: Content-Type, "
     32         "Content-Length, Connection, Cache-Control\r\n"
     33     "Access-Control-Expose-Headers: Content-Length, X-Peer-Id\r\n";
     34 
     35 #if defined(WIN32)
     36 class WinsockInitializer {
     37   static WinsockInitializer singleton;
     38 
     39   WinsockInitializer() {
     40     WSADATA data;
     41     WSAStartup(MAKEWORD(1, 0), &data);
     42   }
     43 
     44  public:
     45   ~WinsockInitializer() { WSACleanup(); }
     46 };
     47 WinsockInitializer WinsockInitializer::singleton;
     48 #endif
     49 
     50 //
     51 // SocketBase
     52 //
     53 
     54 bool SocketBase::Create() {
     55   assert(!valid());
     56   socket_ = ::socket(AF_INET, SOCK_STREAM, 0);
     57   return valid();
     58 }
     59 
     60 void SocketBase::Close() {
     61   if (socket_ != INVALID_SOCKET) {
     62     closesocket(socket_);
     63     socket_ = INVALID_SOCKET;
     64   }
     65 }
     66 
     67 //
     68 // DataSocket
     69 //
     70 
     71 std::string DataSocket::request_arguments() const {
     72   size_t args = request_path_.find('?');
     73   if (args != std::string::npos)
     74     return request_path_.substr(args + 1);
     75   return "";
     76 }
     77 
     78 bool DataSocket::PathEquals(const char* path) const {
     79   assert(path);
     80   size_t args = request_path_.find('?');
     81   if (args != std::string::npos)
     82     return request_path_.substr(0, args).compare(path) == 0;
     83   return request_path_.compare(path) == 0;
     84 }
     85 
     86 bool DataSocket::OnDataAvailable(bool* close_socket) {
     87   assert(valid());
     88   char buffer[0xfff] = {0};
     89   int bytes = recv(socket_, buffer, sizeof(buffer), 0);
     90   if (bytes == SOCKET_ERROR || bytes == 0) {
     91     *close_socket = true;
     92     return false;
     93   }
     94 
     95   *close_socket = false;
     96 
     97   bool ret = true;
     98   if (headers_received()) {
     99     if (method_ != POST) {
    100       // unexpectedly received data.
    101       ret = false;
    102     } else {
    103       data_.append(buffer, bytes);
    104     }
    105   } else {
    106     request_headers_.append(buffer, bytes);
    107     size_t found = request_headers_.find(kHeaderTerminator);
    108     if (found != std::string::npos) {
    109       data_ = request_headers_.substr(found + kHeaderTerminatorLength);
    110       request_headers_.resize(found + kHeaderTerminatorLength);
    111       ret = ParseHeaders();
    112     }
    113   }
    114   return ret;
    115 }
    116 
    117 bool DataSocket::Send(const std::string& data) const {
    118   return send(socket_, data.data(), static_cast<int>(data.length()), 0) !=
    119       SOCKET_ERROR;
    120 }
    121 
    122 bool DataSocket::Send(const std::string& status, bool connection_close,
    123                       const std::string& content_type,
    124                       const std::string& extra_headers,
    125                       const std::string& data) const {
    126   assert(valid());
    127   assert(!status.empty());
    128   std::string buffer("HTTP/1.1 " + status + "\r\n");
    129 
    130   buffer += "Server: PeerConnectionTestServer/0.1\r\n"
    131             "Cache-Control: no-cache\r\n";
    132 
    133   if (connection_close)
    134     buffer += "Connection: close\r\n";
    135 
    136   if (!content_type.empty())
    137     buffer += "Content-Type: " + content_type + "\r\n";
    138 
    139   buffer += "Content-Length: " + int2str(static_cast<int>(data.size())) +
    140             "\r\n";
    141 
    142   if (!extra_headers.empty()) {
    143     buffer += extra_headers;
    144     // Extra headers are assumed to have a separator per header.
    145   }
    146 
    147   buffer += kCrossOriginAllowHeaders;
    148 
    149   buffer += "\r\n";
    150   buffer += data;
    151 
    152   return Send(buffer);
    153 }
    154 
    155 void DataSocket::Clear() {
    156   method_ = INVALID;
    157   content_length_ = 0;
    158   content_type_.clear();
    159   request_path_.clear();
    160   request_headers_.clear();
    161   data_.clear();
    162 }
    163 
    164 bool DataSocket::ParseHeaders() {
    165   assert(!request_headers_.empty());
    166   assert(method_ == INVALID);
    167   size_t i = request_headers_.find("\r\n");
    168   if (i == std::string::npos)
    169     return false;
    170 
    171   if (!ParseMethodAndPath(request_headers_.data(), i))
    172     return false;
    173 
    174   assert(method_ != INVALID);
    175   assert(!request_path_.empty());
    176 
    177   if (method_ == POST) {
    178     const char* headers = request_headers_.data() + i + 2;
    179     size_t len = request_headers_.length() - i - 2;
    180     if (!ParseContentLengthAndType(headers, len))
    181       return false;
    182   }
    183 
    184   return true;
    185 }
    186 
    187 bool DataSocket::ParseMethodAndPath(const char* begin, size_t len) {
    188   struct {
    189     const char* method_name;
    190     size_t method_name_len;
    191     RequestMethod id;
    192   } supported_methods[] = {
    193     { "GET", 3, GET },
    194     { "POST", 4, POST },
    195     { "OPTIONS", 7, OPTIONS },
    196   };
    197 
    198   const char* path = NULL;
    199   for (size_t i = 0; i < ARRAYSIZE(supported_methods); ++i) {
    200     if (len > supported_methods[i].method_name_len &&
    201         isspace(begin[supported_methods[i].method_name_len]) &&
    202         strncmp(begin, supported_methods[i].method_name,
    203                 supported_methods[i].method_name_len) == 0) {
    204       method_ = supported_methods[i].id;
    205       path = begin + supported_methods[i].method_name_len;
    206       break;
    207     }
    208   }
    209 
    210   const char* end = begin + len;
    211   if (!path || path >= end)
    212     return false;
    213 
    214   ++path;
    215   begin = path;
    216   while (!isspace(*path) && path < end)
    217     ++path;
    218 
    219   request_path_.assign(begin, path - begin);
    220 
    221   return true;
    222 }
    223 
    224 bool DataSocket::ParseContentLengthAndType(const char* headers, size_t length) {
    225   assert(content_length_ == 0);
    226   assert(content_type_.empty());
    227 
    228   const char* end = headers + length;
    229   while (headers && headers < end) {
    230     if (!isspace(headers[0])) {
    231       static const char kContentLength[] = "Content-Length:";
    232       static const char kContentType[] = "Content-Type:";
    233       if ((headers + ARRAYSIZE(kContentLength)) < end &&
    234           strncmp(headers, kContentLength,
    235                   ARRAYSIZE(kContentLength) - 1) == 0) {
    236         headers += ARRAYSIZE(kContentLength) - 1;
    237         while (headers[0] == ' ')
    238           ++headers;
    239         content_length_ = atoi(headers);
    240       } else if ((headers + ARRAYSIZE(kContentType)) < end &&
    241                  strncmp(headers, kContentType,
    242                          ARRAYSIZE(kContentType) - 1) == 0) {
    243         headers += ARRAYSIZE(kContentType) - 1;
    244         while (headers[0] == ' ')
    245           ++headers;
    246         const char* type_end = strstr(headers, "\r\n");
    247         if (type_end == NULL)
    248           type_end = end;
    249         content_type_.assign(headers, type_end);
    250       }
    251     } else {
    252       ++headers;
    253     }
    254     headers = strstr(headers, "\r\n");
    255     if (headers)
    256       headers += 2;
    257   }
    258 
    259   return !content_type_.empty() && content_length_ != 0;
    260 }
    261 
    262 //
    263 // ListeningSocket
    264 //
    265 
    266 bool ListeningSocket::Listen(unsigned short port) {
    267   assert(valid());
    268   int enabled = 1;
    269   setsockopt(socket_, SOL_SOCKET, SO_REUSEADDR,
    270       reinterpret_cast<const char*>(&enabled), sizeof(enabled));
    271   struct sockaddr_in addr = {0};
    272   addr.sin_family = AF_INET;
    273   addr.sin_addr.s_addr = htonl(INADDR_ANY);
    274   addr.sin_port = htons(port);
    275   if (bind(socket_, reinterpret_cast<const sockaddr*>(&addr),
    276            sizeof(addr)) == SOCKET_ERROR) {
    277     printf("bind failed\n");
    278     return false;
    279   }
    280   return listen(socket_, 5) != SOCKET_ERROR;
    281 }
    282 
    283 DataSocket* ListeningSocket::Accept() const {
    284   assert(valid());
    285   struct sockaddr_in addr = {0};
    286   socklen_t size = sizeof(addr);
    287   NativeSocket client =
    288       accept(socket_, reinterpret_cast<sockaddr*>(&addr), &size);
    289   if (client == INVALID_SOCKET)
    290     return NULL;
    291 
    292   return new DataSocket(client);
    293 }
    294