http_listen_socket.cc revision c7f5f8508d98d5952d42ed7648c2a8f30a4da156
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 14HttpListenSocket::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 21HttpListenSocket::~HttpListenSocket() { 22} 23 24void 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 37HttpListenSocket* 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. 61enum 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. 71enum 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 85int 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. 98int 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 112HttpServerRequestInfo* 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 179void HttpListenSocket::DidAccept(ListenSocket* server, 180 ListenSocket* connection) { 181 connection->AddRef(); 182} 183 184void 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 196void HttpListenSocket::DidClose(ListenSocket* sock) { 197 sock->Release(); 198} 199 200// Convert the numeric status code to a string. 201// e.g. 200 -> "200 OK" 202std::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 212void 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