OLD | NEW |
(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 } |
OLD | NEW |