Index: chrome_frame/test/test_server.cc |
=================================================================== |
--- chrome_frame/test/test_server.cc (revision 0) |
+++ chrome_frame/test/test_server.cc (revision 0) |
@@ -0,0 +1,211 @@ |
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "base/logging.h" |
+#include "base/registry.h" |
+#include "base/string_util.h" |
+ |
+#include "chrome_frame/test/test_server.h" |
+ |
+#include "net/base/winsock_init.h" |
+#include "net/http/http_util.h" |
+ |
+namespace test_server { |
+const char kDefaultHeaderTemplate[] = |
+ "HTTP/1.1 %hs\r\n" |
+ "Connection: close\r\n" |
+ "Content-Type: %hs\r\n" |
+ "Content-Length: %i\r\n\r\n"; |
+const char kStatusOk[] = "200 OK"; |
+const char kStatusNotFound[] = "404 Not Found"; |
+const char kDefaultContentType[] = "text/html; charset=UTF-8"; |
+ |
+void Request::ParseHeaders(const std::string& headers) { |
+ size_t pos = headers.find("\r\n"); |
+ DCHECK(pos != std::string::npos); |
+ if (pos != std::string::npos) { |
+ headers_ = headers.substr(pos + 2); |
+ |
+ StringTokenizer tokenizer(headers.begin(), headers.begin() + pos, " "); |
+ std::string* parse[] = { &method_, &path_, &version_ }; |
+ int field = 0; |
+ while (tokenizer.GetNext() && field < arraysize(parse)) { |
+ parse[field++]->assign(tokenizer.token_begin(), |
+ tokenizer.token_end()); |
+ } |
+ } |
+ |
+ // Check for content-length in case we're being sent some data. |
+ net::HttpUtil::HeadersIterator it(headers_.begin(), headers_.end(), |
+ "\r\n"); |
+ while (it.GetNext()) { |
+ if (LowerCaseEqualsASCII(it.name(), "content-length")) { |
+ content_length_ = StringToInt(it.values().c_str()); |
+ break; |
+ } |
+ } |
+} |
+ |
+bool Connection::CheckRequestReceived() { |
+ bool ready = false; |
+ if (request_.method().length()) { |
+ // Headers have already been parsed. Just check content length. |
+ ready = (data_.size() >= request_.content_length()); |
+ } else { |
+ size_t index = data_.find("\r\n\r\n"); |
+ if (index != std::string::npos) { |
+ // Parse the headers before returning and chop them of the |
+ // data buffer we've already received. |
+ std::string headers(data_.substr(0, index + 2)); |
+ request_.ParseHeaders(headers); |
+ data_.erase(0, index + 4); |
+ ready = (data_.size() >= request_.content_length()); |
+ } |
+ } |
+ |
+ return ready; |
+} |
+ |
+bool FileResponse::GetContentType(std::string* content_type) const { |
+ size_t length = ContentLength(); |
+ char buffer[4096]; |
+ void* data = NULL; |
+ |
+ if (length) { |
+ // Create a copy of the first few bytes of the file. |
+ // If we try and use the mapped file directly, FindMimeFromData will crash |
+ // 'cause it cheats and temporarily tries to write to the buffer! |
+ length = std::min(arraysize(buffer), length); |
+ memcpy(buffer, file_->data(), length); |
+ data = buffer; |
+ } |
+ |
+ LPOLESTR mime_type = NULL; |
+ FindMimeFromData(NULL, file_path_.value().c_str(), data, length, NULL, |
+ FMFD_DEFAULT, &mime_type, 0); |
+ if (mime_type) { |
+ *content_type = WideToASCII(mime_type); |
+ ::CoTaskMemFree(mime_type); |
+ } |
+ |
+ return content_type->length() > 0; |
+} |
+ |
+void FileResponse::WriteContents(ListenSocket* socket) const { |
+ DCHECK(file_.get()); |
+ if (file_.get()) { |
+ socket->Send(reinterpret_cast<const char*>(file_->data()), |
+ file_->length(), false); |
+ } |
+} |
+ |
+size_t FileResponse::ContentLength() const { |
+ if (file_.get() == NULL) { |
+ file_.reset(new file_util::MemoryMappedFile()); |
+ if (!file_->Initialize(file_path_)) { |
+ NOTREACHED(); |
+ file_.reset(); |
+ } |
+ } |
+ return file_.get() ? file_->length() : 0; |
+} |
+ |
+bool RedirectResponse::GetCustomHeaders(std::string* headers) const { |
+ *headers = StringPrintf("HTTP/1.1 302 Found\r\n" |
+ "Connection: close\r\n" |
+ "Content-Length: 0\r\n" |
+ "Content-Type: text/html\r\n" |
+ "Location: %hs\r\n\r\n", redirect_url_.c_str()); |
+ return true; |
+} |
+ |
+SimpleWebServer::SimpleWebServer(int port) { |
+ CHECK(MessageLoop::current()) << "SimpleWebServer requires a message loop"; |
+ net::EnsureWinsockInit(); |
+ AddResponse(&quit_); |
+ server_ = ListenSocket::Listen("127.0.0.1", port, this); |
+ DCHECK(server_.get() != NULL); |
+} |
+ |
+SimpleWebServer::~SimpleWebServer() { |
+ ConnectionList::const_iterator it; |
+ for (it = connections_.begin(); it != connections_.end(); it++) |
+ delete (*it); |
+ connections_.clear(); |
+} |
+ |
+void SimpleWebServer::AddResponse(Response* response) { |
+ responses_.push_back(response); |
+} |
+ |
+Response* SimpleWebServer::FindResponse(const Request& request) const { |
+ std::list<Response*>::const_iterator it; |
+ for (it = responses_.begin(); it != responses_.end(); it++) { |
+ Response* response = (*it); |
+ if (response->Matches(request)) { |
+ return response; |
+ } |
+ } |
+ return NULL; |
+} |
+ |
+Connection* SimpleWebServer::FindConnection(const ListenSocket* socket) const { |
+ ConnectionList::const_iterator it; |
+ for (it = connections_.begin(); it != connections_.end(); it++) { |
+ if ((*it)->IsSame(socket)) { |
+ return (*it); |
+ } |
+ } |
+ return NULL; |
+} |
+ |
+void SimpleWebServer::DidAccept(ListenSocket* server, |
+ ListenSocket* connection) { |
+ connections_.push_back(new Connection(connection)); |
+} |
+ |
+void SimpleWebServer::DidRead(ListenSocket* connection, |
+ const std::string& data) { |
+ Connection* c = FindConnection(connection); |
+ DCHECK(c); |
+ c->AddData(data); |
+ if (c->CheckRequestReceived()) { |
+ const Request& request = c->request(); |
+ Response* response = FindResponse(request); |
+ if (response) { |
+ std::string headers; |
+ if (!response->GetCustomHeaders(&headers)) { |
+ std::string content_type; |
+ if (!response->GetContentType(&content_type)) |
+ content_type = kDefaultContentType; |
+ headers = StringPrintf(kDefaultHeaderTemplate, kStatusOk, |
+ content_type.c_str(), response->ContentLength()); |
+ } |
+ |
+ connection->Send(headers, false); |
+ response->WriteContents(connection); |
+ response->IncrementAccessCounter(); |
+ } else { |
+ std::string payload = "sorry, I can't find " + request.path(); |
+ std::string headers(StringPrintf(kDefaultHeaderTemplate, kStatusNotFound, |
+ kDefaultContentType, payload.length())); |
+ connection->Send(headers, false); |
+ connection->Send(payload, false); |
+ } |
+ } |
+} |
+ |
+void SimpleWebServer::DidClose(ListenSocket* sock) { |
+ // To keep the historical list of connections reasonably tidy, we delete |
+ // 404's when the connection ends. |
+ Connection* c = FindConnection(sock); |
+ DCHECK(c); |
+ if (!FindResponse(c->request())) { |
+ // extremely inefficient, but in one line and not that common... :) |
+ connections_.erase(std::find(connections_.begin(), connections_.end(), c)); |
+ delete c; |
+ } |
+} |
+ |
+} // namespace test_server |