OSDN Git Service

Async HTTP GET is implemented. HTTP Header class is implemented.
authorAiwota Programmer <aiwotaprog@tetteke.tk>
Sun, 28 Jun 2009 11:42:20 +0000 (20:42 +0900)
committerAiwota Programmer <aiwotaprog@tetteke.tk>
Sun, 28 Jun 2009 11:42:20 +0000 (20:42 +0900)
12 files changed:
src/bbs_detail_base.cxx
src/bbs_detail_base.hxx
src/board_window.cxx
src/http_client.cxx [new file with mode: 0644]
src/http_client.hxx [new file with mode: 0644]
src/http_get.cxx
src/http_get.hxx
src/http_header.cxx [new file with mode: 0644]
src/http_header.hxx [new file with mode: 0644]
src/http_response.hxx [new file with mode: 0644]
src/http_status_line.hxx [new file with mode: 0644]
src/thread_window.cxx

index 90aa2f9..67c32e8 100644 (file)
@@ -38,7 +38,7 @@
 #include "html_parser.hxx"
 #include "buffer_builder.hxx"
 #include "board_subject_item.hxx"
-#include "http_get.hxx"
+#include "http_header.hxx"
 #include "convert/cp932.hxx"
 
 
@@ -230,7 +230,7 @@ void Base::load_subject_from_string(const std::string& subject,
 
 http::Header Base::get_board_subject_request_header() const {
   http::Header request_header;
-  request_header["Host"] = host_;
+  request_header.set_host(host_);
   return request_header;
 }
 
@@ -260,15 +260,11 @@ std::string Base::get_title_from_string(const std::string& dat) const {
 
 http::Header Base::get_thread_dat_request_header() const {
   http::Header request_header;
-  request_header.insert(std::make_pair("Host", host_));
+  request_header.set_host(host_);
 
   if (boost::filesystem::exists(get_thread_file_path())) {
     const size_t file_size = boost::filesystem::file_size(get_thread_file_path());
-    if (file_size > 0) {
-      std::string str = boost::lexical_cast<std::string>(file_size);
-      str = "bytes=" + str + "-";
-      request_header.insert(std::make_pair("Range", str));
-    }
+    if (file_size > 0) request_header.set_range(file_size);
   }
   return request_header;
 }
index f6e07bd..8967d96 100644 (file)
@@ -24,7 +24,7 @@
 #include <string>
 #include <vector>
 #include "board_subject_item.hxx"
-#include "http_get.hxx"
+#include "http_header.hxx"
 
 
 namespace dialektos {
index 6e79459..3e4decc 100644 (file)
@@ -229,9 +229,7 @@ void BoardWindow::on_action_view_refresh() {
 
   SubjectIdx idx = SubjectIdx::from_xml(
       boost::filesystem::path(bbs_->get_board_subject_idx_path()));
-  if (!idx.last_modified_.empty())
-    request_header.insert(
-        std::make_pair("If-Modified-Since", idx.last_modified_));
+  request_header.set_if_modified_since(idx.last_modified_);
 
   http_getter_.reset(new http::GetInThread(uri, request_header));
   http_getter_->signal_end().connect(
@@ -246,22 +244,21 @@ void BoardWindow::save_content(const http::Response& response) {
   ofs.close();
 
   // save the metadata to the file subject.xml
-  const http::Header& header = response.get_header();
-  http::Header::const_iterator it = header.find("Last-Modified");
-  if (it != header.end()) {
-    SubjectIdx idx = idx_;
-    idx.last_modified_ = it->second;
-    idx.to_xml(boost::filesystem::path(bbs_->get_board_subject_idx_path()));
-    idx_ = idx;
-  }
+  idx_.last_modified_ = response.get_header().get_last_modified();
+  idx_.to_xml(boost::filesystem::path(bbs_->get_board_subject_idx_path()));
 }
 
 void BoardWindow::on_http_get_end(bool success) {
 //  const std::string uri = http_getter_->get_uri();
 //  const http::Header request_header = http_getter_->get_request_header();
   const http::Response response = http_getter_->get_response();
-
+  const boost::system::error_code err = http_getter_->get_error();
   http_getter_.reset(0);
+  if (err) {
+    statusbar_.push(err.message());
+    return;
+  }
+
   on_refresh_end(response.get_status_line(), response.get_header());
 
   std::vector<SubjectItem> subjects;
@@ -281,10 +278,10 @@ void BoardWindow::on_http_get_end(bool success) {
 void BoardWindow::on_refresh_end(const http::StatusLine& status,
     const http::Header& header) {
   std::string message = status.get_line();
-  http::Header::const_iterator it = header.find("Last-Modified");
-  if (it != header.end()) {
+  const std::string last_modified = header.get_last_modified();
+  if (!last_modified.empty()) {
     message += " ";
-    message += it->second;
+    message += last_modified;
   }
   statusbar_.push(message);
 }
diff --git a/src/http_client.cxx b/src/http_client.cxx
new file mode 100644 (file)
index 0000000..3fbc4bf
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2009 by Aiwota Programmer
+ * aiwotaprog@tetteke.tk
+ *
+ * This file is part of Dialektos.
+ *
+ * Dialektos is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Dialektos is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Dialektos.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "http_client.hxx"
+
+#include <boost/asio.hpp>
+#include <boost/bind.hpp>
+#include <boost/algorithm/string.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <boost/foreach.hpp>
+#include <boost/format.hpp>
+#include <iostream>
+#include <istream>
+#include <ostream>
+#include <string>
+#include <map>
+#include "http_response.hxx"
+#include "http_status_line.hxx"
+
+
+namespace dialektos {
+
+namespace http {
+
+
+AsyncClient::AsyncClient(
+    boost::asio::io_service& io_service, const std::string& uri,
+    const Header& header) :
+      resolver_(io_service),
+      socket_(io_service),
+      request_(),
+      response_(),
+      content_(),
+      http_version_(),
+      status_code_(),
+      status_message_(),
+      response_header_(),
+      err_() {
+
+  const std::string host = header.get_host();
+  assert(!host.empty());
+
+  std::ostream stream(&request_);
+
+  stream << boost::format("GET %1% HTTP/1.0\r\n") % uri;
+
+  typedef std::map<std::string, std::string>::value_type PairType;
+  BOOST_FOREACH(const PairType& pair, header) {
+    stream << boost::format("%1%: %2%\r\n") % pair.first % pair.second;
+  }
+  stream << "\r\n" << std::flush;
+
+  boost::asio::ip::tcp::resolver::query query(host, "http");
+  resolver_.async_resolve(query,
+      boost::bind(&AsyncClient::handle_resolve, this,
+        boost::asio::placeholders::error,
+        boost::asio::placeholders::iterator));
+}
+
+void AsyncClient::handle_resolve(const boost::system::error_code& err,
+    boost::asio::ip::tcp::resolver::iterator endpoint_iterator) {
+  if (err) {
+    err_ = err;
+    return;
+  }
+
+  boost::asio::ip::tcp::endpoint endpoint = *endpoint_iterator;
+  socket_.async_connect(endpoint,
+      boost::bind(&AsyncClient::handle_connect, this,
+          boost::asio::placeholders::error, ++endpoint_iterator));
+}
+
+void AsyncClient::handle_connect(const boost::system::error_code& err,
+    boost::asio::ip::tcp::resolver::iterator endpoint_iterator) {
+  if (!err) {
+    boost::asio::async_write(socket_, request_,
+        boost::bind(&AsyncClient::handle_write_request, this,
+          boost::asio::placeholders::error));
+  } else if (endpoint_iterator != boost::asio::ip::tcp::resolver::iterator()) {
+    socket_.close();
+    boost::asio::ip::tcp::endpoint endpoint = *endpoint_iterator;
+    socket_.async_connect(endpoint,
+        boost::bind(&AsyncClient::handle_connect, this,
+          boost::asio::placeholders::error, ++endpoint_iterator));
+  } else {
+    err_= err;
+  }
+}
+
+void AsyncClient::handle_write_request(const boost::system::error_code& err) {
+  if (err) {
+    err_ = err;
+    return;
+  }
+
+  boost::asio::async_read_until(socket_, response_, "\r\n",
+      boost::bind(&AsyncClient::handle_read_status_line, this,
+          boost::asio::placeholders::error));
+}
+
+void AsyncClient::handle_read_status_line(
+    const boost::system::error_code& err) {
+  if (err) {
+    err_ = err;
+    return;
+  }
+
+  std::istream stream(&response_);
+
+  stream >> http_version_;
+  stream >> status_code_;
+
+  std::string status_message_;
+  std::getline(stream, status_message_);
+
+  if (!stream || !boost::algorithm::starts_with(http_version_, "HTTP/")) {
+    std::cerr << "response is not HTTP" << std::endl;
+    return;
+  }
+
+  boost::asio::async_read_until(socket_, response_, "\r\n\r\n",
+      boost::bind(&AsyncClient::handle_read_headers, this,
+          boost::asio::placeholders::error));
+}
+
+void AsyncClient::handle_read_headers(const boost::system::error_code& err) {
+  if (err) {
+    err_ = err;
+    return;
+  }
+
+  std::istream stream(&response_);
+  std::string line;
+  while (std::getline(stream, line)) {
+    boost::algorithm::trim_right(line);
+    response_header_.set_header_from_line(line);
+  }
+
+  if (response_.size() > 0)
+    content_ << &response_ << std::flush;
+
+  boost::asio::async_read(socket_, response_,
+      boost::asio::transfer_at_least(1),
+      boost::bind(&AsyncClient::handle_read_content, this,
+          boost::asio::placeholders::error));
+}
+
+void AsyncClient::handle_read_content(const boost::system::error_code& err)
+{
+  if (!err) {
+    content_ << &response_ << std::flush;
+
+    boost::asio::async_read(socket_, response_,
+        boost::asio::transfer_at_least(1),
+        boost::bind(&AsyncClient::handle_read_content, this,
+          boost::asio::placeholders::error));
+  } else if (err != boost::asio::error::eof) {
+    err_ = err;
+  }
+}
+
+Response AsyncClient::get_response() const {
+  std::stringstream ss;
+  ss << boost::format("%1% %2% %3%")
+    % http_version_ % status_code_ % status_message_;
+  std::string status_line = ss.str();
+
+  StatusLine status(http_version_, status_code_, status_message_, status_line);
+  return Response(status, response_header_, content_.str());
+}
+
+
+} // namespace http
+
+} // namespace dialektos
diff --git a/src/http_client.hxx b/src/http_client.hxx
new file mode 100644 (file)
index 0000000..b38f139
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2009 by Aiwota Programmer
+ * aiwotaprog@tetteke.tk
+ *
+ * This file is part of Dialektos.
+ *
+ * Dialektos is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Dialektos is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Dialektos.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef HTTP_CLIENT_HXX
+#define HTTP_CLIENT_HXX
+
+
+#include <boost/asio/io_service.hpp>
+#include <boost/asio/ip/tcp.hpp>
+#include <boost/asio/streambuf.hpp>
+#include <string>
+#include <sstream>
+#include "http_header.hxx"
+#include "http_response.hxx"
+
+
+namespace dialektos {
+
+namespace http {
+
+
+class AsyncClient {
+public:
+  AsyncClient(boost::asio::io_service& io_service,
+      const std::string& uri,
+      const Header& header);
+  Response get_response() const;
+  boost::system::error_code get_error() const { return err_; }
+private:
+  void handle_resolve(const boost::system::error_code& err,
+      boost::asio::ip::tcp::resolver::iterator endpoint_iterator);
+  void handle_connect(const boost::system::error_code& err,
+      boost::asio::ip::tcp::resolver::iterator endpoint_iterator);
+  void handle_write_request(const boost::system::error_code& err);
+  void handle_read_status_line(const boost::system::error_code& err);
+  void handle_read_headers(const boost::system::error_code& err);
+  void handle_read_content(const boost::system::error_code& err);
+
+  boost::asio::ip::tcp::resolver resolver_;
+  boost::asio::ip::tcp::socket socket_;
+  boost::asio::streambuf request_;
+  boost::asio::streambuf response_;
+  std::stringstream content_;
+  std::string http_version_;
+  int status_code_;
+  std::string status_message_;
+  Header response_header_;
+  boost::system::error_code err_;
+};
+
+
+} // namespace http
+
+} // namespace dialektos
+
+
+#endif
index 3f13ed0..fe5766b 100644 (file)
 #include "http_get.hxx"
 
 #include <sigc++/functors/mem_fun.h>
-#include <boost/xpressive/xpressive.hpp>
-#include <boost/algorithm/string.hpp>
-#include <boost/lexical_cast.hpp>
-#include <boost/asio.hpp>
-#include <boost/foreach.hpp>
-#include <boost/format.hpp>
+#include <boost/asio/io_service.hpp>
 #include <boost/thread/locks.hpp>
 #include <boost/thread/thread.hpp>
 #include <boost/function.hpp>
-#include <vector>
 #include <string>
-#include <sstream>
-#include <map>
-#include <iostream>
+#include "http_client.hxx"
 
 
 namespace dialektos {
@@ -43,95 +35,12 @@ namespace dialektos {
 namespace http {
 
 
-namespace {
-
-StatusLine from_string_to_status_line(const std::string& _line) {
-  using namespace boost::xpressive;
-  const sregex regex = "HTTP/"
-    >> (s1=_d >> '.' >> _d) >> _s
-    >> (s2=-repeat<3>(_d)) >> _s
-    >> (s3=*set[_w|_s]);
-
-  const std::string line = boost::algorithm::trim_right_copy(_line);
-  smatch what;
-  if (regex_match(line, what, regex)) {
-    const std::string version = what[1];
-    const int code = boost::lexical_cast<int>(what[2]);
-    const std::string message = what[3];
-    return StatusLine(version, code, message, line);
-  }
-  return StatusLine("", 0, "", line);
-}
-
-Header::value_type from_string_to_header_pair(const std::string& _line) {
-  using namespace boost::xpressive;
-  const sregex regex = (s1=+set[alnum | '-']) >> ":" >> _s >> (s2=-+_);
-
-  const std::string line = boost::algorithm::trim_right_copy(_line);
-  smatch what;
-  if (regex_match(line, what, regex)) {
-    const std::string name = what[1];
-    const std::string value = what[2];
-    return std::make_pair(name, value);
-  }
-  return std::make_pair("", "");
-}
-
-Response get(const std::string& uri, const Header& header) {
-
-  Header::const_iterator it = header.find("Host");
-  assert(it != header.end());
-  const std::string host = it->second;
-  if (host.empty()) return Response();
-
-  boost::asio::ip::tcp::iostream stream(host, "http");
-
-
-  stream << boost::format("GET %1% HTTP/1.0\r\n") % uri;
-  typedef Header::value_type PairType;
-  BOOST_FOREACH(const PairType& pair, header) {
-    stream << boost::format("%1%: %2%\r\n") % pair.first % pair.second;
-  }
-//  stream << "Connection: close\r\n";
-  stream << "\r\n";
-  stream << std::flush;
-
-  std::string line;
-  std::getline(stream, line);
-
-  StatusLine status = from_string_to_status_line(line);
-  if (status.get_version() != "0.9" &&
-      status.get_version() != "1.0" &&
-      status.get_version() != "1.1") {
-    std::cerr << "unknown version: (" << status.get_version() << ") "
-      << status.get_line() << std::endl;
-    return Response(status, Header(), "");
-  }
-
-  Header response_header;
-  while (std::getline(stream, line)) {
-    boost::algorithm::trim_right(line);
-    if (line.empty()) break;
-
-    Header::value_type field = from_string_to_header_pair(line);
-    response_header.insert(field);
-  }
-
-  std::stringstream ss;
-  ss << stream.rdbuf();
-  const std::string content = ss.str();
-
-  return Response(status, response_header, content);
-}
-
-} // namespace
-
-
 GetInThread::GetInThread(const std::string& uri,
     const Header& request_header) :
       dispatcher_(), signal_end_(),
       mutex_(), cancel_(false), thread_(),
-      uri_(uri), request_header_(request_header), response_() {
+      uri_(uri), request_header_(request_header), response_(), err_(),
+      io_service_() {
   dispatcher_.connect(sigc::mem_fun(*this, &GetInThread::on_end_dispather));
 }
 
@@ -144,7 +53,10 @@ GetInThread::~GetInThread() {
 
 void GetInThread::cancel() {
   boost::mutex::scoped_lock lock(mutex_);
-  cancel_ = true;
+  if (io_service_) {
+    io_service_->stop();
+    cancel_ = true;
+  }
 }
 
 bool GetInThread::run() {
@@ -163,13 +75,20 @@ struct ScopeExit {
 
 void GetInThread::operator()() {
   ScopeExit scope_exit(boost::ref(dispatcher_));
-  const Response response = get(uri_, request_header_);
-  response_.reset(new Response(response));
+  {
+    boost::mutex::scoped_lock lock(mutex_);
+    io_service_.reset(new boost::asio::io_service);
+  }
+  AsyncClient client(*io_service_, uri_, request_header_);
+  io_service_->run();
+  response_.reset(new Response(client.get_response()));
+  err_ = client.get_error();
 }
 
 void GetInThread::on_end_dispather() {
   thread_.reset(0);
-  if (!cancel_) signal_end_(true);
+  io_service_.reset(0);
+  signal_end_(!cancel_);
 }
 
 
index 5327d09..5ea0e2c 100644 (file)
 #include <boost/thread/mutex.hpp>
 #include <boost/thread/thread.hpp>
 #include <boost/scoped_ptr.hpp>
+#include <boost/asio/io_service.hpp>
 #include <string>
-#include <map>
+#include "http_header.hxx"
+#include "http_response.hxx"
 
 
 namespace dialektos {
 
 namespace http {
 
-typedef std::map<std::string, std::string> Header;
-
-struct StatusLine {
-  StatusLine() : version_(), code_(0), message_(), line_() {}
-  StatusLine(const std::string& version, int code, const std::string& message,
-      const std::string& line) :
-    version_(version), code_(code), message_(message), line_(line) {}
-
-  const std::string& get_version() const { return version_; }
-  int get_code() const { return code_; }
-  const std::string& get_message() const { return message_; }
-  const std::string& get_line() const { return line_; }
-
-private:
-  const std::string version_;
-  int code_;
-  const std::string message_;
-  const std::string line_;
-};
-
-struct Response {
-  Response() {}
-  Response(const StatusLine& status,
-      const Header& header, const std::string& content) :
-    status_(status),
-    header_(header),
-    content_(content) {}
-
-  const StatusLine& get_status_line() const { return status_; }
-  const Header& get_header() const { return header_; }
-  const std::string& get_content() const { return content_; }
-
-private:
-  const StatusLine status_;
-  const Header header_;
-  const std::string content_;
-};
-
 
 struct GetInThread {
   typedef sigc::signal<void, bool> SignalEndType;
@@ -89,6 +53,7 @@ struct GetInThread {
   const std::string& get_uri() const { return uri_;}
   const Header& get_request_header() const { return request_header_; }
   const Response& get_response() const { return *response_; }
+  boost::system::error_code get_error() const { return err_; }
 private:
   void on_end_dispather();
 
@@ -101,6 +66,8 @@ private:
   const std::string uri_;
   Header request_header_;
   boost::scoped_ptr<Response> response_;
+  boost::system::error_code err_;
+  boost::scoped_ptr<boost::asio::io_service> io_service_;
 };
 
 } // namespace http
diff --git a/src/http_header.cxx b/src/http_header.cxx
new file mode 100644 (file)
index 0000000..cbf3d56
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2009 by Aiwota Programmer
+ * aiwotaprog@tetteke.tk
+ *
+ * This file is part of Dialektos.
+ *
+ * Dialektos is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Dialektos is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Dialektos.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "http_header.hxx"
+
+#include <boost/xpressive/xpressive.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/algorithm/string.hpp>
+#include <string>
+#include <map>
+
+
+namespace dialektos {
+
+namespace http {
+
+
+Header::Header() : map_() {
+}
+
+Header::Header(const Header& rhs) : map_(rhs.map_) {
+}
+
+Header::~Header() {}
+
+void Header::set_header_from_line(const std::string& _line) {
+  using namespace boost::xpressive;
+  const sregex regex = (s1=+set[alnum | '-']) >> ":" >> _s >> (s2=-+_);
+
+  const std::string line = boost::algorithm::trim_right_copy(_line);
+  smatch what;
+  if (regex_match(line, what, regex)) {
+    const std::string name = what[1];
+    const std::string value = what[2];
+    set_header(name, value);
+  }
+}
+
+void Header::set_header(const std::string& name, const std::string& value) {
+  if (!value.empty()) map_[name] =  value;
+}
+
+void Header::set_host(const std::string& value) {
+  set_header("Host", value);
+}
+
+void Header::set_if_modified_since(const std::string& value) {
+  set_header("If-Modified-Since", value);
+}
+
+void Header::set_if_none_match(const std::string& value) {
+  set_header("If-None-Match", value);
+}
+
+void Header::set_range(const int value) {
+  const std::string text = boost::lexical_cast<std::string>(value);
+  set_header("Range", "bytes=" + text + "-");
+}
+
+std::string Header::get_header(const std::string& name) const {
+  MapType::const_iterator it = map_.find(name);
+  if (it != map_.end()) return it->second;
+  return "";
+}
+
+std::string Header::get_host() const {
+  return get_header("Host");
+}
+
+std::string Header::get_last_modified() const {
+  return get_header("Last-Modified");
+}
+
+std::string Header::get_etag() const {
+  return get_header("ETag");
+}
+
+std::string Header::get_range() const {
+  return get_header("Range");
+}
+
+
+} // namespace http
+
+} // namespace dialektos
diff --git a/src/http_header.hxx b/src/http_header.hxx
new file mode 100644 (file)
index 0000000..45c79c1
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2009 by Aiwota Programmer
+ * aiwotaprog@tetteke.tk
+ *
+ * This file is part of Dialektos.
+ *
+ * Dialektos is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Dialektos is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Dialektos.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef HTTP_HEADER_HXX
+#define HTTP_HEADER_HXX
+
+#include <string>
+#include <map>
+
+
+namespace dialektos {
+
+namespace http {
+
+
+struct Header {
+  typedef std::map<std::string, std::string> MapType;
+  typedef MapType::const_iterator const_iterator;
+  typedef MapType::value_type value_type;
+
+  Header();
+  Header(const Header& rhs);
+  ~Header();
+
+  const_iterator begin() const { return map_.begin(); }
+  const_iterator end() const { return map_.end(); }
+
+  void set_header_from_line(const std::string&);
+
+  void set_host(const std::string&);
+  void set_if_modified_since(const std::string&);
+  void set_if_none_match(const std::string&);
+  void set_range(int);
+
+  std::string get_host() const;
+  std::string get_last_modified() const;
+  std::string get_etag() const;
+  std::string get_range() const;
+
+private:
+  void set_header(const std::string&, const std::string&);
+  std::string get_header(const std::string&) const;
+
+  MapType map_;
+};
+
+
+} // namespace http
+
+} // namespace dialektos
+
+
+#endif
diff --git a/src/http_response.hxx b/src/http_response.hxx
new file mode 100644 (file)
index 0000000..306ce94
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2009 by Aiwota Programmer
+ * aiwotaprog@tetteke.tk
+ *
+ * This file is part of Dialektos.
+ *
+ * Dialektos is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Dialektos is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Dialektos.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef HTTP_RESPONSE_HXX
+#define HTTP_RESPONSE_HXX
+
+#include <string>
+#include "http_status_line.hxx"
+#include "http_header.hxx"
+
+
+namespace dialektos {
+
+namespace http {
+
+
+struct Response {
+  Response() {}
+  Response(const StatusLine& status,
+      const Header& header, const std::string& content) :
+    status_(status),
+    header_(header),
+    content_(content) {}
+
+  const StatusLine& get_status_line() const { return status_; }
+  const Header& get_header() const { return header_; }
+  const std::string& get_content() const { return content_; }
+
+private:
+  const StatusLine status_;
+  const Header header_;
+  const std::string content_;
+};
+
+
+} // namespace http
+
+} // namespace dialektos
+
+
+#endif
diff --git a/src/http_status_line.hxx b/src/http_status_line.hxx
new file mode 100644 (file)
index 0000000..a210e6f
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2009 by Aiwota Programmer
+ * aiwotaprog@tetteke.tk
+ *
+ * This file is part of Dialektos.
+ *
+ * Dialektos is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Dialektos is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Dialektos.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef HTTP_STATUS_LINE_HXX
+#define HTTP_STATUS_LINE_HXX
+
+#include <string>
+
+
+namespace dialektos {
+
+namespace http {
+
+
+struct StatusLine {
+  StatusLine() : version_(), code_(0), message_(), line_() {}
+  StatusLine(const std::string& version, int code, const std::string& message,
+      const std::string& line) :
+    version_(version), code_(code), message_(message), line_(line) {}
+
+  const std::string& get_version() const { return version_; }
+  int get_code() const { return code_; }
+  const std::string& get_message() const { return message_; }
+  const std::string& get_line() const { return line_; }
+
+private:
+  const std::string version_;
+  int code_;
+  const std::string message_;
+  const std::string line_;
+};
+
+
+} // namespace http
+
+} // namespace dialektos
+
+
+
+#endif
index f767ce7..c0df602 100644 (file)
@@ -87,12 +87,9 @@ void ThreadWindow::on_action_view_refresh() {
 
   const std::string uri = bbs_->get_thread_dat_uri();
   http::Header request_header = bbs_->get_thread_dat_request_header();
-  if (request_header.find("Range") != request_header.end()) {
-    if (!idx_.last_modified_.empty())
-      request_header.insert(
-          std::make_pair("If-Modified-Since", idx_.last_modified_));
-    if (!idx_.etag_.empty())
-      request_header.insert(std::make_pair("If-None-Match", idx_.etag_));
+  if (!request_header.get_range().empty()) {
+    request_header.set_if_modified_since(idx_.last_modified_);
+    request_header.set_if_none_match(idx_.etag_);
   }
 
   http_getter_.reset(new http::GetInThread(uri, request_header));
@@ -105,7 +102,13 @@ void ThreadWindow::on_http_get_end(bool success) {
 //  const std::string uri = http_getter_->get_uri();
 //  const http::Header request_header = http_getter_->get_request_header();
   const http::Response response = http_getter_->get_response();
+  const boost::system::error_code err = http_getter_->get_error();
   http_getter_.reset(0);
+  if (err) {
+    statusbar_.push(err.message());
+    return;
+  }
+
   on_refresh_end(response.get_status_line(), response.get_header());
 
   const int code = response.get_status_line().get_code();
@@ -121,10 +124,10 @@ void ThreadWindow::on_http_get_end(bool success) {
 void ThreadWindow::on_refresh_end(const http::StatusLine& status,
     const http::Header& header) {
   std::string message = status.get_line();
-  http::Header::const_iterator it = header.find("Last-Modified");
-  if (it != header.end()) {
+  const std::string last_modified = header.get_last_modified();
+  if (!last_modified.empty()) {
     message += " ";
-    message += it->second;
+    message += last_modified;
   }
   statusbar_.push(message);
 }
@@ -142,10 +145,8 @@ void ThreadWindow::save_content(const http::Response& response) {
   if (code == 200)
     idx_.title_ = bbs_->get_title_from_string(response.get_content());
 
-  http::Header::const_iterator it = response.get_header().find("Last-Modified");
-  if (it != response.get_header().end()) idx_.last_modified_ = it->second;
-  it = response.get_header().find("ETag");
-  if (it != response.get_header().end()) idx_.etag_ = it->second;
+  idx_.last_modified_ = response.get_header().get_last_modified();
+  idx_.etag_ = response.get_header().get_etag();
 
   idx_.line_count_ = text_view_.get_res_num();