connection.cpp
Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2014, Willow Garage, Inc.
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions are met:
00007  *
00008  *     * Redistributions of source code must retain the above copyright
00009  *       notice, this list of conditions and the following disclaimer.
00010  *     * Redistributions in binary form must reproduce the above copyright
00011  *       notice, this list of conditions and the following disclaimer in the
00012  *       documentation and/or other materials provided with the distribution.
00013  *     * Neither the name of the Willow Garage, Inc. nor the names of its
00014  *       contributors may be used to endorse or promote products derived from
00015  *       this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00018  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00019  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00020  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
00021  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00022  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00023  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00024  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00025  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00026  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00027  * POSSIBILITY OF SUCH DAMAGE.
00028  *
00029  * connection.cpp
00030  *
00031  *  Created on: Oct 30, 2012
00032  *      Author: jkammerl
00033  *
00034  *  & partly:
00035  *  Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
00036  */
00037 
00038 #include "connection.h"
00039 
00040 #include <string>
00041 #include <vector>
00042 #include <fstream>
00043 #include <sstream>
00044 
00045 #include <boost/bind.hpp>
00046 #include <boost/foreach.hpp>
00047 #include <boost/lexical_cast.hpp>
00048 #include <boost/asio.hpp>
00049 #include <boost/any.hpp>
00050 #include <boost/algorithm/string.hpp>
00051 #include <boost/date_time/posix_time/posix_time.hpp>
00052 #include <boost/date_time/posix_time/posix_time_io.hpp>
00053 
00054 #include "ros/master.h"
00055 #include <sensor_msgs/image_encodings.h>
00056 #include <image_transport/image_transport.h>
00057 #include <image_transport/subscriber_filter.h>
00058 
00059 #define HTTP_TRANSFER_ENCODING
00060 
00061 namespace ros_http_video_streamer
00062 {
00063 
00064 namespace misc_strings {
00065 
00066 const char name_value_separator[] = { ':', ' ' };
00067 const char crlf[] = { '\r', '\n' };
00068 
00069 #ifdef HTTP_TRANSFER_ENCODING
00070 const std::string ok =
00071   "HTTP/1.1 200 OK\r\n";
00072 
00073 } // namespace misc_strings
00074 #else
00075 const std::string ok =
00076   "HTTP/1.0 200 OK\r\n";
00077 
00078 } // namespace misc_strings
00079 #endif
00080 
00081 struct mime_map
00082 {
00083   const char* extension;
00084   const char* mime_type;
00085 } mime_mapping[] =
00086 {
00087   { "gif", "image/gif" },
00088   { "htm", "text/html" },
00089   { "html", "text/html" },
00090   { "jpg", "image/jpeg" },
00091   { "png", "image/png" },
00092   { "css", "text/css" },
00093   { "js", "text/javascript" },
00094   { 0, 0 } // Marks end of list.
00095 };
00096 
00097 connection::connection(boost::asio::io_service& io_service,
00098                        EncoderManager& encoder_manager,
00099                        const ServerConfiguration& default_server_conf)
00100   : server_conf_(default_server_conf),
00101     image_topics_(),
00102     strand_(io_service),
00103     socket_(io_service),
00104     buffer_(),
00105     request_(),
00106     request_parser_(),
00107     reply_(),
00108     encoder_manager_(encoder_manager),
00109     streaming_thread_(),
00110     do_streaming_(false)
00111 {
00112 }
00113 
00114 connection::~connection()
00115 {
00116   do_streaming_ = false;
00117 
00118   if (streaming_thread_)
00119     streaming_thread_->join();
00120 
00121 }
00122 
00123 // Create an asio buffer containing the contents of a c string
00124 // This method stores all type bytes of the string excluding the null terminator
00125 // Passing a c string to the buffer function creates a buffer that includs the null terminator
00126 static boost::asio::const_buffer create_cstr_asio_buffer(const char* c_str)
00127 {
00128   return boost::asio::buffer(c_str, strlen(c_str));
00129 }
00130 
00131 // send http headers for data streaming
00132 void connection::sendHTTPStreamingHeaders()
00133 {
00134   std::vector<boost::asio::const_buffer> buffers;
00135 
00136   buffers.push_back(boost::asio::buffer(misc_strings::ok));
00137 
00138   buffers.push_back(boost::asio::buffer("Date"));
00139   buffers.push_back(boost::asio::buffer(misc_strings::name_value_separator));
00140 
00141   std::ostringstream datetime_ss;
00142   boost::posix_time::time_facet * p_time_output = new boost::posix_time::time_facet;
00143   std::locale special_locale (std::locale(""), p_time_output);
00144   datetime_ss.imbue (special_locale);
00145   (*p_time_output).format("%a, %d %b %Y %H:%M:%S%F %z"); // date time
00146   datetime_ss << boost::posix_time::second_clock::local_time();
00147 
00148   buffers.push_back(boost::asio::buffer(datetime_ss.str()));
00149   buffers.push_back(boost::asio::buffer(misc_strings::crlf));
00150 
00151   buffers.push_back(create_cstr_asio_buffer("Content-Type"));
00152   buffers.push_back(boost::asio::buffer(misc_strings::name_value_separator));
00153   buffers.push_back(create_cstr_asio_buffer("video/webm"));
00154   buffers.push_back(boost::asio::buffer(misc_strings::crlf));
00155 
00156 
00157   buffers.push_back(create_cstr_asio_buffer("Cache-Control"));
00158   buffers.push_back(boost::asio::buffer(misc_strings::name_value_separator));
00159   buffers.push_back(create_cstr_asio_buffer("no-cache"));
00160   buffers.push_back(boost::asio::buffer(misc_strings::crlf));
00161 
00162   buffers.push_back(create_cstr_asio_buffer("Connection"));
00163   buffers.push_back(boost::asio::buffer(misc_strings::name_value_separator));
00164   buffers.push_back(create_cstr_asio_buffer("Close"));
00165   buffers.push_back(boost::asio::buffer(misc_strings::crlf));
00166 
00167   buffers.push_back(create_cstr_asio_buffer("Access-Control-Allow-Origin"));
00168   buffers.push_back(boost::asio::buffer(misc_strings::name_value_separator));
00169   buffers.push_back(create_cstr_asio_buffer("*"));
00170   buffers.push_back(boost::asio::buffer(misc_strings::crlf));
00171 
00172   buffers.push_back(create_cstr_asio_buffer("Pragma"));
00173   buffers.push_back(boost::asio::buffer(misc_strings::name_value_separator));
00174   buffers.push_back(create_cstr_asio_buffer("no-cache"));
00175   buffers.push_back(boost::asio::buffer(misc_strings::crlf));
00176 
00177   buffers.push_back(create_cstr_asio_buffer("Expires"));
00178   buffers.push_back(boost::asio::buffer(misc_strings::name_value_separator));
00179   buffers.push_back(create_cstr_asio_buffer("0"));
00180   buffers.push_back(boost::asio::buffer(misc_strings::crlf));
00181 
00182   buffers.push_back(create_cstr_asio_buffer("Max-Age"));
00183   buffers.push_back(boost::asio::buffer(misc_strings::name_value_separator));
00184   buffers.push_back(create_cstr_asio_buffer("0"));
00185   buffers.push_back(boost::asio::buffer(misc_strings::crlf));
00186 
00187   buffers.push_back(create_cstr_asio_buffer("Trailer"));
00188   buffers.push_back(boost::asio::buffer(misc_strings::name_value_separator));
00189   buffers.push_back(create_cstr_asio_buffer("Expires"));
00190   buffers.push_back(boost::asio::buffer(misc_strings::crlf));
00191 
00192 
00193   #ifdef HTTP_TRANSFER_ENCODING
00194     buffers.push_back(create_cstr_asio_buffer("Transfer-Encoding"));
00195     buffers.push_back(boost::asio::buffer(misc_strings::name_value_separator));
00196     buffers.push_back(create_cstr_asio_buffer("chunked"));
00197     buffers.push_back(boost::asio::buffer(misc_strings::crlf));
00198   #endif
00199 
00200   buffers.push_back(boost::asio::buffer(misc_strings::crlf));
00201 
00202   boost::asio::write(socket_, buffers);
00203 }
00204 
00205 void connection::streamingWorkerThread( const std::string& topic,
00206                                         const ServerConfiguration& config)
00207 {
00208   FFMPEGEncoder image_encoder("WebEncoder", topic, config);
00209 
00210 #ifdef HTTP_TRANSFER_ENCODING
00211   char hexSize[256];
00212 #endif
00213 
00214    std::vector<uint8_t> header;
00215 
00216   if (image_encoder.initEncoding(header) >= 0)
00217   {
00218 
00219     std::vector<boost::asio::const_buffer> buffers;
00220     sendHTTPStreamingHeaders();
00221 
00222 #ifdef HTTP_TRANSFER_ENCODING
00223     sprintf(hexSize, "%X", (unsigned int)header.size());
00224     buffers.push_back(create_cstr_asio_buffer(hexSize));
00225     buffers.push_back(boost::asio::buffer(misc_strings::crlf));
00226 #endif
00227 
00228     buffers.push_back(boost::asio::buffer(header));
00229 #ifdef HTTP_TRANSFER_ENCODING
00230     buffers.push_back(boost::asio::buffer(misc_strings::crlf));
00231 #endif
00232 
00233     try
00234     {
00235       boost::asio::write(socket_, buffers);
00236       ROS_DEBUG("Video header sent (%d bytes)", (int) header.size());
00237 
00238       std::vector<uint8_t> packet;
00239 
00240       while (do_streaming_)
00241       {
00242         buffers.clear();
00243         packet.clear();
00244 
00245         image_encoder.getVideoPacket(packet);
00246         ROS_DEBUG("Video packet sent (%d bytes)", (int) packet.size());
00247 
00248 #ifdef HTTP_TRANSFER_ENCODING
00249         sprintf(hexSize, "%X", (unsigned int)packet.size());
00250         buffers.push_back(create_cstr_asio_buffer(hexSize));
00251         buffers.push_back(boost::asio::buffer(misc_strings::crlf));
00252 #endif
00253 
00254         buffers.push_back(boost::asio::buffer(packet));
00255 #ifdef HTTP_TRANSFER_ENCODING
00256         buffers.push_back(boost::asio::buffer(misc_strings::crlf));
00257 #endif
00258 
00259         boost::asio::write(socket_, buffers);
00260       }
00261     }
00262     catch (const boost::system::system_error& err)
00263     {
00264       do_streaming_ = false;
00265     }
00266 
00267   }
00268 
00269    streaming_thread_.reset();
00270 
00271    ROS_DEBUG("Video encoding thread endded");
00272 }
00273 
00274 void connection::getImageTopics()
00275 {
00276   image_topics_.clear();
00277 
00278   std::string image_message_type = ros::message_traits::datatype<sensor_msgs::Image>();
00279 
00280   ros::master::V_TopicInfo topics;
00281   ros::master::getTopics( topics );
00282 
00283   // Loop through all published topics
00284   ros::master::V_TopicInfo::iterator it;
00285   for( it = topics.begin(); it != topics.end(); ++it )
00286   {
00287     const ros::master::TopicInfo& topic = *it;
00288 
00289     // Only add topics whose type matches.
00290     if ( ( topic.datatype == image_message_type ) &&
00291           (topic.name.find("_raw")==std::string::npos ) )
00292     {
00293       image_topics_.push_back(topic.name);
00294     }
00295   }
00296 }
00297 
00298 
00299 void connection::generateImageTopicHTML()
00300 {
00301   reply_.content =
00302       "<html>"
00303       "<head><title>ROS Image Topic List</title></head>"
00304       "<body><h1>Available ROS Image Topics:</h1>";
00305 
00306   BOOST_FOREACH( const std::string& topic_name, image_topics_ )
00307   {
00308     reply_.content += "<p><a href=";
00309     reply_.content += WEB_PATH;
00310     reply_.content += topic_name+">"+topic_name+"</a></p>";
00311   }
00312   reply_.content +=
00313  //     "<a href=\"/webgl_pointcloud_streaming.html\">WebGL-based pointcloud decoder</a>"
00314       "</body>"
00315       "</html>";
00316 
00317   reply_.status = reply::ok;
00318   reply_.headers.resize(2);
00319   reply_.headers[0].name = "Content-Length";
00320   reply_.headers[0].value = boost::lexical_cast<std::string>(reply_.content.size());
00321   reply_.headers[1].name = "Content-Type";
00322   reply_.headers[1].value = "text/html";
00323 }
00324 
00325 void connection::generateVideoStreamHTML(const std::string& image_topic,
00326                                          const ServerConfiguration& config)
00327 {
00328   reply_.content =
00329 
00330       "<!DOCTYPE html>"
00331       "<html>"
00332       "<head>"
00333           "<meta charset=\"utf-8\">"
00334               "<title>ROS Topic: ";
00335 
00336   reply_.content += image_topic;
00337 
00338   reply_.content +=
00339                "</title>"
00340       "</head>"
00341       "<body>"
00342               "<div id=\"wrapper\">"
00343                               "<h1>Video stream of ROS topic: ";
00344 
00345   reply_.content += image_topic;
00346 
00347   reply_.content +=
00348                                "</h1>"
00349               "<div id=\"content\">"
00350 
00351                   "<div id=\"movie\">"
00352                           "<video src=\"";
00353 
00354   //reply_.content += server_conf_.address_+":"+boost::lexical_cast<std::string>(config.port_);
00355   reply_.content += STREAM_PATH;
00356   reply_.content += image_topic;
00357   reply_.content +=".webm?enc=";
00358   reply_.content +=config.codec_;
00359   reply_.content +="&bitrate=";
00360   reply_.content +=boost::lexical_cast<std::string>( config.bitrate_ );
00361   reply_.content +="&framerate=";
00362   reply_.content +=boost::lexical_cast<std::string>( config.framerate_ );
00363 
00364   reply_.content +=
00365 
00366                                       "\" autoplay=\"true\" preload=\"none\" >  "
00367                           "</video>"
00368                   "</div>"
00369               "</div>"
00370           "</div>"
00371       "</body>"
00372       "</html>";
00373 
00374   reply_.status = reply::ok;
00375   reply_.headers.resize(2);
00376   reply_.headers[0].name = "Content-Length";
00377   reply_.headers[0].value = boost::lexical_cast<std::string>(reply_.content.size());
00378   reply_.headers[1].name = "Content-Type";
00379   reply_.headers[1].value = "text/html";
00380 }
00381 
00382 void connection::getStreamingParametersFromURL(const std::string url, ServerConfiguration& config)
00383 {
00384   std::vector<std::string> parameters;
00385   boost::split(parameters,url,boost::is_any_of("&"));
00386   BOOST_FOREACH( const std::string& p, parameters )
00387   {
00388     std::vector<std::string> setting;
00389     boost::split(setting,p,boost::is_any_of("="));
00390 
00391     if (setting.size()==2)
00392     {
00393       try {
00394         if (!setting[0].compare("bitrate"))
00395         {
00396           config.bitrate_ = boost::lexical_cast<int>( setting[1] );
00397         } else
00398         if (!setting[0].compare("framerate"))
00399         {
00400           config.framerate_ = boost::lexical_cast<int>( setting[1] );
00401         } else
00402         if (!setting[0].compare("enc"))
00403         {
00404           config.codec_ = setting[1] ;
00405         } else
00406         if (!setting[0].compare("profile"))
00407         {
00408           config.profile_ = setting[1] ;
00409         } else
00410         if (!setting[0].compare("width"))
00411         {
00412           config.frame_width_ = boost::lexical_cast<int>( setting[1] );
00413         }
00414         else
00415         if (!setting[0].compare("height"))
00416         {
00417           config.frame_height_ = boost::lexical_cast<int>( setting[1] );
00418         }
00419         else
00420         if (!setting[0].compare("quality"))
00421         {
00422           config.quality_ = boost::lexical_cast<int>( setting[1] );
00423         }
00424         else
00425         if (!setting[0].compare("gop"))
00426         {
00427           config.gop_ = boost::lexical_cast<int>( setting[1] );
00428         } else
00429         {
00430           ROS_WARN_STREAM("Invalid configuration parameter in http request: "<<setting[1]);
00431         }
00432       } catch (boost::bad_lexical_cast& e)  {}
00433     }
00434   }
00435 
00436 }
00437 
00438 
00439 boost::asio::ip::tcp::socket& connection::socket()
00440 {
00441   return socket_;
00442 }
00443 
00444 void connection::start()
00445 {
00446 
00447   socket_.async_read_some(boost::asio::buffer(buffer_),
00448       strand_.wrap(
00449         boost::bind(&connection::handleRead, shared_from_this(),
00450           boost::asio::placeholders::error,
00451           boost::asio::placeholders::bytes_transferred)));
00452 
00453 }
00454 
00455 std::string connection::mimeExtensionToType(const std::string& extension)
00456 {
00457   for (mime_map* m = mime_mapping; m->extension; ++m)
00458   {
00459     if (m->extension == extension)
00460     {
00461       return m->mime_type;
00462     }
00463   }
00464 
00465   return "text/plain";
00466 }
00467 
00468 void connection::handleRead(const boost::system::error_code& e,
00469     std::size_t bytes_transferred)
00470 {
00471 
00472   if (!e)
00473   {
00474     boost::tribool result;
00475     boost::tie(result, boost::tuples::ignore) = request_parser_.parse(
00476         request_, buffer_.data(), buffer_.data() + bytes_transferred);
00477         
00478     const std::string remote_ip = boost::lexical_cast<std::string>(socket_.remote_endpoint());
00479 
00480     if (result)
00481     {
00482       ROS_DEBUG("Http request received from %s: %s", remote_ip.c_str(), request_.uri.c_str());
00483 
00484       std::string request_path;
00485       // update list of available image topics
00486       getImageTopics();
00487 
00488       if (!urlDecode(request_.uri, request_path))
00489       {
00490         reply_ = reply::stock_reply(reply::bad_request);
00491       } else
00492       if (request_path[request_path.size() - 1] == '/')
00493       {
00494         // generate a HTML page showing image topics
00495         generateImageTopicHTML();
00496       }
00497       else if (!request_path.empty() && request_path.find(STREAM_PATH) != std::string::npos)
00498       {
00499         std::string request_topic = request_path.substr(strlen(STREAM_PATH));
00500 
00501         std::string image_topic = request_topic.substr(0, request_topic.find(".webm"));
00502         std::string parameter_req = request_topic.substr(request_topic.find("?")+1);
00503 
00504         // check for requested stream
00505         bool stream_found = false;
00506         BOOST_FOREACH( const std::string& topic_name, image_topics_ )
00507         {
00508           if (!image_topic.compare(topic_name))
00509             stream_found = true;
00510         }
00511 
00512         if (stream_found)
00513         {
00514           ServerConfiguration config = server_conf_;
00515 
00516           // get stream parameters from URL
00517           getStreamingParametersFromURL(parameter_req, config);
00518 
00519           // start streaming thread
00520           do_streaming_ = true;
00521           streaming_thread_ = boost::shared_ptr<boost::thread>(
00522               new boost::thread( boost::bind(&connection::streamingWorkerThread, shared_from_this(), image_topic, config )  ) );
00523 
00524           ROS_INFO("Starting encoder for topic %s (codec: %s, bitrate: %d, framerate: %d)",
00525                    image_topic.c_str(),
00526                    config.codec_.c_str(),
00527                    config.bitrate_,
00528                    config.framerate_);
00529 
00530           return;
00531         }
00532       }
00533       else if (!request_path.empty() && request_path.find(WEB_PATH) != std::string::npos)
00534       {
00535         std::string request_topic = request_path.substr(strlen(WEB_PATH));
00536 
00537         std::string image_topic = request_topic.substr(0, request_topic.find("?"));
00538 
00539         bool stream_found = false;
00540         BOOST_FOREACH( const std::string& topic_name, image_topics_ )
00541         {
00542           if (!image_topic.compare(topic_name))
00543             stream_found = true;
00544         }
00545 
00546         if (stream_found)
00547         {
00548           ROS_INFO("Requesting www for %s", image_topic.c_str());
00549 
00550           // display HTML page that displays a video stream
00551           generateVideoStreamHTML(image_topic, server_conf_);
00552 
00553         } else
00554         {
00555           reply_ = reply::stock_reply(reply::not_found);
00556         }
00557       } else
00558       {
00559 
00560         if (server_conf_.www_file_server_)
00561         {        // Determine the file extension.
00562           std::size_t last_slash_pos = request_path.find_last_of("/");
00563           std::size_t last_dot_pos = request_path.find_last_of(".");
00564           std::string extension;
00565           if (last_dot_pos != std::string::npos && last_dot_pos > last_slash_pos)
00566           {
00567             extension = request_path.substr(last_dot_pos + 1);
00568           }
00569 
00570           // Open the file to send back.
00571           std::string full_path = server_conf_.wwwroot_ + request_path;
00572           std::ifstream is(full_path.c_str(), std::ios::in | std::ios::binary);
00573           if (!is)
00574           {
00575             reply_ = reply::stock_reply(reply::not_found);
00576             ROS_INFO("Http request from client %s: %s - file not found", remote_ip.c_str(), full_path.c_str());
00577             return;
00578           }
00579 
00580           // Fill out the reply to be sent to the client.
00581           reply_.status = reply::ok;
00582           char buf[2048];
00583           while (is.read(buf, sizeof(buf)).gcount() > 0)
00584             reply_.content.append(buf, is.gcount());
00585           reply_.headers.resize(2);
00586           reply_.headers[0].name = "Content-Length";
00587           reply_.headers[0].value = boost::lexical_cast < std::string > (reply_.content.size());
00588           reply_.headers[1].name = "Content-Type";
00589           reply_.headers[1].value = mimeExtensionToType(extension);
00590         } else
00591         {
00592           reply_ = reply::stock_reply(reply::forbidden);
00593           boost::asio::async_write(socket_, reply_.to_buffers(),
00594               strand_.wrap(
00595                 boost::bind(&connection::handleWrite, shared_from_this(),
00596                   boost::asio::placeholders::error)));
00597         }
00598       }
00599 
00600   //    request_handler_.handle_request(request_path, reply_);
00601       boost::asio::async_write(socket_, reply_.to_buffers(),
00602           strand_.wrap(
00603             boost::bind(&connection::handleWrite, shared_from_this(),
00604               boost::asio::placeholders::error)));
00605     }
00606     else if (!result)
00607     {
00608       reply_ = reply::stock_reply(reply::bad_request);
00609       boost::asio::async_write(socket_, reply_.to_buffers(),
00610           strand_.wrap(
00611             boost::bind(&connection::handleWrite, shared_from_this(),
00612               boost::asio::placeholders::error)));
00613     }
00614     else
00615     {
00616       socket_.async_read_some(boost::asio::buffer(buffer_),
00617           strand_.wrap(
00618             boost::bind(&connection::handleRead, shared_from_this(),
00619               boost::asio::placeholders::error,
00620               boost::asio::placeholders::bytes_transferred)));
00621     }
00622   } else
00623   {
00624     ROS_INFO("Http read error: %s", e.message().c_str());
00625   }
00626 
00627   // If an error occurs then no new asynchronous operations are started. This
00628   // means that all shared_ptr references to the connection object will
00629   // disappear and the object will be destroyed automatically after this
00630   // handler returns. The connection class's destructor closes the socket.
00631 }
00632 
00633 
00634 void connection::handleWrite(const boost::system::error_code& e)
00635 {
00636   if (!e)
00637   {
00638     // Initiate graceful connection closure.
00639     boost::system::error_code ignored_ec;
00640     socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec);
00641   }
00642 
00643   // No new asynchronous operations are started. This means that all shared_ptr
00644   // references to the connection object will disappear and the object will be
00645   // destroyed automatically after this handler returns. The connection class's
00646   // destructor closes the socket.
00647 }
00648 
00649 bool connection::urlDecode(const std::string& in, std::string& out)
00650 {
00651   out.clear();
00652   out.reserve(in.size());
00653   for (std::size_t i = 0; i < in.size(); ++i)
00654   {
00655     if (in[i] == '%')
00656     {
00657       if (i + 3 <= in.size())
00658       {
00659         int value = 0;
00660         std::istringstream is(in.substr(i + 1, 2));
00661         if (is >> std::hex >> value)
00662         {
00663           out += static_cast<char>(value);
00664           i += 2;
00665         }
00666         else
00667         {
00668           return false;
00669         }
00670       }
00671       else
00672       {
00673         return false;
00674       }
00675     }
00676     else if (in[i] == '+')
00677     {
00678       out += ' ';
00679     }
00680     else
00681     {
00682       out += in[i];
00683     }
00684   }
00685   return true;
00686 }
00687 
00688 } // ros_http_video_streamer


ros_web_video
Author(s): Julius Kammer
autogenerated on Thu Jun 6 2019 21:07:01