<commit_before>//
//  libavg - Media Playback Engine.
//  Copyright (C) 2003-2008 Ulrich von Zadow
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2 of the License, or (at your option) any later version.
//
//  This library 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
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
//  Current versions can be found at www.libavg.de
//

#include "CameraNode.h"
#include "DisplayEngine.h"
#include "Player.h"
#include "ISurface.h"
#include "NodeDefinition.h"

#include "../base/TimeSource.h"
#include "../base/Logger.h"
#include "../base/Exception.h"
#include "../base/ScopeTimer.h"
#include "../base/XMLHelper.h"

#include "../imaging/FWCamera.h"
#ifdef AVG_ENABLE_V4L2
#include "../imaging/V4LCamera.h"
#endif
#ifdef _WIN32
#include "../imaging/DSCamera.h"
#endif

#include <iostream>
#include <sstream>
#include <unistd.h>

using namespace std;

namespace avg {

NodeDefinition CameraNode::getNodeDefinition()
{
    return NodeDefinition("camera", Node::buildNode<CameraNode>)
        .extendDefinition(VideoBase::getNodeDefinition())
        .addArg(Arg<string>("device", ""))
        .addArg(Arg<double>("framerate", 15))
        .addArg(Arg<string>("source", "firewire"))
        .addArg(Arg<int>("capturewidth", 640))
        .addArg(Arg<int>("captureheight", 480))
        .addArg(Arg<string>("pixelformat", "RGB"))
        .addArg(Arg<int>("channel", 0))
        .addArg(Arg<int>("brightness", -1))
        .addArg(Arg<int>("exposure", -1))
        .addArg(Arg<int>("sharpness", -1))
        .addArg(Arg<int>("saturation", -1))
        .addArg(Arg<int>("gamma", -1))
        .addArg(Arg<int>("shutter", -1))
        .addArg(Arg<int>("gain", -1))
        .addArg(Arg<int>("whitebalance", -1));
}

CameraNode::CameraNode(const ArgList& Args, Player * pPlayer, bool bFromXML)
    : VideoBase(pPlayer),
      m_FrameNum(0)
{
    Args.setMembers(this);
    string sDevice = Args.getArgVal<string>("device");
    double FrameRate = Args.getArgVal<double>("framerate");
    string sSource = Args.getArgVal<string>("source");
    int Width = Args.getArgVal<int>("capturewidth");
    int Height = Args.getArgVal<int>("captureheight");
    string sPF = Args.getArgVal<string>("pixelformat");

    if (sSource == "firewire") {
#if defined(AVG_ENABLE_1394)\
    || defined(AVG_ENABLE_1394_2)
    m_pCamera = CameraPtr(new FWCamera(sDevice, IntPoint(Width, Height), sPF,
            FrameRate, true));
#else
        AVG_TRACE(Logger::ERROR, "Firewire camera specified, but firewire "
                "support not compiled in.");
#endif
    } else if (sSource == "v4l") {
#if defined(AVG_ENABLE_V4L2)
        int Channel = Args.getArgVal<int>("channel");

        m_pCamera = CameraPtr(new V4LCamera(sDevice, Channel,
            IntPoint(Width, Height), sPF, true));
#else
        AVG_TRACE(Logger::ERROR, "Video4Linux camera specified, but "
                "Video4Linux support not compiled in.");
#endif
    } else if (sSource == "directshow") {
#if defined(_WIN32)
        m_pCamera = CameraPtr(new DSCamera(sDevice, IntPoint(Width, Height), sPF,
            FrameRate, true));
#else
        AVG_TRACE(Logger::ERROR, "DirectShow camera specified, but "
                "DirectShow is only available under windows.");
#endif
    } else {
        AVG_TRACE(Logger::ERROR,
            "Unable to set up camera. Camera source '"+sSource+"' unknown.");
    }

    if (m_pCamera) {
        m_pCamera->setFeature (CAM_FEATURE_BRIGHTNESS,
            Args.getArgVal<int>("brightness"));
        m_pCamera->setFeature (CAM_FEATURE_EXPOSURE,
            Args.getArgVal<int>("exposure"));
        m_pCamera->setFeature (CAM_FEATURE_SHARPNESS,
            Args.getArgVal<int>("sharpness"));
        m_pCamera->setFeature (CAM_FEATURE_SATURATION,
            Args.getArgVal<int>("saturation"));
        m_pCamera->setFeature (CAM_FEATURE_GAMMA,
            Args.getArgVal<int>("gamma"));
        m_pCamera->setFeature (CAM_FEATURE_SHUTTER,
            Args.getArgVal<int>("shutter"));
        m_pCamera->setFeature (CAM_FEATURE_GAIN,
            Args.getArgVal<int>("gain"));
        m_pCamera->setFeature (CAM_FEATURE_WHITE_BALANCE,
            Args.getArgVal<int>("whitebalance"));
    }
}

CameraNode::~CameraNode()
{
    close();
}

void CameraNode::setRenderingEngines(DisplayEngine * pDisplayEngine, AudioEngine * pAudioEngine)
{
    VideoBase::setRenderingEngines(pDisplayEngine, pAudioEngine);
}

string CameraNode::getTypeStr()
{
    return "Camera";
}

int CameraNode::getBrightness() const
{
    return getFeature (CAM_FEATURE_BRIGHTNESS);
}

void CameraNode::setBrightness(int Value)
{
    setFeature (CAM_FEATURE_BRIGHTNESS, Value);
}

int CameraNode::getExposure() const
{
    return getFeature (CAM_FEATURE_EXPOSURE);
}

void CameraNode::setExposure(int Value)
{
    setFeature (CAM_FEATURE_EXPOSURE, Value);
}

int CameraNode::getSharpness() const
{
    return getFeature (CAM_FEATURE_SHARPNESS);
}

void CameraNode::setSharpness(int Value)
{
    setFeature (CAM_FEATURE_SHARPNESS, Value);
}

int CameraNode::getSaturation() const
{
    return getFeature (CAM_FEATURE_SATURATION);
}

void CameraNode::setSaturation(int Value)
{
    setFeature (CAM_FEATURE_SATURATION, Value);
}

int CameraNode::getGamma() const
{
    return getFeature (CAM_FEATURE_GAMMA);
}

void CameraNode::setGamma(int Value)
{
    setFeature (CAM_FEATURE_GAMMA, Value);
}

int CameraNode::getShutter() const
{
    return getFeature (CAM_FEATURE_SHUTTER);
}

void CameraNode::setShutter(int Value)
{
    setFeature (CAM_FEATURE_SHUTTER, Value);
}

int CameraNode::getGain() const
{
    return getFeature (CAM_FEATURE_GAIN);
}

void CameraNode::setGain(int Value)
{
    setFeature (CAM_FEATURE_GAIN, Value);
}

unsigned int CameraNode::getWhiteBalance() const
{
    return getFeature (CAM_FEATURE_WHITE_BALANCE);
}

void CameraNode::setWhiteBalance(int Value)
{
    setFeature (CAM_FEATURE_WHITE_BALANCE, Value);
}

IntPoint CameraNode::getMediaSize()
{
    if (m_pCamera) {
        return m_pCamera->getImgSize();
    } else {
        return IntPoint(640,480);
    }
}

double CameraNode::getFPS()
{
    if (m_pCamera) {
        return m_pCamera->getFrameRate();
    } else {
        return 0;
    }
}

void CameraNode::open(YCbCrMode ycbcrMode)
{
    if (m_pCamera) {
        m_pCamera->open();
    }
}

void CameraNode::close()
{
    if (m_pCamera) {
        m_pCamera->close();
    }
}

unsigned int CameraNode::getFeature(CameraFeature Feature) const
{
    if (m_pCamera) {
        return m_pCamera->getFeature(Feature);
    } else {
        return 0;
    }
}

void CameraNode::setFeature(CameraFeature Feature, int Value)
{
    if (m_pCamera) {
        m_pCamera->setFeature(Feature, Value);
    }
}

int CameraNode::getFrameNum() const
{
    return m_FrameNum;
}

static ProfilingZone CameraFetchImage("Camera fetch image");
static ProfilingZone CameraProfilingZone("Camera::render");
static ProfilingZone CameraUploadProfilingZone("Camera tex download");

void CameraNode::preRender()
{
    if (m_pCamera) {
        ScopeTimer Timer(CameraFetchImage);
        pCurBmp = m_pCamera->getImage(false);
        if (pCurBmp) {
            BitmapPtr pTempBmp;
            while (pTempBmp = m_pCamera->getImage(false)) {
                pCurBmp = pTempBmp;
            }
            m_FrameNum++;
        }
    }
}

bool CameraNode::renderToSurface(ISurface * pSurface)
{
    if (m_pCamera) {
        ScopeTimer Timer(CameraProfilingZone);
        if (pCurBmp) {
            BitmapPtr pBmp = pSurface->lockBmp();
            assert(pBmp->getPixelFormat() == pCurBmp->getPixelFormat());
            pBmp->copyPixels(*pCurBmp);
            pSurface->unlockBmps();
            {
                ScopeTimer Timer(CameraUploadProfilingZone);
                getDisplayEngine()->surfaceChanged(pSurface);
            }
        }
    }
    return true;
}

PixelFormat CameraNode::getPixelFormat()
{
    return B8G8R8X8;
}

}
<commit_msg>File permission cosmetics.<commit_after>//
//  libavg - Media Playback Engine.
//  Copyright (C) 2003-2008 Ulrich von Zadow
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2 of the License, or (at your option) any later version.
//
//  This library 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
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
//  Current versions can be found at www.libavg.de
//

#include "CameraNode.h"
#include "DisplayEngine.h"
#include "Player.h"
#include "ISurface.h"
#include "NodeDefinition.h"

#include "../base/TimeSource.h"
#include "../base/Logger.h"
#include "../base/Exception.h"
#include "../base/ScopeTimer.h"
#include "../base/XMLHelper.h"

#include "../imaging/FWCamera.h"
#ifdef AVG_ENABLE_V4L2
#include "../imaging/V4LCamera.h"
#endif
#ifdef _WIN32
#include "../imaging/DSCamera.h"
#endif

#include <iostream>
#include <sstream>
#include <unistd.h>

using namespace std;

