summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/examples/peerconnection/server/data_socket.cc
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/libwebrtc/examples/peerconnection/server/data_socket.cc')
-rw-r--r--third_party/libwebrtc/examples/peerconnection/server/data_socket.cc299
1 files changed, 299 insertions, 0 deletions
diff --git a/third_party/libwebrtc/examples/peerconnection/server/data_socket.cc b/third_party/libwebrtc/examples/peerconnection/server/data_socket.cc
new file mode 100644
index 0000000000..855ebd8c0c
--- /dev/null
+++ b/third_party/libwebrtc/examples/peerconnection/server/data_socket.cc
@@ -0,0 +1,299 @@
+/*
+ * Copyright 2011 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "examples/peerconnection/server/data_socket.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#if defined(WEBRTC_POSIX)
+#include <unistd.h>
+#endif
+
+#include "examples/peerconnection/server/utils.h"
+#include "rtc_base/checks.h"
+
+static const char kHeaderTerminator[] = "\r\n\r\n";
+static const int kHeaderTerminatorLength = sizeof(kHeaderTerminator) - 1;
+
+// static
+const char DataSocket::kCrossOriginAllowHeaders[] =
+ "Access-Control-Allow-Origin: *\r\n"
+ "Access-Control-Allow-Credentials: true\r\n"
+ "Access-Control-Allow-Methods: POST, GET, OPTIONS\r\n"
+ "Access-Control-Allow-Headers: Content-Type, "
+ "Content-Length, Connection, Cache-Control\r\n"
+ "Access-Control-Expose-Headers: Content-Length\r\n";
+
+#if defined(WIN32)
+class WinsockInitializer {
+ static WinsockInitializer singleton;
+
+ WinsockInitializer() {
+ WSADATA data;
+ WSAStartup(MAKEWORD(1, 0), &data);
+ }
+
+ public:
+ ~WinsockInitializer() { WSACleanup(); }
+};
+WinsockInitializer WinsockInitializer::singleton;
+#endif
+
+//
+// SocketBase
+//
+
+bool SocketBase::Create() {
+ RTC_DCHECK(!valid());
+ socket_ = ::socket(AF_INET, SOCK_STREAM, 0);
+ return valid();
+}
+
+void SocketBase::Close() {
+ if (socket_ != INVALID_SOCKET) {
+ closesocket(socket_);
+ socket_ = INVALID_SOCKET;
+ }
+}
+
+//
+// DataSocket
+//
+
+std::string DataSocket::request_arguments() const {
+ size_t args = request_path_.find('?');
+ if (args != std::string::npos)
+ return request_path_.substr(args + 1);
+ return "";
+}
+
+bool DataSocket::PathEquals(const char* path) const {
+ RTC_DCHECK(path);
+ size_t args = request_path_.find('?');
+ if (args != std::string::npos)
+ return request_path_.substr(0, args).compare(path) == 0;
+ return request_path_.compare(path) == 0;
+}
+
+bool DataSocket::OnDataAvailable(bool* close_socket) {
+ RTC_DCHECK(valid());
+ char buffer[0xfff] = {0};
+ int bytes = recv(socket_, buffer, sizeof(buffer), 0);
+ if (bytes == SOCKET_ERROR || bytes == 0) {
+ *close_socket = true;
+ return false;
+ }
+
+ *close_socket = false;
+
+ bool ret = true;
+ if (headers_received()) {
+ if (method_ != POST) {
+ // unexpectedly received data.
+ ret = false;
+ } else {
+ data_.append(buffer, bytes);
+ }
+ } else {
+ request_headers_.append(buffer, bytes);
+ size_t found = request_headers_.find(kHeaderTerminator);
+ if (found != std::string::npos) {
+ data_ = request_headers_.substr(found + kHeaderTerminatorLength);
+ request_headers_.resize(found + kHeaderTerminatorLength);
+ ret = ParseHeaders();
+ }
+ }
+ return ret;
+}
+
+bool DataSocket::Send(const std::string& data) const {
+ return send(socket_, data.data(), static_cast<int>(data.length()), 0) !=
+ SOCKET_ERROR;
+}
+
+bool DataSocket::Send(const std::string& status,
+ bool connection_close,
+ const std::string& content_type,
+ const std::string& extra_headers,
+ const std::string& data) const {
+ RTC_DCHECK(valid());
+ RTC_DCHECK(!status.empty());
+ std::string buffer("HTTP/1.1 " + status + "\r\n");
+
+ buffer +=
+ "Server: PeerConnectionTestServer/0.1\r\n"
+ "Cache-Control: no-cache\r\n";
+
+ if (connection_close)
+ buffer += "Connection: close\r\n";
+
+ if (!content_type.empty())
+ buffer += "Content-Type: " + content_type + "\r\n";
+
+ buffer +=
+ "Content-Length: " + int2str(static_cast<int>(data.size())) + "\r\n";
+
+ if (!extra_headers.empty()) {
+ buffer += extra_headers;
+ // Extra headers are assumed to have a separator per header.
+ }
+
+ buffer += kCrossOriginAllowHeaders;
+
+ buffer += "\r\n";
+ buffer += data;
+
+ return Send(buffer);
+}
+
+void DataSocket::Clear() {
+ method_ = INVALID;
+ content_length_ = 0;
+ content_type_.clear();
+ request_path_.clear();
+ request_headers_.clear();
+ data_.clear();
+}
+
+bool DataSocket::ParseHeaders() {
+ RTC_DCHECK(!request_headers_.empty());
+ RTC_DCHECK_EQ(method_, INVALID);
+ size_t i = request_headers_.find("\r\n");
+ if (i == std::string::npos)
+ return false;
+
+ if (!ParseMethodAndPath(request_headers_.data(), i))
+ return false;
+
+ RTC_DCHECK_NE(method_, INVALID);
+ RTC_DCHECK(!request_path_.empty());
+
+ if (method_ == POST) {
+ const char* headers = request_headers_.data() + i + 2;
+ size_t len = request_headers_.length() - i - 2;
+ if (!ParseContentLengthAndType(headers, len))
+ return false;
+ }
+
+ return true;
+}
+
+bool DataSocket::ParseMethodAndPath(const char* begin, size_t len) {
+ struct {
+ const char* method_name;
+ size_t method_name_len;
+ RequestMethod id;
+ } supported_methods[] = {
+ {"GET", 3, GET},
+ {"POST", 4, POST},
+ {"OPTIONS", 7, OPTIONS},
+ };
+
+ const char* path = NULL;
+ for (size_t i = 0; i < ARRAYSIZE(supported_methods); ++i) {
+ if (len > supported_methods[i].method_name_len &&
+ isspace(begin[supported_methods[i].method_name_len]) &&
+ strncmp(begin, supported_methods[i].method_name,
+ supported_methods[i].method_name_len) == 0) {
+ method_ = supported_methods[i].id;
+ path = begin + supported_methods[i].method_name_len;
+ break;
+ }
+ }
+
+ const char* end = begin + len;
+ if (!path || path >= end)
+ return false;
+
+ ++path;
+ begin = path;
+ while (!isspace(*path) && path < end)
+ ++path;
+
+ request_path_.assign(begin, path - begin);
+
+ return true;
+}
+
+bool DataSocket::ParseContentLengthAndType(const char* headers, size_t length) {
+ RTC_DCHECK_EQ(content_length_, 0);
+ RTC_DCHECK(content_type_.empty());
+
+ const char* end = headers + length;
+ while (headers && headers < end) {
+ if (!isspace(headers[0])) {
+ static const char kContentLength[] = "Content-Length:";
+ static const char kContentType[] = "Content-Type:";
+ if ((headers + ARRAYSIZE(kContentLength)) < end &&
+ strncmp(headers, kContentLength, ARRAYSIZE(kContentLength) - 1) ==
+ 0) {
+ headers += ARRAYSIZE(kContentLength) - 1;
+ while (headers[0] == ' ')
+ ++headers;
+ content_length_ = atoi(headers);
+ } else if ((headers + ARRAYSIZE(kContentType)) < end &&
+ strncmp(headers, kContentType, ARRAYSIZE(kContentType) - 1) ==
+ 0) {
+ headers += ARRAYSIZE(kContentType) - 1;
+ while (headers[0] == ' ')
+ ++headers;
+ const char* type_end = strstr(headers, "\r\n");
+ if (type_end == NULL)
+ type_end = end;
+ content_type_.assign(headers, type_end);
+ }
+ } else {
+ ++headers;
+ }
+ headers = strstr(headers, "\r\n");
+ if (headers)
+ headers += 2;
+ }
+
+ return !content_type_.empty() && content_length_ != 0;
+}
+
+//
+// ListeningSocket
+//
+
+bool ListeningSocket::Listen(unsigned short port) {
+ RTC_DCHECK(valid());
+ int enabled = 1;
+ if (setsockopt(socket_, SOL_SOCKET, SO_REUSEADDR,
+ reinterpret_cast<const char*>(&enabled),
+ sizeof(enabled)) != 0) {
+ printf("setsockopt failed\n");
+ return false;
+ }
+ struct sockaddr_in addr = {0};
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ addr.sin_port = htons(port);
+ if (bind(socket_, reinterpret_cast<const sockaddr*>(&addr), sizeof(addr)) ==
+ SOCKET_ERROR) {
+ printf("bind failed\n");
+ return false;
+ }
+ return listen(socket_, 5) != SOCKET_ERROR;
+}
+
+DataSocket* ListeningSocket::Accept() const {
+ RTC_DCHECK(valid());
+ struct sockaddr_in addr = {0};
+ socklen_t size = sizeof(addr);
+ NativeSocket client =
+ accept(socket_, reinterpret_cast<sockaddr*>(&addr), &size);
+ if (client == INVALID_SOCKET)
+ return NULL;
+
+ return new DataSocket(client);
+}