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 17HttpListenSocket::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 24HttpListenSocket::~HttpListenSocket() { 25} 26 27void HttpListenSocket::Listen() { 28 ListenSocket::Listen(); 29} 30 31void 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 44HttpListenSocket* 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. 70enum 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. 80enum 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 94int 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. 107int 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 121HttpServerRequestInfo* 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 188void HttpListenSocket::DidAccept(ListenSocket* server, 189 ListenSocket* connection) { 190 connection->AddRef(); 191} 192 193void 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 206void HttpListenSocket::DidClose(ListenSocket* sock) { 207 sock->Release(); 208} 209 210// Convert the numeric status code to a string. 211// e.g. 200 -> "200 OK" 212std::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 222void 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