namespace avg {

NodeDefinition CameraNode::getNodeDefinition()
{
    return NodeDefinition("camera", Node::buildNode<CameraNode>)
        .extendDefinition(VideoBase::getNodeDefinition())
        .addArg(Arg<string>("device", ""))
        .addArg(Arg<double>("framerate", 15))
        .addArg(Arg<string>("source", "firewire"))
        .addArg(Arg<int>("capturewidth", 640))
        .addArg(Arg<int>("captureheight", 480))
        .addArg(Arg<string>("pixelformat", "RGB"))
        .addArg(Arg<int>("channel", 0))
        .addArg(Arg<int>("brightness", -1))
        .addArg(Arg<int>("exposure", -1))
        .addArg(Arg<int>("sharpness", -1))
        .addArg(Arg<int>("saturation", -1))
        .addArg(Arg<int>("gamma", -1))
        .addArg(Arg<int>("shutter", -1))
        .addArg(Arg<int>("gain", -1))
        .addArg(Arg<int>("whitebalance", -1));
}

CameraNode::CameraNode(const ArgList& Args, Player * pPlayer, bool bFromXML)
    : VideoBase(pPlayer),
      m_FrameNum(0)
{
    Args.setMembers(this);
    string sDevice = Args.getArgVal<string>("device");
    double FrameRate = Args.getArgVal<double>("framerate");
    string sSource = Args.getArgVal<string>("source");
    int Width = Args.getArgVal<int>("capturewidth");
    int Height = Args.getArgVal<int>("captureheight");
    string sPF = Args.getArgVal<string>("pixelformat");

    if (sSource == "firewire") {
#if defined(AVG_ENABLE_1394)\
    || defined(AVG_ENABLE_1394_2)
    m_pCamera = CameraPtr(new FWCamera(sDevice, IntPoint(Width, Height), sPF,
            FrameRate, true));
#else
        AVG_TRACE(Logger::ERROR, "Firewire camera specified, but firewire "
                "support not compiled in.");
#endif
    } else if (sSource == "v4l") {
#if defined(AVG_ENABLE_V4L2)
        int Channel = Args.getArgVal<int>("channel");

        m_pCamera = CameraPtr(new V4LCamera(sDevice, Channel,
            IntPoint(Width, Height), sPF, true));
#else
        AVG_TRACE(Logger::ERROR, "Video4Linux camera specified, but "
                "Video4Linux support not compiled in.");
#endif
    } else if (sSource == "directshow") {
#if defined(_WIN32)
        m_pCamera = CameraPtr(new DSCamera(sDevice, IntPoint(Width, Height), sPF,
            FrameRate, true));
#else
        AVG_TRACE(Logger::ERROR, "DirectShow camera specified, but "
                "DirectShow is only available under windows.");
#endif
    } else {
        AVG_TRACE(Logger::ERROR,
            "Unable to set up camera. Camera source '"+sSource+"' unknown.");
    }

    if (m_pCamera) {
        m_pCamera->setFeature (CAM_FEATURE_BRIGHTNESS,
            Args.getArgVal<int>("brightness"));
        m_pCamera->setFeature (CAM_FEATURE_EXPOSURE,
            Args.getArgVal<int>("exposure"));
        m_pCamera->setFeature (CAM_FEATURE_SHARPNESS,
            Args.getArgVal<int>("sharpness"));
        m_pCamera->setFeature (CAM_FEATURE_SATURATION,
            Args.getArgVal<int>("saturation"));
        m_pCamera->setFeature (CAM_FEATURE_GAMMA,
            Args.getArgVal<int>("gamma"));
        m_pCamera->setFeature (CAM_FEATURE_SHUTTER,
            Args.getArgVal<int>("shutter"));
        m_pCamera->setFeature (CAM_FEATURE_GAIN,
            Args.getArgVal<int>("gain"));
        m_pCamera->setFeature (CAM_FEATURE_WHITE_BALANCE,
            Args.getArgVal<int>("whitebalance"));
    }
}

CameraNode::~CameraNode()
{
    close();
}

void CameraNode::setRenderingEngines(DisplayEngine * pDisplayEngine, AudioEngine * pAudioEngine)
{
    VideoBase::setRenderingEngines(pDisplayEngine, pAudioEngine);
}

string CameraNode::getTypeStr()
{
    return "Camera";
}

int CameraNode::getBrightness() const
{
    return getFeature (CAM_FEATURE_BRIGHTNESS);
}

void CameraNode::setBrightness(int Value)
{
    setFeature (CAM_FEATURE_BRIGHTNESS, Value);
}

int CameraNode::getExposure() const
{
    return getFeature (CAM_FEATURE_EXPOSURE);
}

void CameraNode::setExposure(int Value)
{
    setFeature (CAM_FEATURE_EXPOSURE, Value);
}

int CameraNode::getSharpness() const
{
    return getFeature (CAM_FEATURE_SHARPNESS);
}

void CameraNode::setSharpness(int Value)
{
    setFeature (CAM_FEATURE_SHARPNESS, Value);
}

int CameraNode::getSaturation() const
{
    return getFeature (CAM_FEATURE_SATURATION);
}

void CameraNode::setSaturation(int Value)
{
    setFeature (CAM_FEATURE_SATURATION, Value);
}

int CameraNode::getGamma() const
{
    return getFeature (CAM_FEATURE_GAMMA);
}

void CameraNode::setGamma(int Value)
{
    setFeature (CAM_FEATURE_GAMMA, Value);
}

int CameraNode::getShutter() const
{
    return getFeature (CAM_FEATURE_SHUTTER);
}

void CameraNode::setShutter(int Value)
{
    setFeature (CAM_FEATURE_SHUTTER, Value);
}

int CameraNode::getGain() const
{
    return getFeature (CAM_FEATURE_GAIN);
}

void CameraNode::setGain(int Value)
{
    setFeature (CAM_FEATURE_GAIN, Value);
}

unsigned int CameraNode::getWhiteBalance() const
{
    return getFeature (CAM_FEATURE_WHITE_BALANCE);
}

void CameraNode::setWhiteBalance(int Value)
{
    setFeature (CAM_FEATURE_WHITE_BALANCE, Value);
}

IntPoint CameraNode::getMediaSize()
{
    if (m_pCamera) {
        return m_pCamera->getImgSize();
    } else {
        return IntPoint(640,480);
    }
}

double CameraNode::getFPS()
{
    if (m_pCamera) {
        return m_pCamera->getFrameRate();
    } else {
        return 0;
    }
}

void CameraNode::open(YCbCrMode ycbcrMode)
{
    if (m_pCamera) {
        m_pCamera->open();
    }
}

void CameraNode::close()
{
    if (m_pCamera) {
        m_pCamera->close();
    }
}

unsigned int CameraNode::getFeature(CameraFeature Feature) const
{
    if (m_pCamera) {
        return m_pCamera->getFeature(Feature);
    } else {
        return 0;
    }
}

void CameraNode::setFeature(CameraFeature Feature, int Value)
{
    if (m_pCamera) {
        m_pCamera->setFeature(Feature, Value);
    }
}

int CameraNode::getFrameNum() const
{
    return m_FrameNum;
}

static ProfilingZone CameraFetchImage("Camera fetch image");
static ProfilingZone CameraProfilingZone("Camera::render");
static ProfilingZone CameraUploadProfilingZone("Camera tex download");

void CameraNode::preRender()
{
    if (m_pCamera) {
        ScopeTimer Timer(CameraFetchImage);
        pCurBmp = m_pCamera->getImage(false);
        if (pCurBmp) {
            BitmapPtr pTempBmp;
            while (pTempBmp = m_pCamera->getImage(false)) {
                pCurBmp = pTempBmp;
            }
            m_FrameNum++;
        }
    }
}

bool CameraNode::renderToSurface(ISurface * pSurface)
{
    if (m_pCamera) {
        ScopeTimer Timer(CameraProfilingZone);
        if (pCurBmp) {
            BitmapPtr pBmp = pSurface->lockBmp();
            assert(pBmp->getPixelFormat() == pCurBmp->getPixelFormat());
            pBmp->copyPixels(*pCurBmp);
            pSurface->unlockBmps();
            {
                ScopeTimer Timer(CameraUploadProfilingZone);
                getDisplayEngine()->surfaceChanged(pSurface);
            }
        }
    }
    return true;
}

PixelFormat CameraNode::getPixelFormat()
{
    return B8G8R8X8;
}

}
<|endoftext|>


