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