OLD | NEW |
| (Empty) |
1 /* | |
2 * libjingle | |
3 * Copyright 2011 Google Inc. | |
4 * | |
5 * Redistribution and use in source and binary forms, with or without | |
6 * modification, are permitted provided that the following conditions are met: | |
7 * | |
8 * 1. Redistributions of source code must retain the above copyright notice, | |
9 * this list of conditions and the following disclaimer. | |
10 * 2. Redistributions in binary form must reproduce the above copyright notice, | |
11 * this list of conditions and the following disclaimer in the documentation | |
12 * and/or other materials provided with the distribution. | |
13 * 3. The name of the author may not be used to endorse or promote products | |
14 * derived from this software without specific prior written permission. | |
15 * | |
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED | |
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | |
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | |
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | |
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | |
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF | |
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
26 */ | |
27 | |
28 #include "talk/examples/peerconnection/server/data_socket.h" | |
29 | |
30 #include <ctype.h> | |
31 #include <stdio.h> | |
32 #include <stdlib.h> | |
33 #include <string.h> | |
34 #if defined(WEBRTC_POSIX) | |
35 #include <unistd.h> | |
36 #endif | |
37 | |
38 #include "talk/examples/peerconnection/server/utils.h" | |
39 | |
40 static const char kHeaderTerminator[] = "\r\n\r\n"; | |
41 static const int kHeaderTerminatorLength = sizeof(kHeaderTerminator) - 1; | |
42 | |
43 // static | |
44 const char DataSocket::kCrossOriginAllowHeaders[] = | |
45 "Access-Control-Allow-Origin: *\r\n" | |
46 "Access-Control-Allow-Credentials: true\r\n" | |
47 "Access-Control-Allow-Methods: POST, GET, OPTIONS\r\n" | |
48 "Access-Control-Allow-Headers: Content-Type, " | |
49 "Content-Length, Connection, Cache-Control\r\n" | |
50 "Access-Control-Expose-Headers: Content-Length, X-Peer-Id\r\n"; | |
51 | |
52 #if defined(WIN32) | |
53 class WinsockInitializer { | |
54 static WinsockInitializer singleton; | |
55 | |
56 WinsockInitializer() { | |
57 WSADATA data; | |
58 WSAStartup(MAKEWORD(1, 0), &data); | |
59 } | |
60 | |
61 public: | |
62 ~WinsockInitializer() { WSACleanup(); } | |
63 }; | |
64 WinsockInitializer WinsockInitializer::singleton; | |
65 #endif | |
66 | |
67 // | |
68 // SocketBase | |
69 // | |
70 | |
71 bool SocketBase::Create() { | |
72 assert(!valid()); | |
73 socket_ = ::socket(AF_INET, SOCK_STREAM, 0); | |
74 return valid(); | |
75 } | |
76 | |
77 void SocketBase::Close() { | |
78 if (socket_ != INVALID_SOCKET) { | |
79 closesocket(socket_); | |
80 socket_ = INVALID_SOCKET; | |
81 } | |
82 } | |
83 | |
84 // | |
85 // DataSocket | |
86 // | |
87 | |
88 std::string DataSocket::request_arguments() const { | |
89 size_t args = request_path_.find('?'); | |
90 if (args != std::string::npos) | |
91 return request_path_.substr(args + 1); | |
92 return ""; | |
93 } | |
94 | |
95 bool DataSocket::PathEquals(const char* path) const { | |
96 assert(path); | |
97 size_t args = request_path_.find('?'); | |
98 if (args != std::string::npos) | |
99 return request_path_.substr(0, args).compare(path) == 0; | |
100 return request_path_.compare(path) == 0; | |
101 } | |
102 | |
103 bool DataSocket::OnDataAvailable(bool* close_socket) { | |
104 assert(valid()); | |
105 char buffer[0xfff] = {0}; | |
106 int bytes = recv(socket_, buffer, sizeof(buffer), 0); | |
107 if (bytes == SOCKET_ERROR || bytes == 0) { | |
108 *close_socket = true; | |
109 return false; | |
110 } | |
111 | |
112 *close_socket = false; | |
113 | |
114 bool ret = true; | |
115 if (headers_received()) { | |
116 if (method_ != POST) { | |
117 // unexpectedly received data. | |
118 ret = false; | |
119 } else { | |
120 data_.append(buffer, bytes); | |
121 } | |
122 } else { | |
123 request_headers_.append(buffer, bytes); | |
124 size_t found = request_headers_.find(kHeaderTerminator); | |
125 if (found != std::string::npos) { | |
126 data_ = request_headers_.substr(found + kHeaderTerminatorLength); | |
127 request_headers_.resize(found + kHeaderTerminatorLength); | |
128 ret = ParseHeaders(); | |
129 } | |
130 } | |
131 return ret; | |
132 } | |
133 | |
134 bool DataSocket::Send(const std::string& data) const { | |
135 return send(socket_, data.data(), static_cast<int>(data.length()), 0) != | |
136 SOCKET_ERROR; | |
137 } | |
138 | |
139 bool DataSocket::Send(const std::string& status, bool connection_close, | |
140 const std::string& content_type, | |
141 const std::string& extra_headers, | |
142 const std::string& data) const { | |
143 assert(valid()); | |
144 assert(!status.empty()); | |
145 std::string buffer("HTTP/1.1 " + status + "\r\n"); | |
146 | |
147 buffer += "Server: PeerConnectionTestServer/0.1\r\n" | |
148 "Cache-Control: no-cache\r\n"; | |
149 | |
150 if (connection_close) | |
151 buffer += "Connection: close\r\n"; | |
152 | |
153 if (!content_type.empty()) | |
154 buffer += "Content-Type: " + content_type + "\r\n"; | |
155 | |
156 buffer += "Content-Length: " + int2str(static_cast<int>(data.size())) + | |
157 "\r\n"; | |
158 | |
159 if (!extra_headers.empty()) { | |
160 buffer += extra_headers; | |
161 // Extra headers are assumed to have a separator per header. | |
162 } | |
163 | |
164 buffer += kCrossOriginAllowHeaders; | |
165 | |
166 buffer += "\r\n"; | |
167 buffer += data; | |
168 | |
169 return Send(buffer); | |
170 } | |
171 | |
172 void DataSocket::Clear() { | |
173 method_ = INVALID; | |
174 content_length_ = 0; | |
175 content_type_.clear(); | |
176 request_path_.clear(); | |
177 request_headers_.clear(); | |
178 data_.clear(); | |
179 } | |
180 | |
181 bool DataSocket::ParseHeaders() { | |
182 assert(!request_headers_.empty()); | |
183 assert(method_ == INVALID); | |
184 size_t i = request_headers_.find("\r\n"); | |
185 if (i == std::string::npos) | |
186 return false; | |
187 | |
188 if (!ParseMethodAndPath(request_headers_.data(), i)) | |
189 return false; | |
190 | |
191 assert(method_ != INVALID); | |
192 assert(!request_path_.empty()); | |
193 | |
194 if (method_ == POST) { | |
195 const char* headers = request_headers_.data() + i + 2; | |
196 size_t len = request_headers_.length() - i - 2; | |
197 if (!ParseContentLengthAndType(headers, len)) | |
198 return false; | |
199 } | |
200 | |
201 return true; | |
202 } | |
203 | |
204 bool DataSocket::ParseMethodAndPath(const char* begin, size_t len) { | |
205 struct { | |
206 const char* method_name; | |
207 size_t method_name_len; | |
208 RequestMethod id; | |
209 } supported_methods[] = { | |
210 { "GET", 3, GET }, | |
211 { "POST", 4, POST }, | |
212 { "OPTIONS", 7, OPTIONS }, | |
213 }; | |
214 | |
215 const char* path = NULL; | |
216 for (size_t i = 0; i < ARRAYSIZE(supported_methods); ++i) { | |
217 if (len > supported_methods[i].method_name_len && | |
218 isspace(begin[supported_methods[i].method_name_len]) && | |
219 strncmp(begin, supported_methods[i].method_name, | |
220 supported_methods[i].method_name_len) == 0) { | |
221 method_ = supported_methods[i].id; | |
222 path = begin + supported_methods[i].method_name_len; | |
223 break; | |
224 } | |
225 } | |
226 | |
227 const char* end = begin + len; | |
228 if (!path || path >= end) | |
229 return false; | |
230 | |
231 ++path; | |
232 begin = path; | |
233 while (!isspace(*path) && path < end) | |
234 ++path; | |
235 | |
236 request_path_.assign(begin, path - begin); | |
237 | |
238 return true; | |
239 } | |
240 | |
241 bool DataSocket::ParseContentLengthAndType(const char* headers, size_t length) { | |
242 assert(content_length_ == 0); | |
243 assert(content_type_.empty()); | |
244 | |
245 const char* end = headers + length; | |
246 while (headers && headers < end) { | |
247 if (!isspace(headers[0])) { | |
248 static const char kContentLength[] = "Content-Length:"; | |
249 static const char kContentType[] = "Content-Type:"; | |
250 if ((headers + ARRAYSIZE(kContentLength)) < end && | |
251 strncmp(headers, kContentLength, | |
252 ARRAYSIZE(kContentLength) - 1) == 0) { | |
253 headers += ARRAYSIZE(kContentLength) - 1; | |
254 while (headers[0] == ' ') | |
255 ++headers; | |
256 content_length_ = atoi(headers); | |
257 } else if ((headers + ARRAYSIZE(kContentType)) < end && | |
258 strncmp(headers, kContentType, | |
259 ARRAYSIZE(kContentType) - 1) == 0) { | |
260 headers += ARRAYSIZE(kContentType) - 1; | |
261 while (headers[0] == ' ') | |
262 ++headers; | |
263 const char* type_end = strstr(headers, "\r\n"); | |
264 if (type_end == NULL) | |
265 type_end = end; | |
266 content_type_.assign(headers, type_end); | |
267 } | |
268 } else { | |
269 ++headers; | |
270 } | |
271 headers = strstr(headers, "\r\n"); | |
272 if (headers) | |
273 headers += 2; | |
274 } | |
275 | |
276 return !content_type_.empty() && content_length_ != 0; | |
277 } | |
278 | |
279 // | |
280 // ListeningSocket | |
281 // | |
282 | |
283 bool ListeningSocket::Listen(unsigned short port) { | |
284 assert(valid()); | |
285 int enabled = 1; | |
286 setsockopt(socket_, SOL_SOCKET, SO_REUSEADDR, | |
287 reinterpret_cast<const char*>(&enabled), sizeof(enabled)); | |
288 struct sockaddr_in addr = {0}; | |
289 addr.sin_family = AF_INET; | |
290 addr.sin_addr.s_addr = htonl(INADDR_ANY); | |
291 addr.sin_port = htons(port); | |
292 if (bind(socket_, reinterpret_cast<const sockaddr*>(&addr), | |
293 sizeof(addr)) == SOCKET_ERROR) { | |
294 printf("bind failed\n"); | |
295 return false; | |
296 } | |
297 return listen(socket_, 5) != SOCKET_ERROR; | |
298 } | |
299 | |
300 DataSocket* ListeningSocket::Accept() const { | |
301 assert(valid()); | |
302 struct sockaddr_in addr = {0}; | |
303 socklen_t size = sizeof(addr); | |
304 NativeSocket client = | |
305 accept(socket_, reinterpret_cast<sockaddr*>(&addr), &size); | |
306 if (client == INVALID_SOCKET) | |
307 return NULL; | |
308 | |
309 return new DataSocket(client); | |
310 } | |
OLD | NEW |