<commit_before>// -*- mode:c++;coding:utf-8; -*- vim:ts=2:sw=2:expandtab:autoindent:filetype=cpp:enc=utf-8:
/*
The MIT License

Copyright (c) 2008, 2009 Flusspferd contributors (see "CONTRIBUTORS" or
                                       http://flusspferd.org/contributors.txt)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

#include "flusspferd/create.hpp"
#include "flusspferd/binary.hpp"
#include "flusspferd/tracer.hpp"
#include "flusspferd/modules.hpp"
#include "flusspferd/security.hpp"
#include "flusspferd/arguments.hpp"
#include "flusspferd/class_description.hpp"
#include "flusspferd/property_iterator.hpp"

#include <sstream>
#include <curl/curl.h>

#include <boost/ptr_container/ptr_unordered_map.hpp>
#include <boost/exception/get_error_info.hpp>
#include <boost/assign/ptr_map_inserter.hpp>
#include <boost/version.hpp>

#include <iostream>

using namespace flusspferd;

namespace {
	typedef boost::error_info<struct tag_curlcode, CURLcode> curlcode_info;

	struct exception
		: flusspferd::exception
	{
		exception(std::string const &what)
			: std::runtime_error(what), flusspferd::exception(what)
		{ }

		char const *what() const throw() {
			if(CURLcode const *code = ::boost::get_error_info<curlcode_info>(*this)) {
				std::string what_ = flusspferd::exception::what();
				what_ += ": ";
				what_ += curl_easy_strerror(*code);
				return what_.c_str();
			}
			else {
				return flusspferd::exception::what();
			}
		}
	};

  void global_init(long flags) {
    CURLcode ret = curl_global_init(flags);
		if(ret != 0) {
			throw flusspferd::exception(std::string("curl_global_init: ")
																	+ curl_easy_strerror(ret));
		}
  }

	class Easy;

	namespace {
		struct handle_option {
			virtual ~handle_option() =0;
			virtual value default_value() const { return value(); }
			virtual property_attributes default_attributes() const {
				return property_attributes();
			}
			virtual void handle(Easy &e, value const &v) const =0;
		};
		handle_option::~handle_option() { }

		typedef boost::ptr_unordered_map<std::string, handle_option> options_map_t;
		options_map_t const &get_options();
	}

	FLUSSPFERD_CLASS_DESCRIPTION
	(
	 EasyOpt,
	 (constructor_name, "EasyOpt")
	 (full_name, "cURL.Easy.EasyOpt")
	 (constructible, false)
	 )
	{
		Easy &parent;
	public:
		EasyOpt(flusspferd::object const &self, Easy &parent)
			: base_type(self), parent(parent)
		{	}

		static EasyOpt &create(Easy &p) {
			return flusspferd::create_native_object<EasyOpt>(object(), boost::ref(p));
		}
	protected:
		bool property_resolve(value const &id, unsigned access);
	};

  FLUSSPFERD_CLASS_DESCRIPTION
  (
   Easy,
   (constructor_name, "Easy")
   (full_name, "cURL.Easy")
   (methods,
    ("cleanup",  bind, cleanup)
    ("perform",  bind, perform)
    ("reset",    bind, reset)
    ("escape",   bind, escape)
    ("unescape", bind, unescape)
    ("valid",    bind, valid))
	 (properties,
		("options", getter, get_opt))
   )
  {
    CURL *handle;
		EasyOpt &opt;

		static size_t writefunction(void *ptr, size_t size, size_t nmemb, void *stream) {
			assert(stream);
			Easy &self = *reinterpret_cast<Easy*>(stream);
			byte_array data(object(),
											reinterpret_cast<byte_array::element_type*>(ptr),
											size*nmemb);
			arguments arg;
			arg.push_back(value(data));
			arg.push_back(value(size));
			object callback = self.opt.get_property("writeFunction").to_object();
			value v = callback.call(arg);
			return v.to_number();
		}

	protected:
		void trace(flusspferd::tracer &trc) {
			trc("options", opt);
		}

  public:
    CURL *data() { return handle; }
    bool valid() { return handle; }
    CURL *get() {
      if(!handle) {
        throw flusspferd::exception("CURL handle not valid!");
      }
      return handle;
    }
		EasyOpt &get_opt() {
			return opt;
		}

    Easy(flusspferd::object const &self, flusspferd::call_context&)
      : base_type(self), handle(curl_easy_init()), opt(EasyOpt::create(*this))
    {
      if(!handle) {
        throw flusspferd::exception("curl_easy_init");
      }
    }

    Easy(flusspferd::object const &self, CURL *hnd)
      : base_type(self), handle(hnd), opt(EasyOpt::create(*this))
    {
      assert(handle);
    }

    void cleanup() {
      if(handle) {
        curl_easy_cleanup(handle);
        handle = 0x0;
      }
    }
    ~Easy() { cleanup(); }

		/**
		 * Applies all options set in EasyOpt.
		 * This is called automatically.
		 * Shouldn't be necessary to call it manually!
		 *
		 * Not exported.
		 *
		 * Maybe this should be called by unwrap and be private ...
		 */
		void apply_options() {
			std::cout << "apply_options()\n";
			options_map_t const &options = get_options();
			for(property_iterator i = opt.begin();
					i != opt.end();
					++i)
			{
				std::cout << "\t" << i->to_std_string() << "\n";
				options_map_t::const_iterator option = options.find(i->to_std_string());
				if(option != options.end()) {
					option->second->handle(*this, opt.get_property(*i));
				}
			}
		}

    void perform() {
			apply_options();
			CURLcode res = curl_easy_perform(get());
			if(res != 0) {
				throw flusspferd::exception(std::string("curl_easy_perform: ") +
																		curl_easy_strerror(res));
			}
    }

    void reset() {
      curl_easy_reset(get());
    }

    std::string unescape(char const *input) {
      int len;
      char *const uesc = curl_easy_unescape(get(), input, 0, &len);
      if(!uesc) {
        throw flusspferd::exception("curl_easy_unescape");
      }
      std::string ret(uesc, len);
      curl_free(uesc);
      return ret;
    }

    std::string escape(char const *input) {
      char *const esc = curl_easy_escape(get(), input, 0);
      if(!esc) {
        throw flusspferd::exception("curl_easy_escape");
      }
      std::string ret(esc);
      curl_free(esc);
      return ret;
    }

    static Easy &create(CURL *hnd) {
      return flusspferd::create_native_object<Easy>(object(), hnd);
    }

		//private:
		template<typename T>
		void do_setopt(CURLoption what, T data) {
			std::cout << "setopt (" << (unsigned)what << ' ' << data << '\n'; // DEBUG
			CURLcode res = curl_easy_setopt(get(), what, data);
			if(res != 0) {
				throw flusspferd::exception(std::string("curl_easy_setopt: ") +
																		curl_easy_strerror(res));
			}
		}

		void do_setopt_function(CURLoption what, bool default_) {
			if(default_) {
				do_setopt(what, 0x0);
			}
			else {
				switch(what) {
				case CURLOPT_WRITEFUNCTION:
					do_setopt(CURLOPT_WRITEFUNCTION, &writefunction);
					do_setopt(CURLOPT_WRITEDATA, this);
					break;
				default:
					throw flusspferd::exception("unkown function option");
				};
			}
		}
  };
  Easy &wrap(CURL *hnd) {
    return Easy::create(hnd);
  }
  CURL *unwrap(Easy &c) {
    return c.data();
  }

	namespace {
		struct integer_option : handle_option {
			integer_option(CURLoption what) : what(what) { }
			value default_value() const { return value(0); }
			void handle(Easy &e, value const &v) const {
				e.do_setopt(what, v.to_number());
			}
  	private:
			CURLoption what;
		};

		struct string_option : handle_option {
			string_option(CURLoption what) : what(what) { }
			value default_value() const { return value(""); }
			void handle(Easy &e, value const &v) const {
				e.do_setopt(what, v.to_std_string().c_str()); // TODO this won't work! string needs to be stored.
			}
		private:
			CURLoption what;
		};

		struct function_option : handle_option {
			function_option(CURLoption what) : what(what) { }
			void handle(Easy &e, value const &v) const {
				e.do_setopt_function(what, v.is_undefined_or_null());
			}
		private:
			CURLoption what;
		};

		options_map_t const &get_options() {
			static options_map_t map;
			if(map.empty()) {
				boost::assign::ptr_map_insert<integer_option>(map)
					("verbose", CURLOPT_VERBOSE)
					("header", CURLOPT_HEADER)
					("noProgress", CURLOPT_NOPROGRESS)
					("noSignal", CURLOPT_NOSIGNAL);
				boost::assign::ptr_map_insert<function_option>(map)
					("writeFunction", CURLOPT_WRITEFUNCTION);
				boost::assign::ptr_map_insert<string_option>(map)
					("url", CURLOPT_URL);
			}
			return map;
		}
	}

	bool EasyOpt::property_resolve(value const &id, unsigned) {
		std::string const name = id.to_std_string();
		options_map_t::const_iterator const i = get_options().find(name);
		if(i != get_options().end()) {
			define_property(id.get_string(), i->second->default_value(),
											i->second->default_attributes());
			return true;
		}
		else {
			return false;
		}
	}

  FLUSSPFERD_LOADER_SIMPLE(cURL) {
    local_root_scope scope;

    cURL.define_property("GLOBAL_ALL", value(CURL_GLOBAL_ALL),
                         read_only_property | permanent_property);
    cURL.define_property("GLOBAL_SSL", value(CURL_GLOBAL_SSL),
                         read_only_property | permanent_property);
    cURL.define_property("GLOBAL_WIN32", value(CURL_GLOBAL_WIN32),
                         read_only_property | permanent_property);
    cURL.define_property("GLOBAL_NOTHING", value(CURL_GLOBAL_NOTHING),
                         read_only_property | permanent_property);
    create_native_function(cURL, "globalInit", &global_init);
    cURL.define_property("version", value(curl_version()),
                         read_only_property | permanent_property);
		load_class<EasyOpt>(cURL);
    load_class<Easy>(cURL);
  }
}
<commit_msg>curl: continued implementing options. New internal API. Simple example working<commit_after>// -*- mode:c++;coding:utf-8; -*- vim:ts=2:sw=2:expandtab:autoindent:filetype=cpp:enc=utf-8:
/*
The MIT License

Copyright (c) 2008, 2009 Flusspferd contributors (see "CONTRIBUTORS" or
                                       http://flusspferd.org/contributors.txt)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

#include "flusspferd/create.hpp"
#include "flusspferd/binary.hpp"
#include "flusspferd/tracer.hpp"
#include "flusspferd/modules.hpp"
#include "flusspferd/security.hpp"
#include "flusspferd/arguments.hpp"
#include "flusspferd/class_description.hpp"
#include "flusspferd/property_iterator.hpp"

#include <sstream>
#include <curl/curl.h>

#include <boost/ptr_container/ptr_unordered_map.hpp>
#include <boost/exception/get_error_info.hpp>
#include <boost/assign/ptr_map_inserter.hpp>
#include <boost/any.hpp>

//#include <iostream> //DEBUG

using namespace flusspferd;

namespace {
	typedef boost::error_info<struct tag_curlcode, CURLcode> curlcode_info;

	struct exception
		: flusspferd::exception
	{
		exception(std::string const &what)
			: std::runtime_error(what), flusspferd::exception(what)
		{ }

		char const *what() const throw() {
			if(CURLcode const *code = ::boost::get_error_info<curlcode_info>(*this)) {
				std::string what_ = flusspferd::exception::what();
				what_ += ": ";
				what_ += curl_easy_strerror(*code);
				return what_.c_str();
			}
			else {
				return flusspferd::exception::what();
			}
		}
	};

  void global_init(long flags) {
    CURLcode ret = curl_global_init(flags);
		if(ret != 0) {
			throw flusspferd::exception(std::string("curl_global_init: ")
																	+ curl_easy_strerror(ret));
		}
  }

	class Easy;

	namespace {
		struct handle_option {
			virtual ~handle_option() =0;
			virtual function getter() const =0;
			virtual function setter() const =0;
			virtual boost::any data() const =0;
			virtual CURLoption what() const =0;
		};
		handle_option::~handle_option() { }

		typedef boost::ptr_unordered_map<std::string, handle_option> options_map_t;
		options_map_t const &get_options();
	}

	FLUSSPFERD_CLASS_DESCRIPTION
	(
	 EasyOpt,
	 (constructor_name, "EasyOpt")
	 (full_name, "cURL.Easy.EasyOpt")
	 (constructible, false)
	 )
	{
	public: // TODO
		typedef boost::unordered_map<CURLoption, boost::any> data_map_t;
		data_map_t data;
		Easy &parent;
	public:
		EasyOpt(flusspferd::object const &self, Easy &parent)
			: base_type(self), parent(parent)
		{	}

		static EasyOpt &create(Easy &p) {
			return flusspferd::create_native_object<EasyOpt>(object(), boost::ref(p));
		}
	protected:
		bool property_resolve(value const &id, unsigned access);
	};

  FLUSSPFERD_CLASS_DESCRIPTION
  (
   Easy,
   (constructor_name, "Easy")
   (full_name, "cURL.Easy")
   (methods,
    ("cleanup",  bind, cleanup)
    ("perform",  bind, perform)
    ("reset",    bind, reset)
    ("escape",   bind, escape)
    ("unescape", bind, unescape)
    ("valid",    bind, valid))
	 (properties,
		("options", getter, get_opt))
   )
  {
    CURL *handle;
		EasyOpt &opt;

	public: // TODO
		object writefunction_callback;
		static size_t writefunction(void *ptr, size_t size, size_t nmemb, void *stream) {
			//std::cout << "writefunction" << std::endl;
			assert(stream);
			Easy &self = *reinterpret_cast<Easy*>(stream);
			if(self.writefunction_callback.is_null()) {
				//std::cout << "writefunction*1" << std::endl;
				return 0;
			}
			else {
				//std::cout << "writefunction^2" << std::endl;
				byte_array data(object(),
												reinterpret_cast<byte_array::element_type*>(ptr),
												size*nmemb);
				arguments arg;
				arg.push_back(value(data));
				arg.push_back(value(size));
				//std::cout << "writefunction^3" << std::endl;
				value v = self.writefunction_callback.call(arg);
				//std::cout << "writefunction^4" << std::endl;
				return v.to_number();
			}
		}

	protected:
		void trace(flusspferd::tracer &trc) {
			trc("options", opt);
		}

  public:
    CURL *data() { return handle; }
    bool valid() { return handle; }
    CURL *get() {
      if(!handle) {
        throw flusspferd::exception("CURL handle not valid!");
      }
      return handle;
    }
		EasyOpt &get_opt() {
			return opt;
		}

    Easy(flusspferd::object const &self, flusspferd::call_context&)
      : base_type(self), handle(curl_easy_init()), opt(EasyOpt::create(*this))
    {
      if(!handle) {
        throw flusspferd::exception("curl_easy_init");
      }
    }

    Easy(flusspferd::object const &self, CURL *hnd)
      : base_type(self), handle(hnd), opt(EasyOpt::create(*this))
    {
      assert(handle);
    }

    void cleanup() {
      if(handle) {
        curl_easy_cleanup(handle);
        handle = 0x0;
      }
    }
    ~Easy() { cleanup(); }

    void perform() {
			CURLcode res = curl_easy_perform(get());
			if(res != 0) {
				throw flusspferd::exception(std::string("curl_easy_perform: ") +
																		curl_easy_strerror(res));
			}
    }

    void reset() {
      curl_easy_reset(get());
    }

    std::string unescape(char const *input) {
      int len;
      char *const uesc = curl_easy_unescape(get(), input, 0, &len);
      if(!uesc) {
        throw flusspferd::exception("curl_easy_unescape");
      }
      std::string ret(uesc, len);
      curl_free(uesc);
      return ret;
    }

    std::string escape(char const *input) {
      char *const esc = curl_easy_escape(get(), input, 0);
      if(!esc) {
        throw flusspferd::exception("curl_easy_escape");
      }
      std::string ret(esc);
      curl_free(esc);
      return ret;
    }

    static Easy &create(CURL *hnd) {
      return flusspferd::create_native_object<Easy>(object(), hnd);
    }

		//private:
		template<typename T>
		void do_setopt(CURLoption what, T data) {
			CURLcode res = curl_easy_setopt(get(), what, data);
			if(res != 0) {
				throw flusspferd::exception(std::string("curl_easy_setopt: ") +
																		curl_easy_strerror(res));
			}
		}
  };
  Easy &wrap(CURL *hnd) {
    return Easy::create(hnd);
  }
  CURL *unwrap(Easy &c) {
    return c.data();
  }

	namespace {
		template<CURLoption What>
		struct integer_option : handle_option {
			function getter() const {
				return create_native_method(object(), "$get_", &get);
			}
			function setter() const {
				return create_native_method(object(), "$set_", &set);
			}
			boost::any data() const { return 0l; }
			CURLoption what() const { return What; }
  	private:
			static long get(EasyOpt *o) {
				assert(o);
				return boost::any_cast<long>(o->data[What]);
			}
			static void set(EasyOpt *o, long opt) {
				assert(o);
				o->data[What] = opt;
				o->parent.do_setopt(What, opt);
			}
		};

		template<CURLoption What>
		struct string_option : handle_option {
			function getter() const {
				return create_native_method(object(), "$get_", &get);
			}
			function setter() const {
				return create_native_method(object(), "$set_", &set);
			}
			boost::any data() const { return std::string(); }
			CURLoption what() const { return What; }
		private:
			static std::string get(EasyOpt *o) {
				assert(o);
				return boost::any_cast<std::string>(o->data[What]);
			}
			static void set(EasyOpt *o, std::string const &val) {
				assert(o);
				o->data[What] = val;
				o->parent.do_setopt(What, boost::any_cast<std::string&>(o->data[What]).c_str());
			}
		};

		struct write_function_option : handle_option {
			static CURLoption const What = CURLOPT_WRITEFUNCTION;
			function getter() const {
				return create_native_method(object(), "$get_writeFunction", &get);
			}
			function setter() const {
				return create_native_method(object(), "$set_writeFunction", &set);
			}
			boost::any data() const { return function(); }
			CURLoption what() const { return What; }
		private:
			static object get(EasyOpt *o) {
				assert(o);
				return o->parent.writefunction_callback;
			}
			static void set(EasyOpt *o, object val) {
				assert(o);
				o->parent.writefunction_callback = val;
				if(val.is_null()) {
					o->parent.do_setopt(CURLOPT_WRITEFUNCTION, 0x0);
				}
				else {
					o->parent.do_setopt(CURLOPT_WRITEFUNCTION, &Easy::writefunction);
					o->parent.do_setopt(CURLOPT_WRITEDATA, &o->parent);
				}
			}
		};

		options_map_t const &get_options() {
			static options_map_t map;
			if(map.empty()) {
				using namespace boost::assign;
				ptr_map_insert< integer_option<CURLOPT_VERBOSE> >(map)
					("verbose");
				ptr_map_insert< integer_option<CURLOPT_HEADER> >(map)
					("header");
				ptr_map_insert< integer_option<CURLOPT_NOPROGRESS> >(map)
					("noProgress");
				ptr_map_insert< integer_option<CURLOPT_NOSIGNAL> >(map)
					("noSignal");
				ptr_map_insert< write_function_option >(map)
					("writeFunction");
				ptr_map_insert< string_option<CURLOPT_URL> >(map)("url");
			}
			return map;
		}
	}

	bool EasyOpt::property_resolve(value const &id, unsigned) {
		std::string const name = id.to_std_string();
		options_map_t::const_iterator const i = get_options().find(name);
		if(i != get_options().end()) {
			property_attributes attr(no_property_flag,
															 i->second->getter(),
															 i->second->setter());
			define_property(id.get_string(), value(), attr);
			data[i->second->what()] = i->second->data();
			return true;
		}
		else {
			return false;
		}
	}

  FLUSSPFERD_LOADER_SIMPLE(cURL) {
    local_root_scope scope;

    cURL.define_property("GLOBAL_ALL", value(CURL_GLOBAL_ALL),
                         read_only_property | permanent_property);
    cURL.define_property("GLOBAL_SSL", value(CURL_GLOBAL_SSL),
                         read_only_property | permanent_property);
    cURL.define_property("GLOBAL_WIN32", value(CURL_GLOBAL_WIN32),
                         read_only_property | permanent_property);
    cURL.define_property("GLOBAL_NOTHING", value(CURL_GLOBAL_NOTHING),
                         read_only_property | permanent_property);
    create_native_function(cURL, "globalInit", &global_init);
    cURL.define_property("version", value(curl_version()),
                         read_only_property | permanent_property);
		load_class<EasyOpt>(cURL);
    load_class<Easy>(cURL);
  }
}
<|endoftext|>


<commit_before>// -*- mode:c++;coding:utf-8; -*- vim:ts=2:sw=2:expandtab:autoindent:filetype=cpp:enc=utf-8:
/*
The MIT License

Copyright (c) 2008, 2009 Flusspferd contributors (see "CONTRIBUTORS" or
                                       http://flusspferd.org/contributors.txt)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

#include "flusspferd/create.hpp"
#include "flusspferd/binary.hpp"
#include "flusspferd/tracer.hpp"
#include "flusspferd/modules.hpp"
#include "flusspferd/security.hpp"
#include "flusspferd/arguments.hpp"
#include "flusspferd/class_description.hpp"

#include <sstream>
#include <curl/curl.h>

#include <boost/exception/get_error_info.hpp>

using namespace flusspferd;

namespace {
	typedef boost::error_info<struct tag_curlcode, CURLcode> curlcode_info;

	struct exception
		: flusspferd::exception
	{
		exception(std::string const &what)
			: std::runtime_error(what), flusspferd::exception(what)
		{ }

		char const *what() const throw() {
			if(CURLcode const *code = ::boost::get_error_info<curlcode_info>(*this)) {
				std::string what_ = flusspferd::exception::what();
				what_ += ": ";
				what_ += curl_easy_strerror(*code);
				return what_.c_str();
			}
			else {
				return flusspferd::exception::what();
			}
		}
	};

  void global_init(long flags) {
    CURLcode ret = curl_global_init(flags);
		if(ret != 0) {
			throw flusspferd::exception(std::string("curl_global_init: ") + curl_easy_strerror(ret));
		}
  }

	class Easy;

	FLUSSPFERD_CLASS_DESCRIPTION
	(
	 EasyOpt,
	 (constructor_name, "EasyOpt")
	 (full_name, "cURL.Easy.EasyOpt")
	 (constructible, false)
	 )
	{
		Easy &parent;
	public:
		EasyOpt(flusspferd::object const &self, Easy &parent) : base_type(self), parent(parent) { }

		// workarround
		EasyOpt(flusspferd::object const &self, Easy const &parent)
			: base_type(self), parent(const_cast<Easy&>(parent))
		{ }

		static EasyOpt &create(Easy &p) {
			return flusspferd::create_native_object<EasyOpt>(object(), p);
		}
	protected:
		bool property_resolve(value const &id, unsigned access);
	};

  FLUSSPFERD_CLASS_DESCRIPTION
  (
   Easy,
   (constructor_name, "Easy")
   (full_name, "cURL.Easy")
   (methods,
    ("cleanup",  bind, cleanup)
    ("perform",  bind, perform)
    ("reset",    bind, reset)
    ("escape",   bind, escape)
    ("unescape", bind, unescape)
    ("setopt",   bind, setopt)
    ("valid",    bind, valid))
	 (properties,
		("options", getter, get_opt))
   )
  {
    CURL *handle;
		EasyOpt &opt;

		object writecallback;
		static size_t writefunction(void *ptr, size_t size, size_t nmemb, void *stream) {
			assert(stream);
			Easy &self = *reinterpret_cast<Easy*>(stream);
			byte_array data(object(),
											reinterpret_cast<byte_array::element_type*>(ptr),
											size*nmemb);
			arguments arg;
			arg.push_back(value(data));
			arg.push_back(value(size));
			value v = self.writecallback.call(arg);
			return v.to_number();
		}
	protected:
		void trace(flusspferd::tracer &trc) {
			trc("writecallback", writecallback);
		}

  public:
    CURL *data() { return handle; }
    bool valid() { return handle; }
    CURL *get() {
      if(!handle) {
        throw flusspferd::exception("CURL handle not valid!");
      }
      return handle;
    }
		EasyOpt &get_opt() { return opt; }

    Easy(flusspferd::object const &self, flusspferd::call_context&)
      : base_type(self), handle(curl_easy_init()), opt(EasyOpt::create(*this))
    {
      if(!handle) {
        throw flusspferd::exception("curl_easy_init");
      }
    }

    Easy(flusspferd::object const &self, CURL *hnd)
      : base_type(self), handle(hnd), opt(EasyOpt::create(*this))
    {
      assert(handle);
    }

    void cleanup() {
      if(handle) {
        curl_easy_cleanup(handle);
        handle = 0x0;
      }
    }
    ~Easy() { cleanup(); }

    void perform() {
			CURLcode res = curl_easy_perform(get());
			if(res != 0) {
				throw flusspferd::exception(std::string("curl_easy_setopt: ") +
																		curl_easy_strerror(res));
			}
    }

    void reset() {
      curl_easy_reset(get());
    }

    std::string unescape(char const *input) {
      int len;
      char *const uesc = curl_easy_unescape(get(), input, 0, &len);
      if(!uesc) {
        throw flusspferd::exception("curl_easy_escape");
      }
      std::string ret(uesc, len);
      curl_free(uesc);
      return ret;
    }

    std::string escape(char const *input) {
      char *const esc = curl_easy_escape(get(), input, 0);
      if(!esc) {
        throw flusspferd::exception("curl_easy_escape");
      }
      std::string ret(esc);
      curl_free(esc);
      return ret;
    }

#define OPT_NUMBER(What)                                                \
    case What :                                                         \
      if(x.arg.size() != 2) {                                           \
        std::stringstream ss;                                           \
        ss << "curl_easy_setopt: Expected two arguments. Got (" << x.arg.size() << ')'; \
        throw flusspferd::exception(ss.str());                          \
      }                                                                 \
      do_setopt(What, x.arg[1].to_number());														\
      break

#define OPT_FUNCTION(What, Data, Callback, Func)												\
		case What : {																												\
			if(x.arg.size() != 2) {																						\
				throw flusspferd::exception("curl_easy_setopt: Expected two arguments"); \
			}																																	\
			if(!x.arg[1].is_object()) {																				\
				throw flusspferd::exception("curl_easy_setopt: Expected a function as second parameter");	\
			}																																	\
			object callback = x.arg[1].get_object();													\
			if(callback == default_function) {																\
				do_setopt(What, 0x0);																						\
				Callback = object();																						\
			}																																	\
			else {																														\
				do_setopt(Data, this);																					\
				do_setopt(What, Func);																					\
				Callback = callback;																						\
			}																																	\
		}																																		\
		break

#define OPT_DATAFUNCTION(What, Prefix)																	\
		OPT_FUNCTION( What ## FUNCTION, What ## DATA, Prefix ## callback, Prefix ## function )

	private:
		template<typename T>
		void do_setopt(CURLoption what, T data) {
		  CURLcode res = curl_easy_setopt(get(), what, data);
			if(res != 0) {
				throw flusspferd::exception(std::string("curl_easy_setopt: ") +
																		curl_easy_strerror(res));
			}
		}
	public:

    void setopt(flusspferd::call_context &x) {
      int what = x.arg.front().to_number();
      switch(what) {
				// Behaviour Options
				OPT_NUMBER(CURLOPT_VERBOSE);
        OPT_NUMBER(CURLOPT_HEADER);
				OPT_NUMBER(CURLOPT_NOPROGRESS);
				OPT_NUMBER(CURLOPT_NOSIGNAL);

				// Callback Options
				OPT_DATAFUNCTION(CURLOPT_WRITE, write);
      default: {
        std::stringstream ss;
        ss << "curl_easy_setopt unkown or unsupported option (" << what << ')';
        throw flusspferd::exception(ss.str());
      }
      };
    }

#undef OPT_DATAFUNCTION
#undef OPT_FUNCTION
#undef OPT_NUMBER

    static Easy &create(CURL *hnd) {
      return flusspferd::create_native_object<Easy>(object(), hnd);
    }

		static object default_function;
  };

	object Easy::default_function;

  Easy &wrap(CURL *hnd) {
    return Easy::create(hnd);
  }
  CURL *unwrap(Easy &c) {
    return c.data();
  }

	bool EasyOpt::property_resolve(value const &id, unsigned) {
		(void)id;
		return false;
	}

  FLUSSPFERD_LOADER_SIMPLE(cURL) {
    local_root_scope scope;

    cURL.define_property("GLOBAL_ALL", value(CURL_GLOBAL_ALL),
                         read_only_property | permanent_property);
    cURL.define_property("GLOBAL_SSL", value(CURL_GLOBAL_SSL),
                         read_only_property | permanent_property);
    cURL.define_property("GLOBAL_WIN32", value(CURL_GLOBAL_WIN32),
                         read_only_property | permanent_property);
    cURL.define_property("GLOBAL_NOTHING", value(CURL_GLOBAL_NOTHING),
                         read_only_property | permanent_property);
    create_native_function(cURL, "globalInit", &global_init);
    cURL.define_property("version", value(curl_version()),
                         read_only_property | permanent_property);
		load_class<EasyOpt>(cURL);
    load_class<Easy>(cURL);
		// Behaviour Options
		cURL.define_property("OPT_VERBOSE", value((int)CURLOPT_VERBOSE),
												 read_only_property | permanent_property);
    cURL.define_property("OPT_HEADER", value((int)CURLOPT_HEADER),
                         read_only_property | permanent_property);
		cURL.define_property("OPT_NOPROGRESS", value((int)CURLOPT_NOPROGRESS),
												 read_only_property | permanent_property);
		cURL.define_property("OPT_NOSIGNAL", value((int)CURLOPT_NOSIGNAL),
												 read_only_property | permanent_property);
		// Callback Options
		cURL.define_property("OPT_WRITEFUNCTION", value((int)CURLOPT_WRITEFUNCTION),
                         read_only_property | permanent_property);
		/*
		CURLOPT_READFUNCTION
		CURLOPT_IOCTLFUNCTION
		CURLOPT_SEEKFUNCTION
		CURLOPT_SOCKOPTFUNCTION
		CURLOPT_OPENSOCKETFUNCTION
		...
		 */
		cURL.define_property("defaultFunction", value(Easy::default_function),
												 read_only_property | permanent_property);
  }
}
<commit_msg>curl: use boost::ref instead of workarround<commit_after>// -*- mode:c++;coding:utf-8; -*- vim:ts=2:sw=2:expandtab:autoindent:filetype=cpp:enc=utf-8:
/*
The MIT License

Copyright (c) 2008, 2009 Flusspferd contributors (see "CONTRIBUTORS" or
                                       http://flusspferd.org/contributors.txt)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

#include "flusspferd/create.hpp"
#include "flusspferd/binary.hpp"
#include "flusspferd/tracer.hpp"
#include "flusspferd/modules.hpp"
#include "flusspferd/security.hpp"
#include "flusspferd/arguments.hpp"
#include "flusspferd/class_description.hpp"

#include <sstream>
#include <curl/curl.h>

#include <boost/exception/get_error_info.hpp>

using namespace flusspferd;

namespace {
	typedef boost::error_info<struct tag_curlcode, CURLcode> curlcode_info;

	struct exception
		: flusspferd::exception
	{
		exception(std::string const &what)
			: std::runtime_error(what), flusspferd::exception(what)
		{ }

		char const *what() const throw() {
			if(CURLcode const *code = ::boost::get_error_info<curlcode_info>(*this)) {
				std::string what_ = flusspferd::exception::what();
				what_ += ": ";
				what_ += curl_easy_strerror(*code);
				return what_.c_str();
			}
			else {
				return flusspferd::exception::what();
			}
		}
	};

  void global_init(long flags) {
    CURLcode ret = curl_global_init(flags);
		if(ret != 0) {
			throw flusspferd::exception(std::string("curl_global_init: ") + curl_easy_strerror(ret));
		}
  }

	class Easy;

	FLUSSPFERD_CLASS_DESCRIPTION
	(
	 EasyOpt,
	 (constructor_name, "EasyOpt")
	 (full_name, "cURL.Easy.EasyOpt")
	 (constructible, false)
	 )
	{
		Easy &parent;
	public:
		EasyOpt(flusspferd::object const &self, Easy &parent) : base_type(self), parent(parent) { }

		static EasyOpt &create(Easy &p) {
			return flusspferd::create_native_object<EasyOpt>(object(), boost::ref(p));
		}
	protected:
		bool property_resolve(value const &id, unsigned access);
	};

  FLUSSPFERD_CLASS_DESCRIPTION
  (
   Easy,
   (constructor_name, "Easy")
   (full_name, "cURL.Easy")
   (methods,
    ("cleanup",  bind, cleanup)
    ("perform",  bind, perform)
    ("reset",    bind, reset)
    ("escape",   bind, escape)
    ("unescape", bind, unescape)
    ("setopt",   bind, setopt)
    ("valid",    bind, valid))
	 (properties,
		("options", getter, get_opt))
   )
  {
    CURL *handle;
		EasyOpt &opt;

		object writecallback;
		static size_t writefunction(void *ptr, size_t size, size_t nmemb, void *stream) {
			assert(stream);
			Easy &self = *reinterpret_cast<Easy*>(stream);
			byte_array data(object(),
											reinterpret_cast<byte_array::element_type*>(ptr),
											size*nmemb);
			arguments arg;
			arg.push_back(value(data));
			arg.push_back(value(size));
			value v = self.writecallback.call(arg);
			return v.to_number();
		}
	protected:
		void trace(flusspferd::tracer &trc) {
			trc("writecallback", writecallback);
		}

  public:
    CURL *data() { return handle; }
    bool valid() { return handle; }
    CURL *get() {
      if(!handle) {
        throw flusspferd::exception("CURL handle not valid!");
      }
      return handle;
    }
		EasyOpt &get_opt() { return opt; }

    Easy(flusspferd::object const &self, flusspferd::call_context&)
      : base_type(self), handle(curl_easy_init()), opt(EasyOpt::create(*this))
    {
      if(!handle) {
        throw flusspferd::exception("curl_easy_init");
      }
    }

    Easy(flusspferd::object const &self, CURL *hnd)
      : base_type(self), handle(hnd), opt(EasyOpt::create(*this))
    {
      assert(handle);
    }

    void cleanup() {
      if(handle) {
        curl_easy_cleanup(handle);
        handle = 0x0;
      }
    }
    ~Easy() { cleanup(); }

    void perform() {
			CURLcode res = curl_easy_perform(get());
			if(res != 0) {
				throw flusspferd::exception(std::string("curl_easy_setopt: ") +
																		curl_easy_strerror(res));
			}
    }

    void reset() {
      curl_easy_reset(get());
    }

    std::string unescape(char const *input) {
      int len;
      char *const uesc = curl_easy_unescape(get(), input, 0, &len);
      if(!uesc) {
        throw flusspferd::exception("curl_easy_escape");
      }
      std::string ret(uesc, len);
      curl_free(uesc);
      return ret;
    }

    std::string escape(char const *input) {
      char *const esc = curl_easy_escape(get(), input, 0);
      if(!esc) {
        throw flusspferd::exception("curl_easy_escape");
      }
      std::string ret(esc);
      curl_free(esc);
      return ret;
    }

#define OPT_NUMBER(What)                                                \
    case What :                                                         \
      if(x.arg.size() != 2) {                                           \
        std::stringstream ss;                                           \
        ss << "curl_easy_setopt: Expected two arguments. Got (" << x.arg.size() << ')'; \
        throw flusspferd::exception(ss.str());                          \
      }                                                                 \
      do_setopt(What, x.arg[1].to_number());														\
      break

#define OPT_FUNCTION(What, Data, Callback, Func)												\
		case What : {																												\
			if(x.arg.size() != 2) {																						\
				throw flusspferd::exception("curl_easy_setopt: Expected two arguments"); \
			}																																	\
			if(!x.arg[1].is_object()) {																				\
				throw flusspferd::exception("curl_easy_setopt: Expected a function as second parameter");	\
			}																																	\
			object callback = x.arg[1].get_object();													\
			if(callback == default_function) {																\
				do_setopt(What, 0x0);																						\
				Callback = object();																						\
			}																																	\
			else {																														\
				do_setopt(Data, this);																					\
				do_setopt(What, Func);																					\
				Callback = callback;																						\
			}																																	\
		}																																		\
		break

#define OPT_DATAFUNCTION(What, Prefix)																	\
		OPT_FUNCTION( What ## FUNCTION, What ## DATA, Prefix ## callback, Prefix ## function )

	private:
		template<typename T>
		void do_setopt(CURLoption what, T data) {
		  CURLcode res = curl_easy_setopt(get(), what, data);
			if(res != 0) {
				throw flusspferd::exception(std::string("curl_easy_setopt: ") +
																		curl_easy_strerror(res));
			}
		}
	public:

    void setopt(flusspferd::call_context &x) {
      int what = x.arg.front().to_number();
      switch(what) {
				// Behaviour Options
				OPT_NUMBER(CURLOPT_VERBOSE);
        OPT_NUMBER(CURLOPT_HEADER);
				OPT_NUMBER(CURLOPT_NOPROGRESS);
				OPT_NUMBER(CURLOPT_NOSIGNAL);

				// Callback Options
				OPT_DATAFUNCTION(CURLOPT_WRITE, write);
      default: {
        std::stringstream ss;
        ss << "curl_easy_setopt unkown or unsupported option (" << what << ')';
        throw flusspferd::exception(ss.str());
      }
      };
    }

#undef OPT_DATAFUNCTION
#undef OPT_FUNCTION
#undef OPT_NUMBER

    static Easy &create(CURL *hnd) {
      return flusspferd::create_native_object<Easy>(object(), hnd);
    }

		static object default_function;
  };

	object Easy::default_function;

  Easy &wrap(CURL *hnd) {
    return Easy::create(hnd);
  }
  CURL *unwrap(Easy &c) {
    return c.data();
  }

	bool EasyOpt::property_resolve(value const &id, unsigned) {
		(void)id;
		return false;
	}

  FLUSSPFERD_LOADER_SIMPLE(cURL) {
    local_root_scope scope;

    cURL.define_property("GLOBAL_ALL", value(CURL_GLOBAL_ALL),
                         read_only_property | permanent_property);
    cURL.define_property("GLOBAL_SSL", value(CURL_GLOBAL_SSL),
                         read_only_property | permanent_property);
    cURL.define_property("GLOBAL_WIN32", value(CURL_GLOBAL_WIN32),
                         read_only_property | permanent_property);
    cURL.define_property("GLOBAL_NOTHING", value(CURL_GLOBAL_NOTHING),
                         read_only_property | permanent_property);
    create_native_function(cURL, "globalInit", &global_init);
    cURL.define_property("version", value(curl_version()),
                         read_only_property | permanent_property);
		load_class<EasyOpt>(cURL);
    load_class<Easy>(cURL);
		// Behaviour Options
		cURL.define_property("OPT_VERBOSE", value((int)CURLOPT_VERBOSE),
												 read_only_property | permanent_property);
    cURL.define_property("OPT_HEADER", value((int)CURLOPT_HEADER),
                         read_only_property | permanent_property);
		cURL.define_property("OPT_NOPROGRESS", value((int)CURLOPT_NOPROGRESS),
												 read_only_property | permanent_property);
		cURL.define_property("OPT_NOSIGNAL", value((int)CURLOPT_NOSIGNAL),
												 read_only_property | permanent_property);
		// Callback Options
		cURL.define_property("OPT_WRITEFUNCTION", value((int)CURLOPT_WRITEFUNCTION),
                         read_only_property | permanent_property);
		/*
		CURLOPT_READFUNCTION
		CURLOPT_IOCTLFUNCTION
		CURLOPT_SEEKFUNCTION
		CURLOPT_SOCKOPTFUNCTION
		CURLOPT_OPENSOCKETFUNCTION
		...
		 */
		cURL.define_property("defaultFunction", value(Easy::default_function),
												 read_only_property | permanent_property);
  }
}
<|endoftext|>


