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