• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
WinsockInitializer()39   WinsockInitializer() {
40     WSADATA data;
41     WSAStartup(MAKEWORD(1, 0), &data);
42   }
43 
44  public:
~WinsockInitializer()45   ~WinsockInitializer() { WSACleanup(); }
46 };
47 WinsockInitializer WinsockInitializer::singleton;
48 #endif
49 
50 //
51 // SocketBase
52 //
53 
Create()54 bool SocketBase::Create() {
55   assert(!valid());
56   socket_ = ::socket(AF_INET, SOCK_STREAM, 0);
57   return valid();
58 }
59 
Close()60 void SocketBase::Close() {
61   if (socket_ != INVALID_SOCKET) {
62     closesocket(socket_);
63     socket_ = INVALID_SOCKET;
64   }
65 }
66 
67 //
68 // DataSocket
69 //
70 
request_arguments() const71 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 
PathEquals(const char * path) const78 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 
OnDataAvailable(bool * close_socket)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 
Send(const std::string & data) const117 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 
Send(const std::string & status,bool connection_close,const std::string & content_type,const std::string & extra_headers,const std::string & data) const122 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 
Clear()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 
ParseHeaders()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 
ParseMethodAndPath(const char * begin,size_t len)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 
ParseContentLengthAndType(const char * headers,size_t length)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 
Listen(unsigned short port)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 
Accept() const283 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