<commit_before>// Copyright © 2017-2018 Dmitriy Khaustov
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Author: Dmitriy Khaustov aka xDimon
// Contacts: khaustov.dm@gmail.com
// File created on: 2017.03.09

// ConnectionManager.cpp

#include <cstring>
#include "ConnectionManager.hpp"

#include "../thread/ThreadPool.hpp"
#include "../utils/Daemon.hpp"
#include "../thread/RollbackStackAndRestoreContext.hpp"
#include "../thread/TaskManager.hpp"

ConnectionManager::ConnectionManager()
: _log("ConnectionManager")
{
	_epfd = epoll_create(poolSize);
	memset(_epev, 0, sizeof(_epev));
}

ConnectionManager::~ConnectionManager()
{
	std::lock_guard<std::recursive_mutex> guard(_mutex);
	std::vector<std::shared_ptr<Connection>> connections;
	for (auto& i : _allConnections)
	{
		connections.emplace_back(i.second);
	}
	for (auto& connection : connections)
	{
		remove(connection);
	}
	close(_epfd);
	_epfd = -1;
}

/// Зарегистрировать соединение
void ConnectionManager::add(const std::shared_ptr<Connection>& connection)
{
	std::lock_guard<std::recursive_mutex> guard(getInstance()._mutex);

	if (getInstance()._allConnections.find(connection.get()) != getInstance()._allConnections.end())
	{
		getInstance()._log.warn("%s already registered in manager", connection->name().c_str());
		return;
	}

	getInstance()._allConnections.emplace(connection.get(), connection);

	getInstance()._log.debug("%s registered in manager", connection->name().c_str());

	epoll_event ev{};

	connection->watch(ev);

	// Включаем наблюдение
	if (epoll_ctl(getInstance()._epfd, EPOLL_CTL_ADD, connection->fd(), &ev) == -1)
	{
		getInstance()._log.warn("Fail add %s for watching (error: '%s')", connection->name().c_str(), strerror(errno));
	}
	else
	{
		getInstance()._log.trace("Add %s for watching", connection->name().c_str());
	}
}

