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