Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1669)

Side by Side Diff: net/server/http_listen_socket.cc

Issue 2868036: Brushed up listen socket: (Closed)
Patch Set: Lint. Created 10 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « net/server/http_listen_socket.h ('k') | net/server/http_server_request_info.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #ifdef _WIN32
6 #include <winsock2.h>
7 #else
8 #include <arpa/inet.h>
9 #endif
10
11 #include "base/compiler_specific.h"
12 #include "base/logging.h"
13 #include "base/md5.h"
14 #include "base/string_util.h"
15 #include "net/server/http_listen_socket.h"
16 #include "net/server/http_server_request_info.h"
17
18 // must run in the IO thread
19 HttpListenSocket::HttpListenSocket(SOCKET s,
20 HttpListenSocket::Delegate* delegate)
21 : ALLOW_THIS_IN_INITIALIZER_LIST(ListenSocket(s, this)),
22 delegate_(delegate),
23 is_web_socket_(false) {
24 }
25
26 // must run in the IO thread
27 HttpListenSocket::~HttpListenSocket() {
28 }
29
30 void HttpListenSocket::Accept() {
31 SOCKET conn = ListenSocket::Accept(socket_);
32 DCHECK_NE(conn, ListenSocket::kInvalidSocket);
33 if (conn == ListenSocket::kInvalidSocket) {
34 // TODO
35 } else {
36 scoped_refptr<HttpListenSocket> sock =
37 new HttpListenSocket(conn, delegate_);
38 // it's up to the delegate to AddRef if it wants to keep it around
39 DidAccept(this, sock);
40 }
41 }
42
43 HttpListenSocket* HttpListenSocket::Listen(
44 const std::string& ip,
45 int port,
46 HttpListenSocket::Delegate* delegate) {
47 SOCKET s = ListenSocket::Listen(ip, port);
48 if (s == ListenSocket::kInvalidSocket) {
49 // TODO (ibrar): error handling
50 } else {
51 HttpListenSocket *serv = new HttpListenSocket(s, delegate);
52 serv->Listen();
53 return serv;
54 }
55 return NULL;
56 }
57
58 std::string GetHeaderValue(
59 HttpServerRequestInfo* request,
60 const std::string& header_name) {
61 HttpServerRequestInfo::HeadersMap::iterator it =
62 request->headers.find(header_name);
63 if (it != request->headers.end())
64 return it->second;
65 return "";
66 }
67
68 uint32 WebSocketKeyFingerprint(const std::string& str) {
69 std::string result;
70 const char* pChar = str.c_str();
71 int length = str.length();
72 int spaces = 0;
73 for (int i = 0; i < length; ++i) {
74 if (pChar[i] >= '0' && pChar[i] <= '9')
75 result.append(&pChar[i], 1);
76 else if (pChar[i] == ' ')
77 spaces++;
78 }
79 if (spaces == 0)
80 return 0;
81 int64 number = 0;
82 if (!StringToInt64(result, &number))
83 return 0;
84 return htonl(static_cast<uint32>(number / spaces));
85 }
86
87 void HttpListenSocket::AcceptWebSocket(HttpServerRequestInfo* request) {
88 std::string key1 = GetHeaderValue(request, "Sec-WebSocket-Key1");
89 std::string key2 = GetHeaderValue(request, "Sec-WebSocket-Key2");
90
91 uint32 fp1 = WebSocketKeyFingerprint(key1);
92 uint32 fp2 = WebSocketKeyFingerprint(key2);
93
94 char data[16];
95 memcpy(data, &fp1, 4);
96 memcpy(data + 4, &fp2, 4);
97 memcpy(data + 8, &request->data[0], 8);
98
99 MD5Digest digest;
100 MD5Sum(data, 16, &digest);
101
102 std::string origin = GetHeaderValue(request, "Origin");
103 std::string host = GetHeaderValue(request, "Host");
104 std::string location = "ws://" + host + request->path;
105 is_web_socket_ = true;
106 Send(StringPrintf("HTTP/1.1 101 WebSocket Protocol Handshake\r\n"
107 "Upgrade: WebSocket\r\n"
108 "Connection: Upgrade\r\n"
109 "Sec-WebSocket-Origin: %s\r\n"
110 "Sec-WebSocket-Location: %s\r\n"
111 "\r\n",
112 origin.c_str(),
113 location.c_str()));
114 Send(reinterpret_cast<char*>(digest.a), 16);
115 }
116
117 void HttpListenSocket::SendOverWebSocket(const std::string& data) {
118 DCHECK(is_web_socket_);
119 char message_start = 0;
120 char message_end = -1;
121 Send(&message_start, 1);
122 Send(data);
123 Send(&message_end, 1);
124 }
125
126 //
127 // HTTP Request Parser
128 // This HTTP request parser uses a simple state machine to quickly parse
129 // through the headers. The parser is not 100% complete, as it is designed
130 // for use in this simple test driver.
131 //
132 // Known issues:
133 // - does not handle whitespace on first HTTP line correctly. Expects
134 // a single space between the method/url and url/protocol.
135
136 // Input character types.
137 enum header_parse_inputs {
138 INPUT_SPACE,
139 INPUT_CR,
140 INPUT_LF,
141 INPUT_COLON,
142 INPUT_00,
143 INPUT_FF,
144 INPUT_DEFAULT,
145 MAX_INPUTS,
146 };
147
148 // Parser states.
149 enum header_parse_states {
150 ST_METHOD, // Receiving the method
151 ST_URL, // Receiving the URL
152 ST_PROTO, // Receiving the protocol
153 ST_HEADER, // Starting a Request Header
154 ST_NAME, // Receiving a request header name
155 ST_SEPARATOR, // Receiving the separator between header name and value
156 ST_VALUE, // Receiving a request header value
157 ST_WS_READY, // Ready to receive web socket frame
158 ST_WS_FRAME, // Receiving WebSocket frame
159 ST_WS_CLOSE, // Closing the connection WebSocket connection
160 ST_DONE, // Parsing is complete and successful
161 ST_ERR, // Parsing encountered invalid syntax.
162 MAX_STATES
163 };
164
165 // State transition table
166 int parser_state[MAX_STATES][MAX_INPUTS] = {
167 /* METHOD */ { ST_URL, ST_ERR, ST_ERR, ST_ERR, ST_ERR, ST_ERR, ST_METHOD },
168 /* URL */ { ST_PROTO, ST_ERR, ST_ERR, ST_URL, ST_ERR, ST_ERR, ST_URL },
169 /* PROTOCOL */ { ST_ERR, ST_HEADER, ST_NAME, ST_ERR, ST_ERR, ST_ERR, ST_PROTO },
170 /* HEADER */ { ST_ERR, ST_ERR, ST_NAME, ST_ERR, ST_ERR, ST_ERR, ST_ERR },
171 /* NAME */ { ST_SEPARATOR, ST_DONE, ST_ERR, ST_SEPARATOR, ST_ERR, ST_ERR, ST_NAME },
172 /* SEPARATOR */ { ST_SEPARATOR, ST_ERR, ST_ERR, ST_SEPARATOR, ST_ERR, ST_ERR, ST_VALUE },
173 /* VALUE */ { ST_VALUE, ST_HEADER, ST_NAME, ST_VALUE, ST_ERR, ST_ERR, ST_VALUE },
174 /* WS_READY */ { ST_ERR, ST_ERR, ST_ERR, ST_ERR, ST_WS_FR AME, ST_WS_CLOSE, ST_ERR},
175 /* WS_FRAME */ { ST_WS_FRAME, ST_WS_FRAME, ST_WS_FRAME, ST_WS_FRAME, ST_ERR, ST_WS_READY, ST_WS_FRAME },
176 /* WS_CLOSE */ { ST_ERR, ST_ERR, ST_ERR, ST_ERR, ST_WS_CL OSE, ST_ERR, ST_ERR },
177 /* DONE */ { ST_DONE, ST_DONE, ST_DONE, ST_DONE, ST_DONE, ST_DONE, ST_DONE },
178 /* ERR */ { ST_ERR, ST_ERR, ST_ERR, ST_ERR, ST_ERR, ST_ERR, ST_ERR }
179 };
180
181 // Convert an input character to the parser's input token.
182 int charToInput(char ch) {
183 switch(ch) {
184 case ' ':
185 return INPUT_SPACE;
186 case '\r':
187 return INPUT_CR;
188 case '\n':
189 return INPUT_LF;
190 case ':':
191 return INPUT_COLON;
192 case 0x0:
193 return INPUT_00;
194 case -1:
195 return INPUT_FF;
196 }
197 return INPUT_DEFAULT;
198 }
199
200 HttpServerRequestInfo* HttpListenSocket::ParseHeaders() {
201 int pos = 0;
202 int data_len = recv_data_.length();
203 int state = is_web_socket_ ? ST_WS_READY : ST_METHOD;
204 scoped_ptr<HttpServerRequestInfo> info(new HttpServerRequestInfo());
205 std::string buffer;
206 std::string header_name;
207 std::string header_value;
208 while (pos < data_len) {
209 char ch = recv_data_[pos++];
210 int input = charToInput(ch);
211 int next_state = parser_state[state][input];
212
213 bool transition = (next_state != state);
214 if (transition) {
215 // Do any actions based on state transitions.
216 switch (state) {
217 case ST_METHOD:
218 info->method = buffer;
219 buffer.clear();
220 break;
221 case ST_URL:
222 info->path = buffer;
223 buffer.clear();
224 break;
225 case ST_PROTO:
226 // TODO(mbelshe): Deal better with parsing protocol.
227 DCHECK(buffer == "HTTP/1.1");
228 buffer.clear();
229 break;
230 case ST_NAME:
231 header_name = buffer;
232 buffer.clear();
233 break;
234 case ST_VALUE:
235 header_value = buffer;
236 // TODO(mbelshe): Deal better with duplicate headers
237 DCHECK(info->headers.find(header_name) == info->headers.end());
238 info->headers[header_name] = header_value;
239 buffer.clear();
240 break;
241 case ST_SEPARATOR:
242 buffer.append(&ch, 1);
243 break;
244 case ST_WS_FRAME:
245 recv_data_ = recv_data_.substr(pos);
246 info->data = buffer;
247 buffer.clear();
248 return info.release();
249 break;
250 }
251 state = next_state;
252 } else {
253 // Do any actions based on current state
254 switch (state) {
255 case ST_METHOD:
256 case ST_URL:
257 case ST_PROTO:
258 case ST_VALUE:
259 case ST_NAME:
260 case ST_WS_FRAME:
261 buffer.append(&ch, 1);
262 break;
263 case ST_DONE:
264 recv_data_ = recv_data_.substr(pos);
265 info->data = recv_data_;
266 recv_data_.clear();
267 return info.release();
268 case ST_WS_CLOSE:
269 is_web_socket_ = false;
270 return NULL;
271 case ST_ERR:
272 return NULL;
273 }
274 }
275 }
276 // No more characters, but we haven't finished parsing yet.
277 return NULL;
278 }
279
280 void HttpListenSocket::DidAccept(ListenSocket* server,
281 ListenSocket* connection) {
282 connection->AddRef();
283 }
284
285 void HttpListenSocket::DidRead(ListenSocket*,
286 const char* data,
287 int len) {
288 recv_data_.append(data, len);
289 while (recv_data_.length()) {
290 scoped_ptr<HttpServerRequestInfo> request(ParseHeaders());
291 if (!request.get())
292 break;
293
294 if (is_web_socket_) {
295 delegate_->OnWebSocketMessage(this, request->data);
296 continue;
297 }
298
299 std::string connection = GetHeaderValue(request.get(), "Connection");
300 if (connection == "Upgrade") {
301 // Is this WebSocket and if yes, upgrade the connection.
302 std::string key1 = GetHeaderValue(request.get(), "Sec-WebSocket-Key1");
303 std::string key2 = GetHeaderValue(request.get(), "Sec-WebSocket-Key2");
304 if (!key1.empty() && !key2.empty()) {
305 delegate_->OnWebSocketRequest(this, request.get());
306 continue;
307 }
308 }
309 delegate_->OnHttpRequest(this, request.get());
310 }
311 }
312
313 void HttpListenSocket::DidClose(ListenSocket* sock) {
314 sock->Release();
315 delegate_->OnClose(this);
316 }
OLDNEW
« no previous file with comments | « net/server/http_listen_socket.h ('k') | net/server/http_server_request_info.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698