/// Удалить регистрацию соединения
bool ConnectionManager::remove(const std::shared_ptr<Connection>& connection)
{
	if (!connection)
	{
		return false;
	}

	// Удаляем из очереди событий
	if (epoll_ctl(getInstance()._epfd, EPOLL_CTL_DEL, connection->fd(), nullptr) == -1)
	{
		getInstance()._log.warn("Fail remove %s from watching (error: '%s')", connection->name().c_str(), strerror(errno));
	}
	else
	{
		getInstance()._log.trace("Remove %s from watching", connection->name().c_str());
	}

	std::lock_guard<std::recursive_mutex> guard(getInstance()._mutex);
	getInstance()._allConnections.erase(connection.get());
	getInstance()._readyConnections.erase(connection);
	getInstance()._capturedConnections.erase(connection);

	getInstance()._log.debug("%s unregistered from manager", connection->name().c_str());

	return true;
}

uint32_t ConnectionManager::rotateEvents(const std::shared_ptr<Connection>& connection)
{
	std::lock_guard<std::recursive_mutex> guard(getInstance()._mutex);
	uint32_t events = connection->rotateEvents();
	return events;
}

void ConnectionManager::watch(const std::shared_ptr<Connection>& connection)
{
	// Для известных соенинений проверяем состояние захваченности
	std::lock_guard<std::recursive_mutex> guard(getInstance()._mutex);

	// Те, что в обработке, не трогаем
	if (connection->isCaptured())
	{
//		_log.trace("Skip watch for %s because already captured", connection->name().c_str());
		return;
	}

	epoll_event ev{};

	connection->watch(ev);

	// Включаем наблюдение
	if (epoll_ctl(getInstance()._epfd, EPOLL_CTL_MOD, connection->fd(), &ev) == -1)
	{
		getInstance()._log.warn("Fail modify watching on %s (error: '%s')", connection->name().c_str(), strerror(errno));
	}
	else
	{
		getInstance()._log.trace("Modify watching on %s", connection->name().c_str());
	}
}

