xref: /aosp_15_r20/external/webrtc/examples/peerconnection/server/data_socket.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
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 <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #if defined(WEBRTC_POSIX)
17 #include <unistd.h>
18 #endif
19 
20 #include "examples/peerconnection/server/utils.h"
21 #include "rtc_base/checks.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\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   RTC_DCHECK(!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   RTC_DCHECK(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   RTC_DCHECK(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,
123                       bool connection_close,
124                       const std::string& content_type,
125                       const std::string& extra_headers,
126                       const std::string& data) const {
127   RTC_DCHECK(valid());
128   RTC_DCHECK(!status.empty());
129   std::string buffer("HTTP/1.1 " + status + "\r\n");
130 
131   buffer +=
132       "Server: PeerConnectionTestServer/0.1\r\n"
133       "Cache-Control: no-cache\r\n";
134 
135   if (connection_close)
136     buffer += "Connection: close\r\n";
137 
138   if (!content_type.empty())
139     buffer += "Content-Type: " + content_type + "\r\n";
140 
141   buffer +=
142       "Content-Length: " + int2str(static_cast<int>(data.size())) + "\r\n";
143 
144   if (!extra_headers.empty()) {
145     buffer += extra_headers;
146     // Extra headers are assumed to have a separator per header.
147   }
148 
149   buffer += kCrossOriginAllowHeaders;
150 
151   buffer += "\r\n";
152   buffer += data;
153 
154   return Send(buffer);
155 }
156 
Clear()157 void DataSocket::Clear() {
158   method_ = INVALID;
159   content_length_ = 0;
160   content_type_.clear();
161   request_path_.clear();
162   request_headers_.clear();
163   data_.clear();
164 }
165 
ParseHeaders()166 bool DataSocket::ParseHeaders() {
167   RTC_DCHECK(!request_headers_.empty());
168   RTC_DCHECK_EQ(method_, INVALID);
169   size_t i = request_headers_.find("\r\n");
170   if (i == std::string::npos)
171     return false;
172 
173   if (!ParseMethodAndPath(request_headers_.data(), i))
174     return false;
175 
176   RTC_DCHECK_NE(method_, INVALID);
177   RTC_DCHECK(!request_path_.empty());
178 
179   if (method_ == POST) {
180     const char* headers = request_headers_.data() + i + 2;
181     size_t len = request_headers_.length() - i - 2;
182     if (!ParseContentLengthAndType(headers, len))
183       return false;
184   }
185 
186   return true;
187 }
188 
ParseMethodAndPath(const char * begin,size_t len)189 bool DataSocket::ParseMethodAndPath(const char* begin, size_t len) {
190   struct {
191     const char* method_name;
192     size_t method_name_len;
193     RequestMethod id;
194   } supported_methods[] = {
195       {"GET", 3, GET},
196       {"POST", 4, POST},
197       {"OPTIONS", 7, OPTIONS},
198   };
199 
200   const char* path = NULL;
201   for (size_t i = 0; i < ARRAYSIZE(supported_methods); ++i) {
202     if (len > supported_methods[i].method_name_len &&
203         isspace(begin[supported_methods[i].method_name_len]) &&
204         strncmp(begin, supported_methods[i].method_name,
205                 supported_methods[i].method_name_len) == 0) {
206       method_ = supported_methods[i].id;
207       path = begin + supported_methods[i].method_name_len;
208       break;
209     }
210   }
211 
212   const char* end = begin + len;
213   if (!path || path >= end)
214     return false;
215 
216   ++path;
217   begin = path;
218   while (!isspace(*path) && path < end)
219     ++path;
220 
221   request_path_.assign(begin, path - begin);
222 
223   return true;
224 }
225 
ParseContentLengthAndType(const char * headers,size_t length)226 bool DataSocket::ParseContentLengthAndType(const char* headers, size_t length) {
227   RTC_DCHECK_EQ(content_length_, 0);
228   RTC_DCHECK(content_type_.empty());
229 
230   const char* end = headers + length;
231   while (headers && headers < end) {
232     if (!isspace(headers[0])) {
233       static const char kContentLength[] = "Content-Length:";
234       static const char kContentType[] = "Content-Type:";
235       if ((headers + ARRAYSIZE(kContentLength)) < end &&
236           strncmp(headers, kContentLength, ARRAYSIZE(kContentLength) - 1) ==
237               0) {
238         headers += ARRAYSIZE(kContentLength) - 1;
239         while (headers[0] == ' ')
240           ++headers;
241         content_length_ = atoi(headers);
242       } else if ((headers + ARRAYSIZE(kContentType)) < end &&
243                  strncmp(headers, kContentType, ARRAYSIZE(kContentType) - 1) ==
244                      0) {
245         headers += ARRAYSIZE(kContentType) - 1;
246         while (headers[0] == ' ')
247           ++headers;
248         const char* type_end = strstr(headers, "\r\n");
249         if (type_end == NULL)
250           type_end = end;
251         content_type_.assign(headers, type_end);
252       }
253     } else {
254       ++headers;
255     }
256     headers = strstr(headers, "\r\n");
257     if (headers)
258       headers += 2;
259   }
260 
261   return !content_type_.empty() && content_length_ != 0;
262 }
263 
264 //
265 // ListeningSocket
266 //
267 
Listen(unsigned short port)268 bool ListeningSocket::Listen(unsigned short port) {
269   RTC_DCHECK(valid());
270   int enabled = 1;
271   if (setsockopt(socket_, SOL_SOCKET, SO_REUSEADDR,
272                  reinterpret_cast<const char*>(&enabled),
273                  sizeof(enabled)) != 0) {
274     printf("setsockopt failed\n");
275     return false;
276   }
277   struct sockaddr_in addr = {0};
278   addr.sin_family = AF_INET;
279   addr.sin_addr.s_addr = htonl(INADDR_ANY);
280   addr.sin_port = htons(port);
281   if (bind(socket_, reinterpret_cast<const sockaddr*>(&addr), sizeof(addr)) ==
282       SOCKET_ERROR) {
283     printf("bind failed\n");
284     return false;
285   }
286   return listen(socket_, 5) != SOCKET_ERROR;
287 }
288 
Accept() const289 DataSocket* ListeningSocket::Accept() const {
290   RTC_DCHECK(valid());
291   struct sockaddr_in addr = {0};
292   socklen_t size = sizeof(addr);
293   NativeSocket client =
294       accept(socket_, reinterpret_cast<sockaddr*>(&addr), &size);
295   if (client == INVALID_SOCKET)
296     return NULL;
297 
298   return new DataSocket(client);
299 }
300