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