/// Ожидать события на соединениях
void ConnectionManager::wait()
{
	// Если набор готовых соединений не пустой, то выходим
	if (!_readyConnections.empty())
	{
		return;
	}

	_mutex.unlock();

	_epool_mutex.lock();

	int n = 0;

	while ([&](){std::lock_guard<std::recursive_mutex> lockGuard(_mutex); return _readyConnections.empty();}())
	{
		if ([&](){std::lock_guard<std::recursive_mutex> lockGuard(_mutex); return _allConnections.empty();}())
		{
			std::this_thread::sleep_for(std::chrono::milliseconds(100));
			break;
		}
		n = epoll_wait(_epfd, _epev, poolSize, 50);
		if (n < 0)
		{
			if (errno != EINTR)
			{
				_log.warn("Error waiting events of connections: fail epoll_wait (%s)", strerror(errno));
				break;
			}
			continue;
		}
		if (n > 0)
		{
			_log.trace("Catch events on %d connection(s)", n);
			break;
		}
		if (Daemon::shutingdown())
		{
			// Закрываем оставшееся
			_epool_mutex.unlock();

			_mutex.lock();

			if (_allConnections.empty())
			{
				_log.debug("Interrupt waiting");
				return;
			}

//			std::vector<std::shared_ptr<Connection>> connections;
//			for (auto& i : _allConnections)
//			{
//				connections.emplace_back(i.second);
//			}
//			for (auto& connection : connections)
//			{
//				remove(connection);
//			}

			_mutex.unlock();

			_epool_mutex.lock();
		}
	}

	_mutex.lock();

	// Перебираем полученые события
	for (int i = 0; i < n; i++)
	{
		// Игнорируем незарегистрированные соединения
		auto it = _allConnections.find(static_cast<const Connection *>(_epev[i].data.ptr));
		if (it == _allConnections.end())
		{
			_log.trace("Skip catching of unregistered Connection %p", _epev[i].data.ptr);
			continue;
		}
		auto connection = it->second;

		uint32_t fdEvent = _epev[i].events;
		uint32_t events = 0;

		if (fdEvent & (EPOLLIN | EPOLLRDNORM))
		{
			events |= static_cast<uint32_t>(ConnectionEvent::READ);
		}
		if (fdEvent & (EPOLLOUT | EPOLLWRNORM))
		{
			events |= static_cast<uint32_t>(ConnectionEvent::WRITE);
		}
		if (fdEvent & EPOLLHUP)
		{
			events |= static_cast<uint32_t>(ConnectionEvent::HUP);
		}
		if (fdEvent & EPOLLRDHUP)
		{
			events |= static_cast<uint32_t>(ConnectionEvent::HALFHUP);
		}
		if (fdEvent & EPOLLERR)
		{
			events |= static_cast<uint32_t>(ConnectionEvent::ERROR);
		}

		_log.trace("Catch events `%s` (%04x) on %s", ConnectionEvent::code(events).c_str(), fdEvent, connection->name().c_str());

		connection->appendEvents(events);

		// Если не в списке захваченых...
		if (_capturedConnections.find(connection) == _capturedConnections.end())
		{
			_log.trace("Insert %s into ready connection list and will be processed now (by events)", connection->name().c_str());

			// ...добавляем в список готовых
			_readyConnections.insert(connection);
		}
	}

	_epool_mutex.unlock();
}

/// Зарегистрировать таймаут
void ConnectionManager::timeout(const std::shared_ptr<Connection>& connection)
{
	auto& instance = getInstance();

	std::lock_guard<std::recursive_mutex> lockGuard(instance._mutex);

	// Игнорируем незарегистрированные соединения
	auto it = instance._allConnections.find(connection.get());
	if (it == instance._allConnections.end())
	{
		instance._log.trace("Skip timeout adding for noregistered Connection %p", connection.get());
		return;
	}

	connection->appendEvents(static_cast<uint32_t>(ConnectionEvent::TIMEOUT));

	instance._log.trace("Catch event `T` on %s", connection->name().c_str());

	// Если не в списке захваченых...
	if (instance._capturedConnections.find(connection) == instance._capturedConnections.end())
	{
		instance._log.trace("Insert %s into ready connection list and will be processed now (by timeout)", connection->name().c_str());

		// ...добавляем в список готовых
		instance._readyConnections.insert(connection);
	}
}

/// Захватить соединение
std::shared_ptr<Connection> ConnectionManager::capture()
{
	std::lock_guard<std::recursive_mutex> lockGuard(_mutex);

	// Если нет готовых...
	while (_readyConnections.empty())
	{
		// то выходим при остановке сервера
		if (Daemon::shutingdown() && _allConnections.empty())
		{
			return nullptr;
		}

		_log.trace("Not found ready connection");

		// а в штатном режиме ожидаем появления готового соединения
		wait();
	}

	_log.trace("Found ready connection");

	if (_readyConnections.empty())
	{
		return nullptr;
	}

	// Берем соединение из набора готовых к обработке
	auto it = _readyConnections.begin();

	auto connection = *it;

	// Перемещаем соединение из набора готовых к обработке в набор захваченных
	_readyConnections.erase(it);
	_capturedConnections.insert(connection);

	_log.trace("Capture %s", connection->name().c_str());

	connection->setCaptured();

	return std::move(connection);
}

/// Освободить соединение
void ConnectionManager::release(const std::shared_ptr<Connection>& connection)
{
	std::lock_guard<std::recursive_mutex> guard(_mutex);

	_log.trace("Release %s", connection->name().c_str());

	_capturedConnections.erase(connection);

	connection->setReleased();

	if (!connection->isClosed())
	{
		watch(connection);
	}
}

