summaryrefslogtreecommitdiffstats
path: root/chrome_frame/test/test_server.cc
diff options
context:
space:
mode:
authorslightlyoff@chromium.org <slightlyoff@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-09-24 05:11:58 +0000
committerslightlyoff@chromium.org <slightlyoff@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-09-24 05:11:58 +0000
commitf781782dd67077478e117c61dca4ea5eefce3544 (patch)
tree4801f724123cfdcbb69c4e7fe40a565b331723ae /chrome_frame/test/test_server.cc
parent63cf4759efa2373e33436fb5df6849f930081226 (diff)
downloadchromium_src-f781782dd67077478e117c61dca4ea5eefce3544.zip
chromium_src-f781782dd67077478e117c61dca4ea5eefce3544.tar.gz
chromium_src-f781782dd67077478e117c61dca4ea5eefce3544.tar.bz2
Initial import of the Chrome Frame codebase. Integration in chrome.gyp coming in a separate CL.
BUG=None TEST=None Review URL: http://codereview.chromium.org/218019 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@27042 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome_frame/test/test_server.cc')
-rw-r--r--chrome_frame/test/test_server.cc211
1 files changed, 211 insertions, 0 deletions
diff --git a/chrome_frame/test/test_server.cc b/chrome_frame/test/test_server.cc
new file mode 100644
index 0000000..79ea2cf
--- /dev/null
+++ b/chrome_frame/test/test_server.cc
@@ -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