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 #include "net/tools/fetch/http_listen_socket.h" 6 7 #include <map> 8 9 #include "base/compiler_specific.h" 10 #include "base/logging.h" 11 #include "base/message_loop.h" 12 #include "base/string_number_conversions.h" 13 #include "net/tools/fetch/http_server_request_info.h" 14 #include "net/tools/fetch/http_server_response_info.h" 15 16 // must run in the IO thread 17 HttpListenSocket::HttpListenSocket(SOCKET s, 18 HttpListenSocket::Delegate* delegate) 19 : ALLOW_THIS_IN_INITIALIZER_LIST(ListenSocket(s, this)), 20 delegate_(delegate) { 21 } 22 23 // must run in the IO thread 24 HttpListenSocket::~HttpListenSocket() { 25 } 26 27 void HttpListenSocket::Listen() { 28 ListenSocket::Listen(); 29 } 30 31 void HttpListenSocket::Accept() { 32 SOCKET conn = ListenSocket::Accept(socket_); 33 DCHECK_NE(conn, ListenSocket::kInvalidSocket); 34 if (conn == ListenSocket::kInvalidSocket) { 35 // TODO 36 } else { 37 scoped_refptr<HttpListenSocket> sock( 38 new HttpListenSocket(conn, delegate_)); 39 // it's up to the delegate to AddRef if it wants to keep it around 40 DidAccept(this, sock); 41 } 42 } 43 44 HttpListenSocket* HttpListenSocket::Listen( 45 const std::string& ip, 46 int port, 47 HttpListenSocket::Delegate* delegate) { 48 SOCKET s = ListenSocket::Listen(ip, port); 49 if (s == ListenSocket::kInvalidSocket) { 50 // TODO (ibrar): error handling 51 } else { 52 HttpListenSocket *serv = new HttpListenSocket(s, delegate); 53 serv->Listen(); 54 return serv; 55 } 56 return NULL; 57 } 58 59 // 60 // HTTP Request Parser 61 // This HTTP request parser uses a simple state machine to quickly parse 62 // through the headers. The parser is not 100% complete, as it is designed 63 // for use in this simple test driver. 64 // 65 // Known issues: 66 // - does not handle whitespace on first HTTP line correctly. Expects 67 // a single space between the method/url and url/protocol. 68 69 // Input character types. 70 enum header_parse_inputs { 71 INPUT_SPACE, 72 INPUT_CR, 73 INPUT_LF, 74 INPUT_COLON, 75 INPUT_DEFAULT, 76 MAX_INPUTS 77 }; 78 79 // Parser states. 80 enum header_parse_states { 81 ST_METHOD, // Receiving the method 82 ST_URL, // Receiving the URL 83 ST_PROTO, // Receiving the protocol 84 ST_HEADER, // Starting a Request Header 85 ST_NAME, // Receiving a request header name 86 ST_SEPARATOR, // Receiving the separator between header name and value 87 ST_VALUE, // Receiving a request header value 88 ST_DONE, // Parsing is complete and successful 89 ST_ERR, // Parsing encountered invalid syntax. 90 MAX_STATES 91 }; 92 93 // State transition table 94 int parser_state[MAX_STATES][MAX_INPUTS] = { 95 /* METHOD */ { ST_URL, ST_ERR, ST_ERR, ST_ERR, ST_METHOD }, 96 /* URL */ { ST_PROTO, ST_ERR, ST_ERR, ST_URL, ST_URL }, 97 /* PROTOCOL */ { ST_ERR, ST_HEADER, ST_NAME, ST_ERR, ST_PROTO }, 98 /* HEADER */ { ST_ERR, ST_ERR, ST_NAME, ST_ERR, ST_ERR }, 99 /* NAME */ { ST_SEPARATOR, ST_DONE, ST_ERR, ST_SEPARATOR, ST_NAME }, 100 /* SEPARATOR */ { ST_SEPARATOR, ST_ERR, ST_ERR, ST_SEPARATOR, ST_VALUE }, 101 /* VALUE */ { ST_VALUE, ST_HEADER, ST_NAME, ST_VALUE, ST_VALUE }, 102 /* DONE */ { ST_DONE, ST_DONE, ST_DONE, ST_DONE, ST_DONE }, 103 /* ERR */ { ST_ERR, ST_ERR, ST_ERR, ST_ERR, ST_ERR } 104 }; 105 106 // Convert an input character to the parser's input token. 107 int charToInput(char ch) { 108 switch(ch) { 109 case ' ': 110 return INPUT_SPACE; 111 case '\r': 112 return INPUT_CR; 113 case '\n': 114 return INPUT_LF; 115 case ':': 116 return INPUT_COLON; 117 } 118 return INPUT_DEFAULT; 119 } 120 121 HttpServerRequestInfo* HttpListenSocket::ParseHeaders() { 122 int pos = 0; 123 int data_len = recv_data_.length(); 124 int state = ST_METHOD; 125 HttpServerRequestInfo* info = new HttpServerRequestInfo(); 126 std::string buffer; 127 std::string header_name; 128 std::string header_value; 129 while (pos < data_len) { 130 char ch = recv_data_[pos++]; 131 int input = charToInput(ch); 132 int next_state = parser_state[state][input]; 133 134 bool transition = (next_state != state); 135 if (transition) { 136 // Do any actions based on state transitions. 137 switch (state) { 138 case ST_METHOD: 139 info->method = buffer; 140 buffer.clear(); 141 break; 142 case ST_URL: 143 info->url = GURL(buffer); 144 buffer.clear(); 145 break; 146 case ST_PROTO: 147 // TODO(mbelshe): Deal better with parsing protocol. 148 DCHECK(buffer == "HTTP/1.1"); 149 buffer.clear(); 150 break; 151 case ST_NAME: 152 header_name = buffer; 153 buffer.clear(); 154 break; 155 case ST_VALUE: 156 header_value = buffer; 157 // TODO(mbelshe): Deal better with duplicate headers 158 DCHECK(info->headers.find(header_name) == info->headers.end()); 159 info->headers[header_name] = header_value; 160 buffer.clear(); 161 break; 162 } 163 state = next_state; 164 } else { 165 // Do any actions based on current state 166 switch (state) { 167 case ST_METHOD: 168 case ST_URL: 169 case ST_PROTO: 170 case ST_VALUE: 171 case ST_NAME: 172 buffer.append(&ch, 1); 173 break; 174 case ST_DONE: 175 recv_data_ = recv_data_.substr(pos); 176 return info; 177 case ST_ERR: 178 delete info; 179 return NULL; 180 } 181 } 182 } 183 // No more characters, but we haven't finished parsing yet. 184 delete info; 185 return NULL; 186 } 187 188 void HttpListenSocket::DidAccept(ListenSocket* server, 189 ListenSocket* connection) { 190 connection->AddRef(); 191 } 192 193 void HttpListenSocket::DidRead(ListenSocket* connection, 194 const char* data, 195 int len) { 196 recv_data_.append(data, len); 197 while (recv_data_.length()) { 198 HttpServerRequestInfo* request = ParseHeaders(); 199 if (!request) 200 break; 201 delegate_->OnRequest(this, request); 202 delete request; 203 } 204 } 205 206 void HttpListenSocket::DidClose(ListenSocket* sock) { 207 sock->Release(); 208 } 209 210 // Convert the numeric status code to a string. 211 // e.g. 200 -> "200 OK" 212 std::string ServerStatus(int code) { 213 switch(code) { 214 case 200: 215 return std::string("200 OK"); 216 // TODO(mbelshe): handle other codes. 217 } 218 NOTREACHED(); 219 return std::string(); 220 } 221 222 void HttpListenSocket::Respond(HttpServerResponseInfo* info, 223 std::string& data) { 224 std::string response; 225 226 // status line 227 response = info->protocol + " "; 228 response += ServerStatus(info->status); 229 response += "\r\n"; 230 231 // standard headers 232 if (info->content_type.length()) 233 response += "Content-type: " + info->content_type + "\r\n"; 234 235 if (info->content_length > 0) 236 response += "Content-length: " + base::IntToString(info->content_length) + 237 "\r\n"; 238 239 if (info->connection_close) 240 response += "Connection: close\r\n"; 241 242 // TODO(mbelshe): support additional headers 243 244 // End of headers 245 response += "\r\n"; 246 247 // Add data 248 response += data; 249 250 // Write it all out. 251 this->Send(response, false); 252 } 253