/// Обработка событий
void ConnectionManager::dispatch()
{
	for (;;)
	{
		std::shared_ptr<Connection> connection = getInstance().capture();
		if (!connection)
		{
			break;
		}

		getInstance()._log.debug("Enqueue %s for '%s' events processing", connection->name().c_str(), ConnectionEvent::code(connection->events()).c_str());

		TaskManager::enqueue(
			[wp = std::weak_ptr<Connection>(connection)]
			{
				auto connection = wp.lock();
				if (!connection)
				{
					getInstance()._log.trace("Connection death");
					return;
				}

				getInstance()._log.trace("Begin processing on %s", connection->name().c_str());

				bool status;
				try
				{
					status = connection->processing();
				}
				catch (const RollbackStackAndRestoreContext& exception)
				{
					getInstance().release(connection);
					throw;
				}
				catch (const std::exception& exception)
				{
					status = false;
					getInstance()._log.warn("Uncatched exception at processing on %s: %s", connection->name().c_str(), exception.what());
				}

				getInstance().release(connection);

				getInstance()._log.trace("End processing on %s: %s", connection->name().c_str(), status ? "success" : "fail");
			}
		);
	}
}
<commit_msg>Set 1sec-TTL for Connection at shutting down<commit_after>// Copyright © 2017-2018 Dmitriy Khaustov
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Author: Dmitriy Khaustov aka xDimon
// Contacts: khaustov.dm@gmail.com
// File created on: 2017.03.09

// ConnectionManager.cpp

#include <cstring>
#include "ConnectionManager.hpp"

#include "../thread/ThreadPool.hpp"
#include "../utils/Daemon.hpp"
#include "../thread/RollbackStackAndRestoreContext.hpp"
#include "../thread/TaskManager.hpp"

ConnectionManager::ConnectionManager()
: _log("ConnectionManager")
{
	_epfd = epoll_create(poolSize);
	memset(_epev, 0, sizeof(_epev));
}

ConnectionManager::~ConnectionManager()
{
	std::lock_guard<std::recursive_mutex> guard(_mutex);
	std::vector<std::shared_ptr<Connection>> connections;
	for (auto& i : _allConnections)
	{
		connections.emplace_back(i.second);
	}
	for (auto& connection : connections)
	{
		remove(connection);
	}
	close(_epfd);
	_epfd = -1;
}

/// Зарегистрировать соединение
void ConnectionManager::add(const std::shared_ptr<Connection>& connection)
{
	std::lock_guard<std::recursive_mutex> guard(getInstance()._mutex);

	if (getInstance()._allConnections.find(connection.get()) != getInstance()._allConnections.end())
	{
		getInstance()._log.warn("%s already registered in manager", connection->name().c_str());
		return;
	}

	getInstance()._allConnections.emplace(connection.get(), connection);

	getInstance()._log.debug("%s registered in manager", connection->name().c_str());

	epoll_event ev{};

	connection->watch(ev);

	// Включаем наблюдение
	if (epoll_ctl(getInstance()._epfd, EPOLL_CTL_ADD, connection->fd(), &ev) == -1)
	{
		getInstance()._log.warn("Fail add %s for watching (error: '%s')", connection->name().c_str(), strerror(errno));
	}
	else
	{
		getInstance()._log.trace("Add %s for watching", connection->name().c_str());
	}
}

/// Удалить регистрацию соединения
bool ConnectionManager::remove(const std::shared_ptr<Connection>& connection)
{
	if (!connection)
	{
		return false;
	}

	// Удаляем из очереди событий
	if (epoll_ctl(getInstance()._epfd, EPOLL_CTL_DEL, connection->fd(), nullptr) == -1)
	{
		getInstance()._log.warn("Fail remove %s from watching (error: '%s')", connection->name().c_str(), strerror(errno));
	}
	else
	{
		getInstance()._log.trace("Remove %s from watching", connection->name().c_str());
	}

	std::lock_guard<std::recursive_mutex> guard(getInstance()._mutex);
	getInstance()._allConnections.erase(connection.get());
	getInstance()._readyConnections.erase(connection);
	getInstance()._capturedConnections.erase(connection);

	getInstance()._log.debug("%s unregistered from manager", connection->name().c_str());

	return true;
}

uint32_t ConnectionManager::rotateEvents(const std::shared_ptr<Connection>& connection)
{
	std::lock_guard<std::recursive_mutex> guard(getInstance()._mutex);
	uint32_t events = connection->rotateEvents();
	return events;
}

void ConnectionManager::watch(const std::shared_ptr<Connection>& connection)
{
	// Для известных соенинений проверяем состояние захваченности
	std::lock_guard<std::recursive_mutex> guard(getInstance()._mutex);

	// Те, что в обработке, не трогаем
	if (connection->isCaptured())
	{
//		_log.trace("Skip watch for %s because already captured", connection->name().c_str());
		return;
	}

	epoll_event ev{};

	connection->watch(ev);

	// Включаем наблюдение
	if (epoll_ctl(getInstance()._epfd, EPOLL_CTL_MOD, connection->fd(), &ev) == -1)
	{
		getInstance()._log.warn("Fail modify watching on %s (error: '%s')", connection->name().c_str(), strerror(errno));
	}
	else
	{
		getInstance()._log.trace("Modify watching on %s", connection->name().c_str());
	}
}

/// Ожидать события на соединениях
void ConnectionManager::wait()
{
	// Если набор готовых соединений не пустой, то выходим
	if (!_readyConnections.empty())
	{
		return;
	}

	_mutex.unlock();

	_epool_mutex.lock();

	int n = 0;

	while ([&](){std::lock_guard<std::recursive_mutex> lockGuard(_mutex); return _readyConnections.empty();}())
	{
		if ([&](){std::lock_guard<std::recursive_mutex> lockGuard(_mutex); return _allConnections.empty();}())
		{
			std::this_thread::sleep_for(std::chrono::milliseconds(100));
			break;
		}
		n = epoll_wait(_epfd, _epev, poolSize, 50);
		if (n < 0)
		{
			if (errno != EINTR)
			{
				_log.warn("Error waiting events of connections: fail epoll_wait (%s)", strerror(errno));
				break;
			}
			continue;
		}
		if (n > 0)
		{
			_log.trace("Catch events on %d connection(s)", n);
			break;
		}
		if (Daemon::shutingdown())
		{
			// Закрываем оставшееся
			_epool_mutex.unlock();

			_mutex.lock();

			if (_allConnections.empty())
			{
				_log.debug("Interrupt waiting");
				return;
			}

			for (auto& i : _allConnections)
			{
				i.second->setTtl(std::chrono::seconds(1));
			}

//			std::vector<std::shared_ptr<Connection>> connections;
//			for (auto& i : _allConnections)
//			{
//				connections.emplace_back(i.second);
//			}
//			for (auto& connection : connections)
//			{
//				remove(connection);
//			}

			_mutex.unlock();

			_epool_mutex.lock();
		}
	}

	_mutex.lock();

	// Перебираем полученые события
	for (int i = 0; i < n; i++)
	{
		// Игнорируем незарегистрированные соединения
		auto it = _allConnections.find(static_cast<const Connection *>(_epev[i].data.ptr));
		if (it == _allConnections.end())
		{
			_log.trace("Skip catching of unregistered Connection %p", _epev[i].data.ptr);
			continue;
		}
		auto connection = it->second;

		uint32_t fdEvent = _epev[i].events;
		uint32_t events = 0;

		if (fdEvent & (EPOLLIN | EPOLLRDNORM))
		{
			events |= static_cast<uint32_t>(ConnectionEvent::READ);
		}
		if (fdEvent & (EPOLLOUT | EPOLLWRNORM))
		{
			events |= static_cast<uint32_t>(ConnectionEvent::WRITE);
		}
		if (fdEvent & EPOLLHUP)
		{
			events |= static_cast<uint32_t>(ConnectionEvent::HUP);
		}
		if (fdEvent & EPOLLRDHUP)
		{
			events |= static_cast<uint32_t>(ConnectionEvent::HALFHUP);
		}
		if (fdEvent & EPOLLERR)
		{
			events |= static_cast<uint32_t>(ConnectionEvent::ERROR);
		}

		_log.trace("Catch events `%s` (%04x) on %s", ConnectionEvent::code(events).c_str(), fdEvent, connection->name().c_str());

		connection->appendEvents(events);

		// Если не в списке захваченых...
		if (_capturedConnections.find(connection) == _capturedConnections.end())
		{
			_log.trace("Insert %s into ready connection list and will be processed now (by events)", connection->name().c_str());

			// ...добавляем в список готовых
			_readyConnections.insert(connection);
		}
	}

	_epool_mutex.unlock();
}

/// Зарегистрировать таймаут
void ConnectionManager::timeout(const std::shared_ptr<Connection>& connection)
{
	auto& instance = getInstance();

	std::lock_guard<std::recursive_mutex> lockGuard(instance._mutex);

	// Игнорируем незарегистрированные соединения
	auto it = instance._allConnections.find(connection.get());
	if (it == instance._allConnections.end())
	{
		instance._log.trace("Skip timeout adding for noregistered Connection %p", connection.get());
		return;
	}

	connection->appendEvents(static_cast<uint32_t>(ConnectionEvent::TIMEOUT));

	instance._log.trace("Catch event `T` on %s", connection->name().c_str());

	// Если не в списке захваченых...
	if (instance._capturedConnections.find(connection) == instance._capturedConnections.end())
	{
		instance._log.trace("Insert %s into ready connection list and will be processed now (by timeout)", connection->name().c_str());

		// ...добавляем в список готовых
		instance._readyConnections.insert(connection);
	}
}

/// Захватить соединение
std::shared_ptr<Connection> ConnectionManager::capture()
{
	std::lock_guard<std::recursive_mutex> lockGuard(_mutex);

	// Если нет готовых...
	while (_readyConnections.empty())
	{
		// то выходим при остановке сервера
		if (Daemon::shutingdown() && _allConnections.empty())
		{
			return nullptr;
		}

		_log.trace("Not found ready connection");

		// а в штатном режиме ожидаем появления готового соединения
		wait();
	}

	_log.trace("Found ready connection");

	if (_readyConnections.empty())
	{
		return nullptr;
	}

	// Берем соединение из набора готовых к обработке
	auto it = _readyConnections.begin();

	auto connection = *it;

	// Перемещаем соединение из набора готовых к обработке в набор захваченных
	_readyConnections.erase(it);
	_capturedConnections.insert(connection);

	_log.trace("Capture %s", connection->name().c_str());

	connection->setCaptured();

	return std::move(connection);
}

/// Освободить соединение
void ConnectionManager::release(const std::shared_ptr<Connection>& connection)
{
	std::lock_guard<std::recursive_mutex> guard(_mutex);

	_log.trace("Release %s", connection->name().c_str());

	_capturedConnections.erase(connection);

	connection->setReleased();

	if (!connection->isClosed())
	{
		watch(connection);
	}
}

