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