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