/// Обработка событий
void ConnectionManager::dispatch()
{
	for (;;)
	{
		std::shared_ptr<Connection> connection = getInstance().capture();
		if (!connection)
		{
			break;
		}

		getInstance()._log.debug("Enqueue %s for '%s' events processing", connection->name().c_str(), ConnectionEvent::code(connection->events()).c_str());

		TaskManager::enqueue(
			[wp = std::weak_ptr<Connection>(connection)]
			{
				auto connection = wp.lock();
				if (!connection)
				{
					getInstance()._log.trace("Connection death");
					return;
				}

				getInstance()._log.trace("Begin processing on %s", connection->name().c_str());

				bool status;
				try
				{
					status = connection->processing();
				}
				catch (const RollbackStackAndRestoreContext& exception)
				{
					getInstance().release(connection);
					throw;
				}
				catch (const std::exception& exception)
				{
					status = false;
					getInstance()._log.warn("Uncatched exception at processing on %s: %s", connection->name().c_str(), exception.what());
				}

				getInstance().release(connection);

				getInstance()._log.trace("End processing on %s: %s", connection->name().c_str(), status ? "success" : "fail");
			}
		);
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 Mirants Lu. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "log/checkpoint_receiver.h"

#include <vector>

#include "log/checkpoint_manager.h"
#include "paxos/config.h"
#include "skywalker/checkpoint.h"
#include "skywalker/file.h"
#include "skywalker/logging.h"
#include "util/timeops.h"

namespace skywalker {

CheckpointReceiver::CheckpointReceiver(Config* config,
                                       CheckpointManager* manager)
    : config_(config), manager_(manager) {}

CheckpointReceiver::~CheckpointReceiver() {}

bool CheckpointReceiver::BeginToReceive(const CheckpointMessage& msg) {
  sender_node_id_ = msg.node_id();
  sequence_id_ = 0;
  dirs_.clear();

  bool res = true;
  std::vector<std::string> dirs;
  std::vector<std::string> files;
  FileManager::Instance()->GetChildren(config_->CheckpointPath(), &dirs);

  for (auto& dir : dirs) {
    std::string d = config_->CheckpointPath() + "/" + dir;
    FileManager::Instance()->GetChildren(d, &files, true);
    if (files.empty()) {
      continue;
    }
    for (auto& file : files) {
      Status del = FileManager::Instance()->DeleteFile(d + "/" + file);
      if (!del.ok()) {
        LOG_ERROR("Group %u - %s.", config_->GetGroupId(),
                  del.ToString().c_str());
        res = false;
        break;
      }
    }
    FileManager::Instance()->DeleteDir(d);
  }

  return ComfirmReceive(msg, res);
}

bool CheckpointReceiver::ReceiveCheckpoint(const CheckpointMessage& msg) {
  LOG_DEBUG("Group %u - sequence_id(%d==%d)", config_->GetGroupId(),
            msg.sequence_id(), sequence_id_ + 1);
  bool res = ReceiveFiles(msg);
  return ComfirmReceive(msg, res);
}

bool CheckpointReceiver::ReceiveFiles(const CheckpointMessage& msg) {
  if (msg.node_id() != sender_node_id_) {
    return false;
  }

  if (msg.sequence_id() == sequence_id_) {
    return true;
  }

  if (msg.sequence_id() != sequence_id_ + 1) {
    return false;
  }

  if (dirs_.find(msg.machine_id()) == dirs_.end()) {
    char dir[512];
    snprintf(dir, sizeof(dir), "%s/m%d", config_->CheckpointPath().c_str(),
             msg.machine_id());
    FileManager::Instance()->CreateDir(dir);
    bool exits = FileManager::Instance()->FileExists(dir);
    if (!exits) {
      LOG_ERROR("Group %u - create checkpoint dir=%s failed.",
                config_->GetGroupId(), dir);
      return false;
    }
    dirs_[msg.machine_id()] = std::string(dir);
  }

  WritableFile* file;
  std::string fname = dirs_[msg.machine_id()] + "/" + msg.file();
  Status s = FileManager::Instance()->NewAppendableFile(fname, &file);
  if (s.ok()) {
    s = file->Append(msg.data());
    if (s.ok()) {
      ++sequence_id_;
    }
    delete file;
  }
  if (!s.ok()) {
    LOG_ERROR("Group %u - %s.", config_->GetGroupId(), s.ToString().c_str());
    return false;
  }
  return true;
}

bool CheckpointReceiver::EndToReceive(const CheckpointMessage& msg) {
  if (msg.node_id() != sender_node_id_) {
    return true;
  }

  if (msg.sequence_id() != sequence_id_ + 1) {
    return false;
  }

  bool res = true;

  bool lock = false;
  while (!lock) {
    lock = config_->GetCheckpoint()->LockCheckpoint(config_->GetGroupId());
    SleepForMicroseconds(1000);
  }
  std::vector<std::string> files;
  for (auto& d : dirs_) {
    Status s = FileManager::Instance()->GetChildren(d.second, &files, true);
    if (!s.ok()) {
      LOG_ERROR("Group %u - %s", config_->GetGroupId(), s.ToString().c_str());
      res = false;
      break;
    }
    if (files.empty()) {
      continue;
    }
    res = config_->GetCheckpoint()->LoadCheckpoint(
        config_->GetGroupId(), msg.instance_id(), d.first, d.second, files);
    if (!res) {
      LOG_ERROR("Group %u - load checkpoint failed, the machine_id=%d, dir=%s.",
                config_->GetGroupId(), d.first, d.second.c_str());
      break;
    }
  }
  config_->GetCheckpoint()->UnLockCheckpoint(config_->GetGroupId());

  if (res) {
    LOG_INFO("Group %u - load checkpoint successful!", config_->GetGroupId());
    LOG_WARN("Killing the process now...");
    SetLogHandler(nullptr);
    exit(1);
  }
  return res;
}

bool CheckpointReceiver::ComfirmReceive(const CheckpointMessage& msg,
                                        bool res) {
  CheckpointMessage* reply_msg = new CheckpointMessage();
  reply_msg->set_type(CHECKPOINT_COMFIRM);
  reply_msg->set_node_id(config_->GetNodeId());
  reply_msg->set_sequence_id(msg.sequence_id());
  reply_msg->set_flag(res);
  config_->GetMessager()->SendMessage(
      msg.node_id(), config_->GetMessager()->PackMessage(reply_msg));
  if (!res && msg.node_id() == sender_node_id_) {
    Reset();
    return false;
  }
  return true;
}

void CheckpointReceiver::Reset() {
  sender_node_id_ = 0;
  sequence_id_ = 0;
  dirs_.clear();
}

}  // namespace skywalker
<commit_msg>fix bug<commit_after>// Copyright (c) 2016 Mirants Lu. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "log/checkpoint_receiver.h"

#include <unistd.h>
#include <vector>

#include "log/checkpoint_manager.h"
#include "paxos/config.h"
#include "skywalker/checkpoint.h"
#include "skywalker/file.h"
#include "skywalker/logging.h"
#include "util/timeops.h"

namespace skywalker {

CheckpointReceiver::CheckpointReceiver(Config* config,
                                       CheckpointManager* manager)
    : config_(config), manager_(manager) {}

CheckpointReceiver::~CheckpointReceiver() {}

bool CheckpointReceiver::BeginToReceive(const CheckpointMessage& msg) {
  sender_node_id_ = msg.node_id();
  sequence_id_ = 0;
  dirs_.clear();

  bool res = true;
  std::vector<std::string> dirs;
  std::vector<std::string> files;
  FileManager::Instance()->GetChildren(config_->CheckpointPath(), &dirs);

  for (auto& dir : dirs) {
    std::string d = config_->CheckpointPath() + "/" + dir;
    FileManager::Instance()->GetChildren(d, &files, true);
    if (files.empty()) {
      continue;
    }
    for (auto& file : files) {
      Status del = FileManager::Instance()->DeleteFile(d + "/" + file);
      if (!del.ok()) {
        LOG_ERROR("Group %u - %s.", config_->GetGroupId(),
                  del.ToString().c_str());
        res = false;
        break;
      }
    }
    FileManager::Instance()->DeleteDir(d);
  }

  return ComfirmReceive(msg, res);
}

bool CheckpointReceiver::ReceiveCheckpoint(const CheckpointMessage& msg) {
  LOG_DEBUG("Group %u - sequence_id(%d==%d)", config_->GetGroupId(),
            msg.sequence_id(), sequence_id_ + 1);
  bool res = ReceiveFiles(msg);
  return ComfirmReceive(msg, res);
}

bool CheckpointReceiver::ReceiveFiles(const CheckpointMessage& msg) {
  if (msg.node_id() != sender_node_id_) {
    return false;
  }

  if (msg.sequence_id() == sequence_id_) {
    return true;
  }

  if (msg.sequence_id() != sequence_id_ + 1) {
    return false;
  }

  if (dirs_.find(msg.machine_id()) == dirs_.end()) {
    char dir[512];
    snprintf(dir, sizeof(dir), "%s/m%d", config_->CheckpointPath().c_str(),
             msg.machine_id());
    FileManager::Instance()->CreateDir(dir);
    bool exits = FileManager::Instance()->FileExists(dir);
    if (!exits) {
      LOG_ERROR("Group %u - create checkpoint dir=%s failed.",
                config_->GetGroupId(), dir);
      return false;
    }
    dirs_[msg.machine_id()] = std::string(dir);
  }

  WritableFile* file;
  std::string fname = dirs_[msg.machine_id()] + "/" + msg.file();
  Status s = FileManager::Instance()->NewAppendableFile(fname, &file);
  if (s.ok()) {
    s = file->Append(msg.data());
    if (s.ok()) {
      ++sequence_id_;
    }
    delete file;
  }
  if (!s.ok()) {
    LOG_ERROR("Group %u - %s.", config_->GetGroupId(), s.ToString().c_str());
    return false;
  }
  return true;
}

bool CheckpointReceiver::EndToReceive(const CheckpointMessage& msg) {
  if (msg.node_id() != sender_node_id_) {
    return true;
  }

  if (msg.sequence_id() != sequence_id_ + 1) {
    return false;
  }

  bool res = true;

  bool lock = false;
  while (!lock) {
    lock = config_->GetCheckpoint()->LockCheckpoint(config_->GetGroupId());
    SleepForMicroseconds(1000);
  }
  std::vector<std::string> files;
  for (auto& d : dirs_) {
    Status s = FileManager::Instance()->GetChildren(d.second, &files, true);
    if (!s.ok()) {
      LOG_ERROR("Group %u - %s", config_->GetGroupId(), s.ToString().c_str());
      res = false;
      break;
    }
    if (files.empty()) {
      continue;
    }
    res = config_->GetCheckpoint()->LoadCheckpoint(
        config_->GetGroupId(), msg.instance_id(), d.first, d.second, files);
    if (!res) {
      LOG_ERROR("Group %u - load checkpoint failed, the machine_id=%d, dir=%s.",
                config_->GetGroupId(), d.first, d.second.c_str());
      break;
    }
  }
  config_->GetCheckpoint()->UnLockCheckpoint(config_->GetGroupId());

  if (res) {
    LOG_INFO("Group %u - load checkpoint successful!", config_->GetGroupId());
    LOG_WARN("Killing the process now...");
    // FIXME
    _exit(2);
  }
  return res;
}

bool CheckpointReceiver::ComfirmReceive(const CheckpointMessage& msg,
                                        bool res) {
  CheckpointMessage* reply_msg = new CheckpointMessage();
  reply_msg->set_type(CHECKPOINT_COMFIRM);
  reply_msg->set_node_id(config_->GetNodeId());
  reply_msg->set_sequence_id(msg.sequence_id());
  reply_msg->set_flag(res);
  config_->GetMessager()->SendMessage(
      msg.node_id(), config_->GetMessager()->PackMessage(reply_msg));
  if (!res && msg.node_id() == sender_node_id_) {
    Reset();
    return false;
  }
  return true;
}

void CheckpointReceiver::Reset() {
  sender_node_id_ = 0;
  sequence_id_ = 0;
  dirs_.clear();
}

}  // namespace skywalker
<|endoftext|>