OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2006-2008 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/logging.h" |
| 6 #include "base/registry.h" |
| 7 #include "base/string_util.h" |
| 8 |
| 9 #include "chrome_frame/test/test_server.h" |
| 10 |
| 11 #include "net/base/winsock_init.h" |
| 12 #include "net/http/http_util.h" |
| 13 |
| 14 namespace test_server { |
| 15 const char kDefaultHeaderTemplate[] = |
| 16 "HTTP/1.1 %hs\r\n" |
| 17 "Connection: close\r\n" |
| 18 "Content-Type: %hs\r\n" |
| 19 "Content-Length: %i\r\n\r\n"; |
| 20 const char kStatusOk[] = "200 OK"; |
| 21 const char kStatusNotFound[] = "404 Not Found"; |
| 22 const char kDefaultContentType[] = "text/html; charset=UTF-8"; |
| 23 |
| 24 void Request::ParseHeaders(const std::string& headers) { |
| 25 size_t pos = headers.find("\r\n"); |
| 26 DCHECK(pos != std::string::npos); |
| 27 if (pos != std::string::npos) { |
| 28 headers_ = headers.substr(pos + 2); |
| 29 |
| 30 StringTokenizer tokenizer(headers.begin(), headers.begin() + pos, " "); |
| 31 std::string* parse[] = { &method_, &path_, &version_ }; |
| 32 int field = 0; |
| 33 while (tokenizer.GetNext() && field < arraysize(parse)) { |
| 34 parse[field++]->assign(tokenizer.token_begin(), |
| 35 tokenizer.token_end()); |
| 36 } |
| 37 } |
| 38 |
| 39 // Check for content-length in case we're being sent some data. |
| 40 net::HttpUtil::HeadersIterator it(headers_.begin(), headers_.end(), |
| 41 "\r\n"); |
| 42 while (it.GetNext()) { |
| 43 if (LowerCaseEqualsASCII(it.name(), "content-length")) { |
| 44 content_length_ = StringToInt(it.values().c_str()); |
| 45 break; |
| 46 } |
| 47 } |
| 48 } |
| 49 |
| 50 bool Connection::CheckRequestReceived() { |
| 51 bool ready = false; |
| 52 if (request_.method().length()) { |
| 53 // Headers have already been parsed. Just check content length. |
| 54 ready = (data_.size() >= request_.content_length()); |
| 55 } else { |
| 56 size_t index = data_.find("\r\n\r\n"); |
| 57 if (index != std::string::npos) { |
| 58 // Parse the headers before returning and chop them of the |
| 59 // data buffer we've already received. |
| 60 std::string headers(data_.substr(0, index + 2)); |
| 61 request_.ParseHeaders(headers); |
| 62 data_.erase(0, index + 4); |
| 63 ready = (data_.size() >= request_.content_length()); |
| 64 } |
| 65 } |
| 66 |
| 67 return ready; |
| 68 } |
| 69 |
| 70 bool FileResponse::GetContentType(std::string* content_type) const { |
| 71 size_t length = ContentLength(); |
| 72 char buffer[4096]; |
| 73 void* data = NULL; |
| 74 |
| 75 if (length) { |
| 76 // Create a copy of the first few bytes of the file. |
| 77 // If we try and use the mapped file directly, FindMimeFromData will crash |
| 78 // 'cause it cheats and temporarily tries to write to the buffer! |
| 79 length = std::min(arraysize(buffer), length); |
| 80 memcpy(buffer, file_->data(), length); |
| 81 data = buffer; |
| 82 } |
| 83 |
| 84 LPOLESTR mime_type = NULL; |
| 85 FindMimeFromData(NULL, file_path_.value().c_str(), data, length, NULL, |
| 86 FMFD_DEFAULT, &mime_type, 0); |
| 87 if (mime_type) { |
| 88 *content_type = WideToASCII(mime_type); |
| 89 ::CoTaskMemFree(mime_type); |
| 90 } |
| 91 |
| 92 return content_type->length() > 0; |
| 93 } |
| 94 |
| 95 void FileResponse::WriteContents(ListenSocket* socket) const { |
| 96 DCHECK(file_.get()); |
| 97 if (file_.get()) { |
| 98 socket->Send(reinterpret_cast<const char*>(file_->data()), |
| 99 file_->length(), false); |
| 100 } |
| 101 } |
| 102 |
| 103 size_t FileResponse::ContentLength() const { |
| 104 if (file_.get() == NULL) { |
| 105 file_.reset(new file_util::MemoryMappedFile()); |
| 106 if (!file_->Initialize(file_path_)) { |
| 107 NOTREACHED(); |
| 108 file_.reset(); |
| 109 } |
| 110 } |
| 111 return file_.get() ? file_->length() : 0; |
| 112 } |
| 113 |
| 114 bool RedirectResponse::GetCustomHeaders(std::string* headers) const { |
| 115 *headers = StringPrintf("HTTP/1.1 302 Found\r\n" |
| 116 "Connection: close\r\n" |
| 117 "Content-Length: 0\r\n" |
| 118 "Content-Type: text/html\r\n" |
| 119 "Location: %hs\r\n\r\n", redirect_url_.c_str()); |
| 120 return true; |
| 121 } |
| 122 |
| 123 SimpleWebServer::SimpleWebServer(int port) { |
| 124 CHECK(MessageLoop::current()) << "SimpleWebServer requires a message loop"; |
| 125 net::EnsureWinsockInit(); |
| 126 AddResponse(&quit_); |
| 127 server_ = ListenSocket::Listen("127.0.0.1", port, this); |
| 128 DCHECK(server_.get() != NULL); |
| 129 } |
| 130 |
| 131 SimpleWebServer::~SimpleWebServer() { |
| 132 ConnectionList::const_iterator it; |
| 133 for (it = connections_.begin(); it != connections_.end(); it++) |
| 134 delete (*it); |
| 135 connections_.clear(); |
| 136 } |
| 137 |
| 138 void SimpleWebServer::AddResponse(Response* response) { |
| 139 responses_.push_back(response); |
| 140 } |
| 141 |
| 142 Response* SimpleWebServer::FindResponse(const Request& request) const { |
| 143 std::list<Response*>::const_iterator it; |
| 144 for (it = responses_.begin(); it != responses_.end(); it++) { |
| 145 Response* response = (*it); |
| 146 if (response->Matches(request)) { |
| 147 return response; |
| 148 } |
| 149 } |
| 150 return NULL; |
| 151 } |
| 152 |
| 153 Connection* SimpleWebServer::FindConnection(const ListenSocket* socket) const { |
| 154 ConnectionList::const_iterator it; |
| 155 for (it = connections_.begin(); it != connections_.end(); it++) { |
| 156 if ((*it)->IsSame(socket)) { |
| 157 return (*it); |
| 158 } |
| 159 } |
| 160 return NULL; |
| 161 } |
| 162 |
| 163 void SimpleWebServer::DidAccept(ListenSocket* server, |
| 164 ListenSocket* connection) { |
| 165 connections_.push_back(new Connection(connection)); |
| 166 } |
| 167 |
| 168 void SimpleWebServer::DidRead(ListenSocket* connection, |
| 169 const std::string& data) { |
| 170 Connection* c = FindConnection(connection); |
| 171 DCHECK(c); |
| 172 c->AddData(data); |
| 173 if (c->CheckRequestReceived()) { |
| 174 const Request& request = c->request(); |
| 175 Response* response = FindResponse(request); |
| 176 if (response) { |
| 177 std::string headers; |
| 178 if (!response->GetCustomHeaders(&headers)) { |
| 179 std::string content_type; |
| 180 if (!response->GetContentType(&content_type)) |
| 181 content_type = kDefaultContentType; |
| 182 headers = StringPrintf(kDefaultHeaderTemplate, kStatusOk, |
| 183 content_type.c_str(), response->ContentLength()); |
| 184 } |
| 185 |
| 186 connection->Send(headers, false); |
| 187 response->WriteContents(connection); |
| 188 response->IncrementAccessCounter(); |
| 189 } else { |
| 190 std::string payload = "sorry, I can't find " + request.path(); |
| 191 std::string headers(StringPrintf(kDefaultHeaderTemplate, kStatusNotFound, |
| 192 kDefaultContentType, payload.length())); |
| 193 connection->Send(headers, false); |
| 194 connection->Send(payload, false); |
| 195 } |
| 196 } |
| 197 } |
| 198 |
| 199 void SimpleWebServer::DidClose(ListenSocket* sock) { |
| 200 // To keep the historical list of connections reasonably tidy, we delete |
| 201 // 404's when the connection ends. |
| 202 Connection* c = FindConnection(sock); |
| 203 DCHECK(c); |
| 204 if (!FindResponse(c->request())) { |
| 205 // extremely inefficient, but in one line and not that common... :) |
| 206 connections_.erase(std::find(connections_.begin(), connections_.end(), c)); |
| 207 delete c; |
| 208 } |
| 209 } |
| 210 |
| 211 } // namespace test_server |
OLD | NEW |