<commit_before>/*
 * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc. All rights reserved.
 *
 * Portions are Copyright (C) 1998 Netscape Communications Corporation.
 *
 * Other contributors:
 *   Robert O'Callahan <roc+@cs.cmu.edu>
 *   David Baron <dbaron@fas.harvard.edu>
 *   Christian Biesinger <cbiesinger@web.de>
 *   Randall Jesup <rjesup@wgate.com>
 *   Roland Mainz <roland.mainz@informatik.med.uni-giessen.de>
 *   Josh Soref <timeless@mac.com>
 *   Boris Zbarsky <bzbarsky@mit.edu>
 *
 * 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.1 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * Alternatively, the contents of this file may be used under the terms
 * of either the Mozilla Public License Version 1.1, found at
 * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public
 * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html
 * (the "GPL"), in which case the provisions of the MPL or the GPL are
 * applicable instead of those above.  If you wish to allow use of your
 * version of this file only under the terms of one of those two
 * licenses (the MPL or the GPL) and not to allow others to use your
 * version of this file under the LGPL, indicate your decision by
 * deletingthe provisions above and replace them with the notice and
 * other provisions required by the MPL or the GPL, as the case may be.
 * If you do not delete the provisions above, a recipient may use your
 * version of this file under any of the LGPL, the MPL or the GPL.
 */

#include "config.h"
#include "core/rendering/RenderLayerRepainter.h"

#include "core/rendering/FilterEffectRenderer.h"
#include "core/rendering/RenderLayer.h"
#include "core/rendering/RenderView.h"
#include "core/rendering/compositing/CompositedLayerMapping.h"

namespace WebCore {

RenderLayerRepainter::RenderLayerRepainter(RenderLayerModelObject& renderer)
    : m_renderer(renderer)
    , m_repaintStatus(NeedsNormalRepaint)
{
}

void RenderLayerRepainter::repaintAfterLayout(bool shouldCheckForRepaint)
{
    if (RuntimeEnabledFeatures::repaintAfterLayoutEnabled())
        return;

    // FIXME: really, we're in the repaint phase here, and the following queries are legal.
    // Until those states are fully fledged, I'll just disable the ASSERTS.
    DisableCompositingQueryAsserts disabler;
    if (m_renderer.layer()->hasVisibleContent()) {
        RenderView* view = m_renderer.view();
        ASSERT(view);
        // FIXME: LayoutState does not work with RenderLayers as there is not a 1-to-1
        // mapping between them and the RenderObjects. It would be neat to enable
        // LayoutState outside the layout() phase and use it here.
        ASSERT(!view->layoutStateEnabled());

        const RenderLayerModelObject* repaintContainer = m_renderer.containerForRepaint();
        LayoutRect oldRepaintRect = m_repaintRect;
        LayoutPoint oldOffset = m_offset;
        computeRepaintRects(repaintContainer);
        shouldCheckForRepaint &= shouldRepaintLayer();

        if (shouldCheckForRepaint) {
            if (view && !view->document().printing()) {
                if (m_repaintStatus & NeedsFullRepaint) {
                    m_renderer.repaintUsingContainer(repaintContainer, pixelSnappedIntRect(oldRepaintRect), InvalidationLayer);
                    if (m_repaintRect != oldRepaintRect)
                        m_renderer.repaintUsingContainer(repaintContainer, pixelSnappedIntRect(m_repaintRect), InvalidationLayer);
                } else {
                    m_renderer.repaintAfterLayoutIfNeeded(repaintContainer, m_renderer.selfNeedsLayout(), oldRepaintRect, oldOffset, &m_repaintRect, &m_offset);
                }
            }
        }
    } else {
        clearRepaintRects();
    }

    m_repaintStatus = NeedsNormalRepaint;

}

void RenderLayerRepainter::clearRepaintRects()
{
    ASSERT(!m_renderer.layer()->hasVisibleContent());

    m_repaintRect = IntRect();
}

void RenderLayerRepainter::computeRepaintRects(const RenderLayerModelObject* repaintContainer)
{
    if (RuntimeEnabledFeatures::repaintAfterLayoutEnabled()) {
        // FIXME: We want RenderLayerRepainter to go away when
        // repaint-after-layout is on by default so we need to figure out how to
        // handle this update.
        m_renderer.setPreviousRepaintRect(m_renderer.clippedOverflowRectForRepaint(m_renderer.containerForRepaint()));
    } else {
        m_repaintRect = m_renderer.clippedOverflowRectForRepaint(repaintContainer);
        m_offset = m_renderer.positionFromRepaintContainer(repaintContainer);
    }
}

void RenderLayerRepainter::computeRepaintRectsIncludingDescendants()
{
    // FIXME: computeRepaintRects() has to walk up the parent chain for every layer to compute the rects.
    // We should make this more efficient.
    // FIXME: it's wrong to call this when layout is not up-to-date, which we do.
    computeRepaintRects(m_renderer.containerForRepaint());

    for (RenderLayer* layer = m_renderer.layer()->firstChild(); layer; layer = layer->nextSibling())
        layer->repainter().computeRepaintRectsIncludingDescendants();
}

inline bool RenderLayerRepainter::shouldRepaintLayer() const
{
    if (RuntimeEnabledFeatures::repaintAfterLayoutEnabled())
        return false;

    if (m_repaintStatus != NeedsFullRepaintForPositionedMovementLayout)
        return true;

    // Composited layers that were moved during a positioned movement only
    // layout, don't need to be repainted. They just need to be recomposited.
    return m_renderer.compositingState() != PaintsIntoOwnBacking;
}

// Since we're only painting non-composited layers, we know that they all share the same repaintContainer.
void RenderLayerRepainter::repaintIncludingNonCompositingDescendants(const RenderLayerModelObject* repaintContainer)
{
    m_renderer.repaintUsingContainer(repaintContainer, pixelSnappedIntRect(m_renderer.clippedOverflowRectForRepaint(repaintContainer)), InvalidationLayer);

    for (RenderLayer* curr = m_renderer.layer()->firstChild(); curr; curr = curr->nextSibling()) {
        if (!curr->hasCompositedLayerMapping())
            curr->repainter().repaintIncludingNonCompositingDescendants(repaintContainer);
    }
}

LayoutRect RenderLayerRepainter::repaintRectIncludingNonCompositingDescendants() const
{
    LayoutRect repaintRect;
    if (RuntimeEnabledFeatures::repaintAfterLayoutEnabled())
        repaintRect = m_renderer.previousRepaintRect();
    else
        repaintRect = m_repaintRect;

    for (RenderLayer* child = m_renderer.layer()->firstChild(); child; child = child->nextSibling()) {
        // Don't include repaint rects for composited child layers; they will paint themselves and have a different origin.
        if (child->hasCompositedLayerMapping())
            continue;

        repaintRect.unite(child->repainter().repaintRectIncludingNonCompositingDescendants());
    }
    return repaintRect;
}

void RenderLayerRepainter::setBackingNeedsRepaint()
{
    // There is only one call site, and that call site ensures that the compositing state is PaintsIntoOwnBacking.
    ASSERT(m_renderer.compositingState() == PaintsIntoOwnBacking);

    m_renderer.compositedLayerMapping()->setContentsNeedDisplay();
}

void RenderLayerRepainter::setBackingNeedsRepaintInRect(const LayoutRect& r)
{
    // https://bugs.webkit.org/show_bug.cgi?id=61159 describes an unreproducible crash here,
    // so assert but check that the layer is composited.
    ASSERT(m_renderer.compositingState() != NotComposited);
    if (m_renderer.compositingState() == NotComposited) {
        // If we're trying to repaint the placeholder document layer, propagate the
        // repaint to the native view system.
        LayoutRect absRect(r);
        LayoutPoint delta;
        m_renderer.layer()->convertToLayerCoords(m_renderer.layer()->root(), delta);
        absRect.moveBy(delta);

        if (absRect.isEmpty())
            return;

        RenderView* view = m_renderer.view();
        if (view)
            view->repaintViewRectangle(absRect);
        return;
    }
    if (m_renderer.compositingState() == PaintsIntoGroupedBacking) {
        LayoutRect updatedRect(r);

        RenderLayerModelObject* transformedAncestor = m_renderer.layer()->enclosingTransformedAncestor()->renderer();

        // If the transformedAncestor is actually the RenderView, we might get
        // confused and think that we can use LayoutState. Ideally, we'd made
        // LayoutState work for all composited layers as well, but until then
        // we need to disable LayoutState for squashed layers.
        LayoutStateDisabler layoutStateDisabler(*transformedAncestor);

        // This code adjusts the repaint rectangle to be in the space of the transformed ancestor of the grouped (i.e. squashed)
        // layer. This is because all layers that squash together need to repaint w.r.t. a single container that is
        // an ancestor of all of them, in order to properly take into account any local transforms etc.
        // FIXME: remove this special-case code that works around the repainting code structure.
        m_renderer.computeRectForRepaint(transformedAncestor, updatedRect);
        updatedRect.moveBy(-m_renderer.layer()->groupedMapping()->squashingOffsetFromTransformedAncestor());

        IntRect repaintRect = pixelSnappedIntRect(updatedRect);
        if (GraphicsLayer* squashingLayer = m_renderer.groupedMapping()->squashingLayer())
            squashingLayer->setNeedsDisplayInRect(repaintRect);
    } else {
        IntRect repaintRect = pixelSnappedIntRect(r);
        m_renderer.compositedLayerMapping()->setContentsNeedDisplayInRect(repaintRect);
    }
}

void RenderLayerRepainter::setFilterBackendNeedsRepaintingInRect(const LayoutRect& rect)
{
    if (rect.isEmpty())
        return;

    LayoutRect rectForRepaint = rect;
    m_renderer.style()->filterOutsets().expandRect(rectForRepaint);

    RenderLayerFilterInfo* filterInfo = m_renderer.layer()->filterInfo();
    ASSERT(filterInfo);
    filterInfo->expandDirtySourceRect(rectForRepaint);

    RenderLayer* parentLayer = enclosingFilterRepaintLayer();
    ASSERT(parentLayer);
    FloatQuad repaintQuad(rectForRepaint);
    LayoutRect parentLayerRect = m_renderer.localToContainerQuad(repaintQuad, parentLayer->renderer()).enclosingBoundingBox();

    if (parentLayerRect.isEmpty())
        return;

    if (parentLayer->hasCompositedLayerMapping()) {
        parentLayer->repainter().setBackingNeedsRepaintInRect(parentLayerRect);
        return;
    }

    if (parentLayer->paintsWithFilters()) {
        parentLayer->repainter().setFilterBackendNeedsRepaintingInRect(parentLayerRect);
        return;
    }

    if (parentLayer->isRootLayer()) {
        RenderView* view = toRenderView(parentLayer->renderer());
        view->repaintViewRectangle(parentLayerRect);
        return;
    }

    ASSERT_NOT_REACHED();
}

RenderLayer* RenderLayerRepainter::enclosingFilterRepaintLayer() const
{
    for (const RenderLayer* curr = m_renderer.layer(); curr; curr = curr->parent()) {
        if ((curr != m_renderer.layer() && curr->requiresFullLayerImageForFilters()) || curr->compositingState() == PaintsIntoOwnBacking || curr->isRootLayer())
            return const_cast<RenderLayer*>(curr);
    }
    return 0;
}

} // Namespace WebCore
<commit_msg>Add defensive code for now to RenderLayerRepainter to prevent squashing crashes when removing certain kinds of layers.<commit_after>/*
 * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc. All rights reserved.
 *
 * Portions are Copyright (C) 1998 Netscape Communications Corporation.
 *
 * Other contributors:
 *   Robert O'Callahan <roc+@cs.cmu.edu>
 *   David Baron <dbaron@fas.harvard.edu>
 *   Christian Biesinger <cbiesinger@web.de>
 *   Randall Jesup <rjesup@wgate.com>
 *   Roland Mainz <roland.mainz@informatik.med.uni-giessen.de>
 *   Josh Soref <timeless@mac.com>
 *   Boris Zbarsky <bzbarsky@mit.edu>
 *
 * 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.1 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * Alternatively, the contents of this file may be used under the terms
 * of either the Mozilla Public License Version 1.1, found at
 * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public
 * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html
 * (the "GPL"), in which case the provisions of the MPL or the GPL are
 * applicable instead of those above.  If you wish to allow use of your
 * version of this file only under the terms of one of those two
 * licenses (the MPL or the GPL) and not to allow others to use your
 * version of this file under the LGPL, indicate your decision by
 * deletingthe provisions above and replace them with the notice and
 * other provisions required by the MPL or the GPL, as the case may be.
 * If you do not delete the provisions above, a recipient may use your
 * version of this file under any of the LGPL, the MPL or the GPL.
 */

#include "config.h"
#include "core/rendering/RenderLayerRepainter.h"

#include "core/rendering/FilterEffectRenderer.h"
#include "core/rendering/RenderLayer.h"
#include "core/rendering/RenderView.h"
#include "core/rendering/compositing/CompositedLayerMapping.h"

namespace WebCore {

RenderLayerRepainter::RenderLayerRepainter(RenderLayerModelObject& renderer)
    : m_renderer(renderer)
    , m_repaintStatus(NeedsNormalRepaint)
{
}

void RenderLayerRepainter::repaintAfterLayout(bool shouldCheckForRepaint)
{
    if (RuntimeEnabledFeatures::repaintAfterLayoutEnabled())
        return;

    // FIXME: really, we're in the repaint phase here, and the following queries are legal.
    // Until those states are fully fledged, I'll just disable the ASSERTS.
    DisableCompositingQueryAsserts disabler;
    if (m_renderer.layer()->hasVisibleContent()) {
        RenderView* view = m_renderer.view();
        ASSERT(view);
        // FIXME: LayoutState does not work with RenderLayers as there is not a 1-to-1
        // mapping between them and the RenderObjects. It would be neat to enable
        // LayoutState outside the layout() phase and use it here.
        ASSERT(!view->layoutStateEnabled());

        const RenderLayerModelObject* repaintContainer = m_renderer.containerForRepaint();
        LayoutRect oldRepaintRect = m_repaintRect;
        LayoutPoint oldOffset = m_offset;
        computeRepaintRects(repaintContainer);
        shouldCheckForRepaint &= shouldRepaintLayer();

        if (shouldCheckForRepaint) {
            if (view && !view->document().printing()) {
                if (m_repaintStatus & NeedsFullRepaint) {
                    m_renderer.repaintUsingContainer(repaintContainer, pixelSnappedIntRect(oldRepaintRect), InvalidationLayer);
                    if (m_repaintRect != oldRepaintRect)
                        m_renderer.repaintUsingContainer(repaintContainer, pixelSnappedIntRect(m_repaintRect), InvalidationLayer);
                } else {
                    m_renderer.repaintAfterLayoutIfNeeded(repaintContainer, m_renderer.selfNeedsLayout(), oldRepaintRect, oldOffset, &m_repaintRect, &m_offset);
                }
            }
        }
    } else {
        clearRepaintRects();
    }

    m_repaintStatus = NeedsNormalRepaint;

}

void RenderLayerRepainter::clearRepaintRects()
{
    ASSERT(!m_renderer.layer()->hasVisibleContent());

    m_repaintRect = IntRect();
}

void RenderLayerRepainter::computeRepaintRects(const RenderLayerModelObject* repaintContainer)
{
    if (RuntimeEnabledFeatures::repaintAfterLayoutEnabled()) {
        // FIXME: We want RenderLayerRepainter to go away when
        // repaint-after-layout is on by default so we need to figure out how to
        // handle this update.
        m_renderer.setPreviousRepaintRect(m_renderer.clippedOverflowRectForRepaint(m_renderer.containerForRepaint()));
    } else {
        m_repaintRect = m_renderer.clippedOverflowRectForRepaint(repaintContainer);
        m_offset = m_renderer.positionFromRepaintContainer(repaintContainer);
    }
}

void RenderLayerRepainter::computeRepaintRectsIncludingDescendants()
{
    // FIXME: computeRepaintRects() has to walk up the parent chain for every layer to compute the rects.
    // We should make this more efficient.
    // FIXME: it's wrong to call this when layout is not up-to-date, which we do.
    computeRepaintRects(m_renderer.containerForRepaint());

    for (RenderLayer* layer = m_renderer.layer()->firstChild(); layer; layer = layer->nextSibling())
        layer->repainter().computeRepaintRectsIncludingDescendants();
}

inline bool RenderLayerRepainter::shouldRepaintLayer() const
{
    if (RuntimeEnabledFeatures::repaintAfterLayoutEnabled())
        return false;

    if (m_repaintStatus != NeedsFullRepaintForPositionedMovementLayout)
        return true;

    // Composited layers that were moved during a positioned movement only
    // layout, don't need to be repainted. They just need to be recomposited.
    return m_renderer.compositingState() != PaintsIntoOwnBacking;
}

// Since we're only painting non-composited layers, we know that they all share the same repaintContainer.
void RenderLayerRepainter::repaintIncludingNonCompositingDescendants(const RenderLayerModelObject* repaintContainer)
{
    m_renderer.repaintUsingContainer(repaintContainer, pixelSnappedIntRect(m_renderer.clippedOverflowRectForRepaint(repaintContainer)), InvalidationLayer);

    for (RenderLayer* curr = m_renderer.layer()->firstChild(); curr; curr = curr->nextSibling()) {
        if (!curr->hasCompositedLayerMapping())
            curr->repainter().repaintIncludingNonCompositingDescendants(repaintContainer);
    }
}

LayoutRect RenderLayerRepainter::repaintRectIncludingNonCompositingDescendants() const
{
    LayoutRect repaintRect;
    if (RuntimeEnabledFeatures::repaintAfterLayoutEnabled())
        repaintRect = m_renderer.previousRepaintRect();
    else
        repaintRect = m_repaintRect;

    for (RenderLayer* child = m_renderer.layer()->firstChild(); child; child = child->nextSibling()) {
        // Don't include repaint rects for composited child layers; they will paint themselves and have a different origin.
        if (child->hasCompositedLayerMapping())
            continue;

        repaintRect.unite(child->repainter().repaintRectIncludingNonCompositingDescendants());
    }
    return repaintRect;
}

void RenderLayerRepainter::setBackingNeedsRepaint()
{
    // There is only one call site, and that call site ensures that the compositing state is PaintsIntoOwnBacking.
    ASSERT(m_renderer.compositingState() == PaintsIntoOwnBacking);

    m_renderer.compositedLayerMapping()->setContentsNeedDisplay();
}

void RenderLayerRepainter::setBackingNeedsRepaintInRect(const LayoutRect& r)
{
    // https://bugs.webkit.org/show_bug.cgi?id=61159 describes an unreproducible crash here,
    // so assert but check that the layer is composited.
    ASSERT(m_renderer.compositingState() != NotComposited);
    if (m_renderer.compositingState() == NotComposited) {
        // If we're trying to repaint the placeholder document layer, propagate the
        // repaint to the native view system.
        LayoutRect absRect(r);
        LayoutPoint delta;
        m_renderer.layer()->convertToLayerCoords(m_renderer.layer()->root(), delta);
        absRect.moveBy(delta);

        if (absRect.isEmpty())
            return;

        RenderView* view = m_renderer.view();
        if (view)
            view->repaintViewRectangle(absRect);
        return;
    }
    if (m_renderer.compositingState() == PaintsIntoGroupedBacking) {
        LayoutRect updatedRect(r);

        ASSERT(m_renderer.layer());
        ASSERT(m_renderer.layer()->enclosingTransformedAncestor());
        ASSERT(m_renderer.layer()->enclosingTransformedAncestor()->renderer());

        // FIXME: this defensive code should not have to exist. None of these pointers should ever be 0. See crbug.com/370410.
        RenderLayerModelObject* transformedAncestor = 0;
        if (RenderLayer* ancestor = m_renderer.layer()->enclosingTransformedAncestor())
            transformedAncestor = ancestor->renderer();
        if (!transformedAncestor)
            return;

        // If the transformedAncestor is actually the RenderView, we might get
        // confused and think that we can use LayoutState. Ideally, we'd made
        // LayoutState work for all composited layers as well, but until then
        // we need to disable LayoutState for squashed layers.
        LayoutStateDisabler layoutStateDisabler(*transformedAncestor);

        // This code adjusts the repaint rectangle to be in the space of the transformed ancestor of the grouped (i.e. squashed)
        // layer. This is because all layers that squash together need to repaint w.r.t. a single container that is
        // an ancestor of all of them, in order to properly take into account any local transforms etc.
        // FIXME: remove this special-case code that works around the repainting code structure.
        m_renderer.computeRectForRepaint(transformedAncestor, updatedRect);
        updatedRect.moveBy(-m_renderer.layer()->groupedMapping()->squashingOffsetFromTransformedAncestor());

        IntRect repaintRect = pixelSnappedIntRect(updatedRect);
        if (GraphicsLayer* squashingLayer = m_renderer.groupedMapping()->squashingLayer())
            squashingLayer->setNeedsDisplayInRect(repaintRect);
    } else {
        IntRect repaintRect = pixelSnappedIntRect(r);
        m_renderer.compositedLayerMapping()->setContentsNeedDisplayInRect(repaintRect);
    }
}

void RenderLayerRepainter::setFilterBackendNeedsRepaintingInRect(const LayoutRect& rect)
{
    if (rect.isEmpty())
        return;

    LayoutRect rectForRepaint = rect;
    m_renderer.style()->filterOutsets().expandRect(rectForRepaint);

    RenderLayerFilterInfo* filterInfo = m_renderer.layer()->filterInfo();
    ASSERT(filterInfo);
    filterInfo->expandDirtySourceRect(rectForRepaint);

    RenderLayer* parentLayer = enclosingFilterRepaintLayer();
    ASSERT(parentLayer);
    FloatQuad repaintQuad(rectForRepaint);
    LayoutRect parentLayerRect = m_renderer.localToContainerQuad(repaintQuad, parentLayer->renderer()).enclosingBoundingBox();

    if (parentLayerRect.isEmpty())
        return;

    if (parentLayer->hasCompositedLayerMapping()) {
        parentLayer->repainter().setBackingNeedsRepaintInRect(parentLayerRect);
        return;
    }

    if (parentLayer->paintsWithFilters()) {
        parentLayer->repainter().setFilterBackendNeedsRepaintingInRect(parentLayerRect);
        return;
    }

    if (parentLayer->isRootLayer()) {
        RenderView* view = toRenderView(parentLayer->renderer());
        view->repaintViewRectangle(parentLayerRect);
        return;
    }

    ASSERT_NOT_REACHED();
}

RenderLayer* RenderLayerRepainter::enclosingFilterRepaintLayer() const
{
    for (const RenderLayer* curr = m_renderer.layer(); curr; curr = curr->parent()) {
        if ((curr != m_renderer.layer() && curr->requiresFullLayerImageForFilters()) || curr->compositingState() == PaintsIntoOwnBacking || curr->isRootLayer())
            return const_cast<RenderLayer*>(curr);
    }
    return 0;
}

} // Namespace WebCore
<|endoftext|>


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

#include <atlbase.h>

#include "base/logging.h"
#include "chrome_frame/crash_reporting/vectored_handler-impl.h"
#include "chrome_frame/crash_reporting/veh_test.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

using testing::_;

ACTION_P(StackTraceDump, s) {
  memcpy(arg2, s->stack_, s->count_ * sizeof(s->stack_[0]));
  return s->count_;
}
namespace {
class MockApi : public Win32VEHTraits, public ModuleOfInterest {
 public:
  MockApi() {
    Win32VEHTraits::InitializeIgnoredBlocks();
  }

  MOCK_METHOD1(WriteDump, void(const EXCEPTION_POINTERS*));
  MOCK_METHOD0(RtlpGetExceptionList, const EXCEPTION_REGISTRATION_RECORD*());
  MOCK_METHOD4(RtlCaptureStackBackTrace, WORD(DWORD FramesToSkip,
      DWORD FramesToCapture, void** BackTrace, DWORD* BackTraceHash));

  // Helpers
  void SetSEH(const SEHChain& sehchain) {
    EXPECT_CALL(*this, RtlpGetExceptionList())
        .Times(testing::AnyNumber())
        .WillRepeatedly(testing::Return(sehchain.chain_));
  }

  void SetStack(const StackHelper& s) {
    EXPECT_CALL(*this, RtlCaptureStackBackTrace(_, _, _, _))
        .Times(testing::AnyNumber())
        .WillRepeatedly(StackTraceDump(&s));
  }

  enum {max_back_trace = 15};
};
};  // namespace

typedef VectoredHandlerT<MockApi> VectoredHandlerMock;
TEST(ChromeFrame, ExceptionReport) {
  MockApi api;
  VectoredHandlerMock veh(&api);

  // Start address of our module.
  char* s = reinterpret_cast<char*>(0x30000000);
  char *e = s + 0x10000;
  api.SetModule(s, e);

  SEHChain have_seh(s - 0x1000, s + 0x1000, e + 0x7127, NULL);
  SEHChain no_seh(s - 0x1000, e + 0x7127, NULL);
  StackHelper on_stack(s - 0x11283, s - 0x278361, s + 0x9171, e + 1231, NULL);
  StackHelper not_on_stack(s - 0x11283, s - 0x278361, e + 1231, NULL);

  char* our_code = s + 0x1111;
  char* not_our_code = s - 0x5555;
  char* not_our_code2 = e + 0x5555;

  // Exception in our code, but we have SEH filter => no dump.
  api.SetSEH(have_seh);
  api.SetStack(on_stack);
  EXPECT_CALL(api, WriteDump(_)).Times(0);
  EXPECT_EQ(ExceptionContinueSearch,
            veh.Handler(&ExceptionInfo(STATUS_ACCESS_VIOLATION, our_code)));
  testing::Mock::VerifyAndClearExpectations(&api);

  // RPC_E_DISCONNECTED (0x80010108) is "The object invoked has disconnected
  // from its clients", shall not be caught since it's a warning only.
  EXPECT_CALL(api, WriteDump(_)).Times(0);
  EXPECT_EQ(ExceptionContinueSearch,
      veh.Handler(&ExceptionInfo(RPC_E_DISCONNECTED, our_code)));
  testing::Mock::VerifyAndClearExpectations(&api);

  // Exception, not in our code, we do not have SEH and we are not in stack.
  api.SetSEH(no_seh);
  api.SetStack(not_on_stack);
  EXPECT_CALL(api, WriteDump(_)).Times(0);
  EXPECT_EQ(ExceptionContinueSearch,
      veh.Handler(&ExceptionInfo(STATUS_INTEGER_DIVIDE_BY_ZERO, not_our_code)));
  testing::Mock::VerifyAndClearExpectations(&api);

  // Exception, not in our code, no SEH, but we are on the stack.
  api.SetSEH(no_seh);
  api.SetStack(on_stack);
  EXPECT_CALL(api, WriteDump(_)).Times(1);
  EXPECT_EQ(ExceptionContinueSearch,
      veh.Handler(&ExceptionInfo(STATUS_INTEGER_DIVIDE_BY_ZERO,
                                 not_our_code2)));
  testing::Mock::VerifyAndClearExpectations(&api);

  // Exception, in our code, no SEH, not on stack (assume FPO screwed us)
  api.SetSEH(no_seh);
  api.SetStack(not_on_stack);
  EXPECT_CALL(api, WriteDump(_)).Times(1);
  EXPECT_EQ(ExceptionContinueSearch,
      veh.Handler(&ExceptionInfo(STATUS_PRIVILEGED_INSTRUCTION, our_code)));
  testing::Mock::VerifyAndClearExpectations(&api);

  // Exception, in IsBadStringPtrA, we are on the stack.
  api.SetSEH(no_seh);
  api.SetStack(on_stack);
  EXPECT_CALL(api, WriteDump(_)).Times(0);
  char* ignore_address = reinterpret_cast<char*>(GetProcAddress(
      GetModuleHandleA("kernel32.dll"), "IsBadStringPtrA")) + 10;
  EXPECT_EQ(ExceptionContinueSearch,
      veh.Handler(&ExceptionInfo(STATUS_ACCESS_VIOLATION, ignore_address)));
  testing::Mock::VerifyAndClearExpectations(&api);

  // Exception, in IsBadStringPtrA, we are not on the stack.
  api.SetSEH(no_seh);
  api.SetStack(not_on_stack);
  EXPECT_CALL(api, WriteDump(_)).Times(0);
  EXPECT_EQ(ExceptionContinueSearch,
      veh.Handler(&ExceptionInfo(STATUS_ACCESS_VIOLATION, ignore_address)));
  testing::Mock::VerifyAndClearExpectations(&api);
}

MATCHER_P(ExceptionCodeIs, code, "") {
  return (arg->ExceptionRecord->ExceptionCode == code);
}

void OverflowStack() {
  char tmp[1024 * 2048];
  ZeroMemory(tmp, sizeof(tmp));
}

DWORD WINAPI CrashingThread(PVOID tmp) {
  __try {
    OverflowStack();
  } __except(EXCEPTION_EXECUTE_HANDLER) {

  }

  // This will cause STATUS_ACCESS_VIOLATION
  __try {
    OverflowStack();
  } __except(EXCEPTION_EXECUTE_HANDLER) {

  }

  return 0;
}

static VectoredHandlerMock* g_mock_veh = NULL;
static LONG WINAPI VEH(EXCEPTION_POINTERS* exptrs) {
  return g_mock_veh->Handler(exptrs);
}

TEST(ChromeFrame, TrickyStackOverflow) {
  MockApi api;
  VectoredHandlerMock veh(&api);

  // Start address of our module.
  char* s = reinterpret_cast<char*>(0x30000000);
  char *e = s + 0x10000;
  api.SetModule(s, e);

  SEHChain no_seh(s - 0x1000, e + 0x7127, NULL);
  StackHelper on_stack(s - 0x11283, s - 0x278361, s + 0x9171, e + 1231, NULL);
  api.SetSEH(no_seh);
  api.SetStack(on_stack);

  EXPECT_CALL(api, WriteDump(ExceptionCodeIs(STATUS_STACK_OVERFLOW))).Times(1);

  g_mock_veh = &veh;
  void* id = ::AddVectoredExceptionHandler(FALSE, VEH);

  DWORD tid;
  HANDLE h = ::CreateThread(0, 0, CrashingThread, 0, 0, &tid);
  ::WaitForSingleObject(h, INFINITE);
  ::CloseHandle(h);

  EXPECT_EQ(2, veh.get_exceptions_seen());
  ::RemoveVectoredExceptionHandler(id);
  g_mock_veh = NULL;
}
<commit_msg>Committing for stoyan since we need this now...<commit_after>// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <atlbase.h>

#include "base/logging.h"
#include "chrome_frame/crash_reporting/vectored_handler-impl.h"
#include "chrome_frame/crash_reporting/veh_test.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

using testing::_;

ACTION_P(StackTraceDump, s) {
  memcpy(arg2, s->stack_, s->count_ * sizeof(s->stack_[0]));
  return s->count_;
}
namespace {
class MockApi : public Win32VEHTraits, public ModuleOfInterest {
 public:
  MockApi() {
    Win32VEHTraits::InitializeIgnoredBlocks();
  }

  MOCK_METHOD1(WriteDump, void(const EXCEPTION_POINTERS*));
  MOCK_METHOD0(RtlpGetExceptionList, const EXCEPTION_REGISTRATION_RECORD*());
  MOCK_METHOD4(RtlCaptureStackBackTrace, WORD(DWORD FramesToSkip,
      DWORD FramesToCapture, void** BackTrace, DWORD* BackTraceHash));

  // Helpers
  void SetSEH(const SEHChain& sehchain) {
    EXPECT_CALL(*this, RtlpGetExceptionList())
        .Times(testing::AnyNumber())
        .WillRepeatedly(testing::Return(sehchain.chain_));
  }

  void SetStack(const StackHelper& s) {
    EXPECT_CALL(*this, RtlCaptureStackBackTrace(_, _, _, _))
        .Times(testing::AnyNumber())
        .WillRepeatedly(StackTraceDump(&s));
  }

  enum {max_back_trace = 15};
};
};  // namespace

typedef VectoredHandlerT<MockApi> VectoredHandlerMock;
TEST(ChromeFrame, ExceptionReport) {
  MockApi api;
  VectoredHandlerMock veh(&api);

  // Start address of our module.
  char* s = reinterpret_cast<char*>(0x30000000);
  char *e = s + 0x10000;
  api.SetModule(s, e);

  SEHChain have_seh(s - 0x1000, s + 0x1000, e + 0x7127, NULL);
  SEHChain no_seh(s - 0x1000, e + 0x7127, NULL);
  StackHelper on_stack(s - 0x11283, s - 0x278361, s + 0x9171, e + 1231, NULL);
  StackHelper not_on_stack(s - 0x11283, s - 0x278361, e + 1231, NULL);

  char* our_code = s + 0x1111;
  char* not_our_code = s - 0x5555;
  char* not_our_code2 = e + 0x5555;

  // Exception in our code, but we have SEH filter => no dump.
  api.SetSEH(have_seh);
  api.SetStack(on_stack);
  EXPECT_CALL(api, WriteDump(_)).Times(0);
  EXPECT_EQ(ExceptionContinueSearch,
            veh.Handler(&ExceptionInfo(STATUS_ACCESS_VIOLATION, our_code)));
  testing::Mock::VerifyAndClearExpectations(&api);

  // RPC_E_DISCONNECTED (0x80010108) is "The object invoked has disconnected
  // from its clients", shall not be caught since it's a warning only.
  EXPECT_CALL(api, WriteDump(_)).Times(0);
  EXPECT_EQ(ExceptionContinueSearch,
      veh.Handler(&ExceptionInfo(RPC_E_DISCONNECTED, our_code)));
  testing::Mock::VerifyAndClearExpectations(&api);

  // Exception, not in our code, we do not have SEH and we are not in stack.
  api.SetSEH(no_seh);
  api.SetStack(not_on_stack);
  EXPECT_CALL(api, WriteDump(_)).Times(0);
  EXPECT_EQ(ExceptionContinueSearch,
      veh.Handler(&ExceptionInfo(STATUS_INTEGER_DIVIDE_BY_ZERO, not_our_code)));
  testing::Mock::VerifyAndClearExpectations(&api);

  // Exception, not in our code, no SEH, but we are on the stack.
  api.SetSEH(no_seh);
  api.SetStack(on_stack);
  EXPECT_CALL(api, WriteDump(_)).Times(1);
  EXPECT_EQ(ExceptionContinueSearch,
      veh.Handler(&ExceptionInfo(STATUS_INTEGER_DIVIDE_BY_ZERO,
                                 not_our_code2)));
  testing::Mock::VerifyAndClearExpectations(&api);

  // Exception, in our code, no SEH, not on stack (assume FPO screwed us)
  api.SetSEH(no_seh);
  api.SetStack(not_on_stack);
  EXPECT_CALL(api, WriteDump(_)).Times(1);
  EXPECT_EQ(ExceptionContinueSearch,
      veh.Handler(&ExceptionInfo(STATUS_PRIVILEGED_INSTRUCTION, our_code)));
  testing::Mock::VerifyAndClearExpectations(&api);

  // Exception, in IsBadStringPtrA, we are on the stack.
  api.SetSEH(no_seh);
  api.SetStack(on_stack);
  EXPECT_CALL(api, WriteDump(_)).Times(0);
  char* ignore_address = reinterpret_cast<char*>(GetProcAddress(
      GetModuleHandleA("kernel32.dll"), "IsBadStringPtrA")) + 10;
  EXPECT_EQ(ExceptionContinueSearch,
      veh.Handler(&ExceptionInfo(STATUS_ACCESS_VIOLATION, ignore_address)));
  testing::Mock::VerifyAndClearExpectations(&api);

  // Exception, in IsBadStringPtrA, we are not on the stack.
  api.SetSEH(no_seh);
  api.SetStack(not_on_stack);
  EXPECT_CALL(api, WriteDump(_)).Times(0);
  EXPECT_EQ(ExceptionContinueSearch,
      veh.Handler(&ExceptionInfo(STATUS_ACCESS_VIOLATION, ignore_address)));
  testing::Mock::VerifyAndClearExpectations(&api);
}

MATCHER_P(ExceptionCodeIs, code, "") {
  return (arg->ExceptionRecord->ExceptionCode == code);
}

DECLSPEC_NOINLINE static void OverflowStack() {
  char tmp[1024 * 2048] = {0};
  GetSystemInfo(reinterpret_cast<SYSTEM_INFO*>(&tmp));
}

DWORD WINAPI CrashingThread(PVOID tmp) {
  __try {
    OverflowStack();
  } __except(EXCEPTION_EXECUTE_HANDLER) {

  }

  // This will cause STATUS_ACCESS_VIOLATION
  __try {
    OverflowStack();
  } __except(EXCEPTION_EXECUTE_HANDLER) {

  }

  return 0;
}

static VectoredHandlerMock* g_mock_veh = NULL;
static LONG WINAPI VEH(EXCEPTION_POINTERS* exptrs) {
  return g_mock_veh->Handler(exptrs);
}

TEST(ChromeFrame, TrickyStackOverflow) {
  MockApi api;
  VectoredHandlerMock veh(&api);

  // Start address of our module.
  char* s = reinterpret_cast<char*>(0x30000000);
  char *e = s + 0x10000;
  api.SetModule(s, e);

  SEHChain no_seh(s - 0x1000, e + 0x7127, NULL);
  StackHelper on_stack(s - 0x11283, s - 0x278361, s + 0x9171, e + 1231, NULL);
  api.SetSEH(no_seh);
  api.SetStack(on_stack);

  EXPECT_CALL(api, WriteDump(ExceptionCodeIs(STATUS_STACK_OVERFLOW))).Times(1);

  g_mock_veh = &veh;
  void* id = ::AddVectoredExceptionHandler(FALSE, VEH);

  DWORD tid;
  HANDLE h = ::CreateThread(0, 0, CrashingThread, 0, 0, &tid);
  ::WaitForSingleObject(h, INFINITE);
  ::CloseHandle(h);

  EXPECT_EQ(2, veh.get_exceptions_seen());
  ::RemoveVectoredExceptionHandler(id);
  g_mock_veh = NULL;
}
<|endoftext|>


<commit_before>//
// Created by dc on 9/7/17.
//
#include <fcntl.h>
#include <sys/param.h>
#include "config.hpp"
#include "client.hpp"

namespace suil {
    namespace http {
        namespace client {

#undef  CRLF
#define CRLF "\r\n"

            size_t Form::encode(buffer_t &out) const {
                if (data.empty() && files.empty()) {
                    strace("trying to encode an empty form");
                    return 0;
                }

                if (encoding == URL_ENCODED) {
                    bool first{true};
                    for(auto& pair : data) {
                        if (!first) {
                            out << "&";
                        }
                        first = false;
                        out << pair.first << "=" << pair.second;
                    }

                    return out.size();
                } else {
                    /* multi-part encoded form */
                    for (auto& fd: data) {
                        out << "--" << boundary << CRLF;
                        out << "Content-Disposition: form-data; name=\""
                            << fd.first << "\"" << CRLF;
                        out << CRLF << fd.second << CRLF;
                    }

                    if (files.empty()) {
                        out << "--" << boundary << "--";
                        return out.size();
                    }

                    size_t  content_length{out.size()};
                    /* encode file into upload buffers */
                    for (auto& ff: files) {
                        buffer_t tmp(127);
                        tmp << "--" << boundary << CRLF;
                        tmp << "Content-Disposition: form-data; name=\""
                            << ff.name << "\"; filename=\"" << basename(ff.path.cstr)
                            << "\"" << CRLF;
                        if (ff.ctype) {
                            /* append content type header */
                            tmp << "Content-Type: " << ff.ctype << CRLF;
                        }
                        tmp << CRLF;
                        content_length += tmp.size() + ff.size;

                        Upload upload(&ff, std::move(zcstring(tmp)));
                        uploads.push_back(std::move(upload));
                        /* \r\n will added at the end of each upload */
                        content_length += sizeofcstr(CRLF);
                    }

                    /* --boundary--  will be appended */
                    content_length += sizeofcstr("----") + boundary.len;
                    return content_length;
                }
            }

            int Form::Upload::open() {
                if (fd > 0) {
                    return fd;
                }
                fd = ::open(file->path.cstr, O_RDWR);
                if (fd < 0) {
                    /* opening file failed */
                    throw suil_error::create("open '", file->path.cstr,
                                             "' failed: ", errno_s);
                }

                return fd;
            }

            void Form::Upload::close() {
                if (fd > 0) {
                    ::close(fd);
                    fd = -1;
                }
            }

            void response::receive(sock_adaptor &sock, int64_t timeout) {
                buffer_t tmp(1023);
                do {
                    size_t nrd = tmp.capacity();
                    if (!sock.read(&tmp[0], nrd, timeout)) {
                        /* failed to receive headers*/
                        throw suil_error::create("receiving request failed: ", errno_s);
                    }

                    if (!feed(tmp.data(), nrd)) {
                        throw suil_error::create("parsing headers failed: ",
                              http_errno_name((enum http_errno) http_errno));
                    }
                } while (!headers_complete);

                if (body_read || content_length == 0) {
                    strace("%s - response has no body to read: %lu", sock.id(), content_length);
                    return;
                }

                /* received and parse body */
                size_t len  = 0, left = content_length;
                // read body in chunks
                tmp.reserve(MIN(content_length, 8900));

                do {
                    len = MIN(tmp.capacity(), left);
                    tmp.reset(len, true);
                    if (!sock.receive(&tmp[0], len, timeout)) {
                        throw suil_error::create("receive failed: ", errno_s);
                    }

                    tmp.seek(len);
                    // parse header line
                    if (!feed(tmp, len)) {
                        throw suil_error::create("parsing  body failed: ",
                                     http_errno_name((enum http_errno )http_errno));
                    }
                    left -= len;
                    // no need to reset buffer
                } while (!body_complete && left > 0);
            }

            int response::handle_body_part(const char *at, size_t length) {
                if (reader == nullptr) {
                    if (body.empty()) {
                        /* reserve enough room for entire body */
                        body.reserve(content_length + 2);
                    }
                    return parser::handle_body_part(at, length);
                }
                else {
                    if (reader(at, length) == length) {
                        return 0;
                    }
                    return -1;
                }
            }

            int response::msg_complete() {
                if (reader) {
                    reader(nullptr, 0);
                }
                body_read = true;
            }

            const strview_t response::contenttype() const {
                auto it = headers.find("Content-Type");
                if (it != headers.end()) {
                    return it->second;
                }

                return strview_t();
            }

            request& request::operator=(request &&o) noexcept {
                if (this != &o) {
                    sock_ptr = o.sock_ptr;
                    sock = *sock_ptr;
                    headers = std::move(o.headers);
                    arguments = std::move(o.arguments);
                    method = o.method;
                    resource = std::move(o.resource);
                    form = std::move(o.form);
                    body = std::move(o.body);

                    o.sock_ptr = nullptr;
                    o.method = method_t::Unknown;
                    o.cleanup();
                }
                return *this;
            }

            request::request(request &&o) noexcept
                : sock_ptr(o.sock_ptr),
                  sock(*sock_ptr),
                  headers(std::move(o.headers)),
                  arguments(std::move(o.arguments)),
                  method(o.method),
                  resource(std::move(o.resource)),
                  form(std::move(o.form)),
                  body(std::move(o.body))
            {
                o.sock_ptr = nullptr;
                o.method = method_t::Unknown;
            }

            request& request::operator<<(Form &&f) {
                form = std::move(f);
                zcstring key("Content-Type");
                zcstring val;
                if (form.encoding == Form::URL_ENCODED) {
                    val = zcstring("application/x-www-form-urlencoded").dup();
                }
                else {
                    val = utils::catstr("multipart/form-data; boundary=", form.boundary);
                }
                headers.emplace(key.dup(), std::move(val));

                return *this;
            }

            void request::encodeargs(buffer_t &dst) const {
                if (!arguments.empty()) {
                    dst << "?";
                    bool first{true};
                    for(auto& arg: arguments) {
                        if (!first) {
                            dst << "&";
                        }
                        first = false;
                        dst << arg.first << "=" << arg.second;
                    }
                }
            }

            void request::encodehdrs(buffer_t &dst) const {
                if (!headers.empty()) {
                    for (auto& hdr: headers) {
                        dst << hdr.first << ": " << hdr.second << CRLF;
                    }
                }
            }

            size_t request::buildbody() {
                size_t content_length{0};
                if (utils::matchany(method, method_t::Put, method_t::Post)) {
                    if (form) {
                        /*encode form if available */
                        content_length = form.encode(body);
                    }
                    else {
                        content_length = body.size();
                    }
                }

                return content_length;
            }

            void request::submit(int timeout) {
                buffer_t head(1023);
                size_t  content_length{buildbody()};
                hdrs("Content-Length", content_length);

                head << method_name(method) << " " << resource;
                encodeargs(head);
                head << " HTTP/1.1" << CRLF;
                hdrs("Date", datetime()(datetime::HTTP_FMT));
                encodehdrs(head);
                head << CRLF;

                size_t nwr = sock.send(head.data(), head.size(), timeout);
                if (nwr != head.size()) {
                    /* sending headers failed, no need to send body */
                    throw suil_error::create("send request failed: ", errno_s);
                }
                if (content_length == 0) {
                    sock.flush();
                    return;
                }

                if (!body.empty()) {
                    /* send body */
                    nwr = sock.send(body.data(), body.size(), timeout);
                    if (nwr != body.size()) {
                        /* sending headers failed, no need to send body */
                        throw suil_error::create("send request failed: ", errno_s);
                    }
                }

                if (!form.uploads.empty()) {
                    /* send upload files one after the other */
                    for (auto &up: form.uploads) {
                        int fd = up.open();
                        nwr = sock.send(up.head.cstr, up.head.len, timeout);
                        if (nwr != up.head.len) {
                            /* sending upload head failed, no need to send body */
                            throw suil_error::create("send request failed: ", errno_s);
                        }

                        /* send file */
                        nwr = sock.sendfile(fd, 0, up.file->size, timeout);
                        if (nwr != up.file->size) {
                            throw suil_error::create("uploading file: '", up.file->path.cstr,
                                                     "' failed: ", errno_s);
                        }

                        nwr = sock.send(CRLF, sizeofcstr(CRLF), timeout);
                        if (nwr != sizeofcstr(CRLF)) {
                            throw suil_error::create("send CRLF failed: ", errno_s);
                        }

                        /* close boundary*/
                        up.close();
                    }

                    /* send end of boundary tag */
                    head.reset(1023, true);
                    head << "--" << form.boundary << "--"
                         << CRLF;

                    nwr = sock.send(head.data(), head.size(), timeout);
                    if (nwr != head.size()) {
                        throw suil_error::create("send terminal boundary failed: ", errno_s);
                    }
                }
                sock.flush();
            }

            response session::perform(handle_t& h, method_t m, const char *resource, request_builder_t& builder, response_writer_t& rd) {
                request& req = h.req;
                response resp;
                req.reset(m, resource, false);

                for(auto& hdr: headers) {
                    zcstring key(hdr.first.cstr, hdr.first.len, false);
                    zcstring val(hdr.second.cstr, hdr.second.len, false);
                    req.hdr(std::move(key), std::move(val));
                }

                if (builder != nullptr && !builder(req)) {
                    throw suil_error::create("building request '", resource, "' failed");
                }

                if (!req.sock.isopen()) {
                    /* open a new socket for the request */
                    if (!req.sock.connect(addr, timeout)) {
                        throw suil_error::create("Connecting to '", host.cstr, ":",
                                                 port, "' failed: ", errno_s);
                    }
                }

                req.submit(timeout);
                resp.reader = rd;
                resp.receive(req.sock, timeout);
                return std::move(resp);
            }

            void session::connect(handle_t &h, zcstr_map_t<zcstring> hdrs) {
                response resp = std::move(perform(h, method_t::Connect, "/"));
                if (resp.status() != status_t::OK) {
                    /* connecting to server failed */
                    throw suil_error::create("sending CONNECT to '", host.cstr, "' failed: ",
                            http::status_text(resp.status()));
                }

                /* cleanup request and return it fresh */
                h.req.cleanup();
            }

            response session::head(handle_t &h, const char *resource, zcstr_map_t<zcstring> hdr) {

                response resp = std::move(perform(h, method_t::Connect, resource));
            }

#undef CRLF
        }
    }
}<commit_msg>Adding http client<commit_after>//
// Created by dc on 9/7/17.
//
#include <fcntl.h>
#include <libgen.h>
#include <sys/param.h>
#include "config.hpp"
#include "client.hpp"

namespace suil {
    namespace http {
        namespace client {

#undef  CRLF
#define CRLF "\r\n"

            size_t Form::encode(buffer_t &out) const {
                if (data.empty() && files.empty()) {
                    strace("trying to encode an empty form");
                    return 0;
                }

                if (encoding == URL_ENCODED) {
                    bool first{true};
                    for(auto& pair : data) {
                        if (!first) {
                            out << "&";
                        }
                        first = false;
                        out << pair.first << "=" << pair.second;
                    }

                    return out.size();
                } else {
                    /* multi-part encoded form */
                    for (auto& fd: data) {
                        out << "--" << boundary << CRLF;
                        out << "Content-Disposition: form-data; name=\""
                            << fd.first << "\"" << CRLF;
                        out << CRLF << fd.second << CRLF;
                    }

                    if (files.empty()) {
                        out << "--" << boundary << "--";
                        return out.size();
                    }

                    size_t  content_length{out.size()};
                    /* encode file into upload buffers */
                    for (auto& ff: files) {
                        buffer_t tmp(127);
                        tmp << "--" << boundary << CRLF;
                        tmp << "Content-Disposition: form-data; name=\""
                            << ff.name << "\"; filename=\"" << ::basename(ff.path.cstr)
                            << "\"" << CRLF;
                        if (ff.ctype) {
                            /* append content type header */
                            tmp << "Content-Type: " << ff.ctype << CRLF;
                        }
                        tmp << CRLF;
                        content_length += tmp.size() + ff.size;

                        Upload upload(&ff, std::move(zcstring(tmp)));
                        uploads.push_back(std::move(upload));
                        /* \r\n will added at the end of each upload */
                        content_length += sizeofcstr(CRLF);
                    }

                    /* --boundary--  will be appended */
                    content_length += sizeofcstr("----") + boundary.len;
                    return content_length;
                }
            }

            int Form::Upload::open() {
                if (fd > 0) {
                    return fd;
                }
                fd = ::open(file->path.cstr, O_RDWR);
                if (fd < 0) {
                    /* opening file failed */
                    throw suil_error::create("open '", file->path.cstr,
                                             "' failed: ", errno_s);
                }

                return fd;
            }

            void Form::Upload::close() {
                if (fd > 0) {
                    ::close(fd);
                    fd = -1;
                }
            }

            void response::receive(sock_adaptor &sock, int64_t timeout) {
                buffer_t tmp(1023);
                do {
                    size_t nrd = tmp.capacity();
                    if (!sock.read(&tmp[0], nrd, timeout)) {
                        /* failed to receive headers*/
                        throw suil_error::create("receiving request failed: ", errno_s);
                    }

                    if (!feed(tmp.data(), nrd)) {
                        throw suil_error::create("parsing headers failed: ",
                              http_errno_name((enum http_errno) http_errno));
                    }
                } while (!headers_complete);

                if (body_read || content_length == 0) {
                    strace("%s - response has no body to read: %lu", sock.id(), content_length);
                    return;
                }

                /* received and parse body */
                size_t len  = 0, left = content_length;
                // read body in chunks
                tmp.reserve(MIN(content_length, 8900));

                do {
                    len = MIN(tmp.capacity(), left);
                    tmp.reset(len, true);
                    if (!sock.receive(&tmp[0], len, timeout)) {
                        throw suil_error::create("receive failed: ", errno_s);
                    }

                    tmp.seek(len);
                    // parse header line
                    if (!feed(tmp, len)) {
                        throw suil_error::create("parsing  body failed: ",
                                     http_errno_name((enum http_errno )http_errno));
                    }
                    left -= len;
                    // no need to reset buffer
                } while (!body_complete && left > 0);
            }

            int response::handle_body_part(const char *at, size_t length) {
                if (reader == nullptr) {
                    if (body.empty()) {
                        /* reserve enough room for entire body */
                        body.reserve(content_length + 2);
                    }
                    return parser::handle_body_part(at, length);
                }
                else {
                    if (reader(at, length) == length) {
                        return 0;
                    }
                    return -1;
                }
            }

            int response::msg_complete() {
                if (reader) {
                    reader(nullptr, 0);
                }
                body_read = true;
            }

            const strview_t response::contenttype() const {
                auto it = headers.find("Content-Type");
                if (it != headers.end()) {
                    return it->second;
                }

                return strview_t();
            }

            request& request::operator=(request &&o) noexcept {
                if (this != &o) {
                    sock_ptr = o.sock_ptr;
                    sock = *sock_ptr;
                    headers = std::move(o.headers);
                    arguments = std::move(o.arguments);
                    method = o.method;
                    resource = std::move(o.resource);
                    form = std::move(o.form);
                    body = std::move(o.body);

                    o.sock_ptr = nullptr;
                    o.method = method_t::Unknown;
                    o.cleanup();
                }
                return *this;
            }

            request::request(request &&o) noexcept
                : sock_ptr(o.sock_ptr),
                  sock(*sock_ptr),
                  headers(std::move(o.headers)),
                  arguments(std::move(o.arguments)),
                  method(o.method),
                  resource(std::move(o.resource)),
                  form(std::move(o.form)),
                  body(std::move(o.body))
            {
                o.sock_ptr = nullptr;
                o.method = method_t::Unknown;
            }

            request& request::operator<<(Form &&f) {
                form = std::move(f);
                zcstring key("Content-Type");
                zcstring val;
                if (form.encoding == Form::URL_ENCODED) {
                    val = zcstring("application/x-www-form-urlencoded").dup();
                }
                else {
                    val = utils::catstr("multipart/form-data; boundary=", form.boundary);
                }
                headers.emplace(key.dup(), std::move(val));

                return *this;
            }

            void request::encodeargs(buffer_t &dst) const {
                if (!arguments.empty()) {
                    dst << "?";
                    bool first{true};
                    for(auto& arg: arguments) {
                        if (!first) {
                            dst << "&";
                        }
                        first = false;
                        dst << arg.first << "=" << arg.second;
                    }
                }
            }

            void request::encodehdrs(buffer_t &dst) const {
                if (!headers.empty()) {
                    for (auto& hdr: headers) {
                        dst << hdr.first << ": " << hdr.second << CRLF;
                    }
                }
            }

            size_t request::buildbody() {
                size_t content_length{0};
                if (utils::matchany(method, method_t::Put, method_t::Post)) {
                    if (form) {
                        /*encode form if available */
                        content_length = form.encode(body);
                    }
                    else {
                        content_length = body.size();
                    }
                }

                return content_length;
            }

            void request::submit(int timeout) {
                buffer_t head(1023);
                size_t  content_length{buildbody()};
                hdrs("Content-Length", content_length);

                head << method_name(method) << " " << resource;
                encodeargs(head);
                head << " HTTP/1.1" << CRLF;
                hdrs("Date", datetime()(datetime::HTTP_FMT));
                encodehdrs(head);
                head << CRLF;

                size_t nwr = sock.send(head.data(), head.size(), timeout);
                if (nwr != head.size()) {
                    /* sending headers failed, no need to send body */
                    throw suil_error::create("send request failed: ", errno_s);
                }
                if (content_length == 0) {
                    sock.flush();
                    return;
                }

                if (!body.empty()) {
                    /* send body */
                    nwr = sock.send(body.data(), body.size(), timeout);
                    if (nwr != body.size()) {
                        /* sending headers failed, no need to send body */
                        throw suil_error::create("send request failed: ", errno_s);
                    }
                }

                if (!form.uploads.empty()) {
                    /* send upload files one after the other */
                    for (auto &up: form.uploads) {
                        int fd = up.open();
                        nwr = sock.send(up.head.cstr, up.head.len, timeout);
                        if (nwr != up.head.len) {
                            /* sending upload head failed, no need to send body */
                            throw suil_error::create("send request failed: ", errno_s);
                        }

                        /* send file */
                        nwr = sock.sendfile(fd, 0, up.file->size, timeout);
                        if (nwr != up.file->size) {
                            throw suil_error::create("uploading file: '", up.file->path.cstr,
                                                     "' failed: ", errno_s);
                        }

                        nwr = sock.send(CRLF, sizeofcstr(CRLF), timeout);
                        if (nwr != sizeofcstr(CRLF)) {
                            throw suil_error::create("send CRLF failed: ", errno_s);
                        }

                        /* close boundary*/
                        up.close();
                    }

                    /* send end of boundary tag */
                    head.reset(1023, true);
                    head << "--" << form.boundary << "--"
                         << CRLF;

                    nwr = sock.send(head.data(), head.size(), timeout);
                    if (nwr != head.size()) {
                        throw suil_error::create("send terminal boundary failed: ", errno_s);
                    }
                }
                sock.flush();
            }

            response session::perform(handle_t& h, method_t m, const char *resource, request_builder_t& builder, response_writer_t& rd) {
                request& req = h.req;
                response resp;
                req.reset(m, resource, false);

                for(auto& hdr: headers) {
                    zcstring key(hdr.first.cstr, hdr.first.len, false);
                    zcstring val(hdr.second.cstr, hdr.second.len, false);
                    req.hdr(std::move(key), std::move(val));
                }

                if (builder != nullptr && !builder(req)) {
                    throw suil_error::create("building request '", resource, "' failed");
                }

                if (!req.sock.isopen()) {
                    /* open a new socket for the request */
                    if (!req.sock.connect(addr, timeout)) {
                        throw suil_error::create("Connecting to '", host.cstr, ":",
                                                 port, "' failed: ", errno_s);
                    }
                }

                req.submit(timeout);
                resp.reader = rd;
                resp.receive(req.sock, timeout);
                return std::move(resp);
            }

            void session::connect(handle_t &h, zcstr_map_t<zcstring> hdrs) {
                response resp = std::move(perform(h, method_t::Connect, "/"));
                if (resp.status() != status_t::OK) {
                    /* connecting to server failed */
                    throw suil_error::create("sending CONNECT to '", host.cstr, "' failed: ",
                            http::status_text(resp.status()));
                }

                /* cleanup request and return it fresh */
                h.req.cleanup();
            }

            response session::head(handle_t &h, const char *resource, zcstr_map_t<zcstring> hdr) {

                response resp = std::move(perform(h, method_t::Connect, resource));
            }

#undef CRLF
        }
    }
}<|endoftext|>


<commit_before>// This file is distributed under the MIT license.
// See the LICENSE file for details.

#include <cstddef>
#include <sstream>

#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>

#include <visionaray/math/serialization.h>

#include <gtest/gtest.h>

using namespace visionaray;

TEST(Serialization, AABB)
{
    // use as input
    basic_aabb<int>    dsti;
    basic_aabb<float>  dstf;
    basic_aabb<double> dstd;

    // test some invalid boxes
    basic_aabb<int>    srci;
    basic_aabb<float>  srcf;
    basic_aabb<double> srcd;

    srci.invalidate();
    srcf.invalidate();
    srcd.invalidate();

    std::stringstream sstream;
    boost::archive::text_oarchive oa(sstream);

    oa << srci;
    oa << srcf;
    oa << srcd;

    boost::archive::text_iarchive ia(sstream);

    ia >> dsti;
    ia >> dstf;
    ia >> dstd;

    ASSERT_EQ(dsti.min, srci.min);
    ASSERT_EQ(dsti.max, srci.max);
    EXPECT_FLOAT_EQ(dstf.min.x, srcf.min.x);
    EXPECT_FLOAT_EQ(dstf.min.y, srcf.min.y);
    EXPECT_FLOAT_EQ(dstf.min.z, srcf.min.z);
    EXPECT_FLOAT_EQ(dstf.max.x, srcf.max.x);
    EXPECT_FLOAT_EQ(dstf.max.y, srcf.max.y);
    EXPECT_FLOAT_EQ(dstf.max.z, srcf.max.z);
    EXPECT_DOUBLE_EQ(dstd.min.x, srcd.min.x);
    EXPECT_DOUBLE_EQ(dstd.min.y, srcd.min.y);
    EXPECT_DOUBLE_EQ(dstd.min.z, srcd.min.z);
    EXPECT_DOUBLE_EQ(dstd.max.x, srcd.max.x);
    EXPECT_DOUBLE_EQ(dstd.max.y, srcd.max.y);
    EXPECT_DOUBLE_EQ(dstd.max.z, srcd.max.z);
}

TEST(Serialization, Matrix)
{
    matrix<3, 3, float>  dst3f;
    matrix<3, 3, double> dst3d;
    matrix<4, 4, float>  dst4f;
    matrix<4, 4, double> dst4d;

    matrix<3, 3, float>  src3f(
            0.0f, 1.0f, 2.0f,
            3.0f, 4.0f, 5.0f,
            6.0f, 7.0f, 8.0f
            );
    matrix<3, 3, double> src3d(
            0.0, 1.0, 2.0,
            3.0, 4.0, 5.0,
            6.0, 7.0, 8.0
            );
    matrix<4, 4, float> src4f(
             0.0f,  1.0f,  2.0f,  3.0f,
             4.0f,  5.0f,  6.0f,  7.0f,
             8.0f,  9.0f, 10.0f, 11.0f,
            12.0f, 13.0f, 14.0f, 15.0f
            );
    matrix<4, 4, double> src4d(
             0.0,  1.0,  2.0,  3.0,
             4.0,  5.0,  6.0,  7.0,
             8.0,  9.0, 10.0, 11.0,
            12.0, 13.0, 14.0, 15.0
            );

    std::stringstream sstream;
    boost::archive::text_oarchive oa(sstream);

    oa << src3f;
    oa << src3d;
    oa << src4f;
    oa << src4d;

    boost::archive::text_iarchive ia(sstream);

    ia >> dst3f;
    ia >> dst3d;
    ia >> dst4f;
    ia >> dst4d;

    for (size_t i = 0; i < 4; ++i)
    {
        for (size_t j = 0; j < 4; ++j)
        {
            if (i < 3 && j < 3)
            {
                EXPECT_FLOAT_EQ(dst3f(i, j), src3f(i, j));
                EXPECT_DOUBLE_EQ(dst3d(i, j), src3d(i, j));
            }
            EXPECT_FLOAT_EQ(dst4f(i, j), src4f(i, j));
            EXPECT_DOUBLE_EQ(dst4d(i, j), src4d(i, j));
        }
    }
}

TEST(Serialization, RectangleXYWH)
{
    rectangle<xywh_layout<int>, int>       dsti;
    rectangle<xywh_layout<float>, float>   dstf;
    rectangle<xywh_layout<double>, double> dstd;

    rectangle<xywh_layout<int>, int>       srci(0, 0, 1024, 768);
    rectangle<xywh_layout<float>, float>   srcf(-3.14f, -314.15f, 6.28f, 628.31f);
    rectangle<xywh_layout<double>, double> srcd(700.0, 700.0, 0.0, 0.0);

    std::stringstream sstream;
    boost::archive::text_oarchive oa(sstream);

    oa << srci;
    oa << srcf;
    oa << srcd;

    boost::archive::text_iarchive ia(sstream);

    ia >> dsti;
    ia >> dstf;
    ia >> dstd;

    ASSERT_EQ(dsti.x, srci.x);
    ASSERT_EQ(dsti.y, srci.y);
    ASSERT_EQ(dsti.w, srci.w);
    ASSERT_EQ(dsti.h, srci.h);

    EXPECT_FLOAT_EQ(dstf.x, srcf.x);
    EXPECT_FLOAT_EQ(dstf.y, srcf.y);
    EXPECT_FLOAT_EQ(dstf.w, srcf.w);
    EXPECT_FLOAT_EQ(dstf.h, srcf.h);

    EXPECT_DOUBLE_EQ(dstd.x, srcd.x);
    EXPECT_DOUBLE_EQ(dstd.y, srcd.y);
    EXPECT_DOUBLE_EQ(dstd.w, srcd.w);
    EXPECT_DOUBLE_EQ(dstd.h, srcd.h);
}

TEST(Serialization, Vector)
{
    float f1[] = { 3.14f };
    float f2[] = { 3.14f, 3.15f };
    float f3[] = { 3.14f, 3.15f, 3.16f };
    float f4[] = { 3.14f, 3.15f, 3.16f, 3.17f };
    float f5[] = { 3.14f, 3.15f, 3.16f, 3.17f, 3.18f };
    float f6[] = { 3.14f, 3.15f, 3.16f, 3.17f, 3.18f, 3.19f };
    float f7[] = { 3.14f, 3.15f, 3.16f, 3.17f, 3.18f, 3.19f, 3.20f };

    vector<1, float> src1f(f1);
    vector<2, float> src2f(f2);
    vector<3, float> src3f(f3);
    vector<4, float> src4f(f4);
    vector<5, float> src5f(f5);
    vector<6, float> src6f(f6);
    vector<7, float> src7f(f7);

    vector<1, float> dst1f;
    vector<2, float> dst2f;
    vector<3, float> dst3f;
    vector<4, float> dst4f;
    vector<5, float> dst5f;
    vector<6, float> dst6f;
    vector<7, float> dst7f;

    std::stringstream sstream;
    boost::archive::text_oarchive oa(sstream);

    oa << src1f;
    oa << src2f;
    oa << src3f;
    oa << src4f;
    oa << src5f;
    oa << src6f;
    oa << src7f;

    boost::archive::text_iarchive ia(sstream);

    ia >> dst1f;
    ia >> dst2f;
    ia >> dst3f;
    ia >> dst4f;
    ia >> dst5f;
    ia >> dst6f;
    ia >> dst7f;

    EXPECT_FLOAT_EQ(dst1f[0], src1f[0]);

    EXPECT_FLOAT_EQ(dst2f[0], src2f[0]);
    EXPECT_FLOAT_EQ(dst2f[1], src2f[1]);

    EXPECT_FLOAT_EQ(dst3f[0], src3f[0]);
    EXPECT_FLOAT_EQ(dst3f[1], src3f[1]);
    EXPECT_FLOAT_EQ(dst3f[2], src3f[2]);

    EXPECT_FLOAT_EQ(dst4f[0], src4f[0]);
    EXPECT_FLOAT_EQ(dst4f[1], src4f[1]);
    EXPECT_FLOAT_EQ(dst4f[2], src4f[2]);
    EXPECT_FLOAT_EQ(dst4f[3], src4f[3]);

    EXPECT_FLOAT_EQ(dst5f[0], src5f[0]);
    EXPECT_FLOAT_EQ(dst5f[1], src5f[1]);
    EXPECT_FLOAT_EQ(dst5f[2], src5f[2]);
    EXPECT_FLOAT_EQ(dst5f[3], src5f[3]);
    EXPECT_FLOAT_EQ(dst5f[4], src5f[4]);

    EXPECT_FLOAT_EQ(dst6f[0], src6f[0]);
    EXPECT_FLOAT_EQ(dst6f[1], src6f[1]);
    EXPECT_FLOAT_EQ(dst6f[2], src6f[2]);
    EXPECT_FLOAT_EQ(dst6f[3], src6f[3]);
    EXPECT_FLOAT_EQ(dst6f[4], src6f[4]);
    EXPECT_FLOAT_EQ(dst6f[5], src6f[5]);

    EXPECT_FLOAT_EQ(dst7f[0], src7f[0]);
    EXPECT_FLOAT_EQ(dst7f[1], src7f[1]);
    EXPECT_FLOAT_EQ(dst7f[2], src7f[2]);
    EXPECT_FLOAT_EQ(dst7f[3], src7f[3]);
    EXPECT_FLOAT_EQ(dst7f[4], src7f[4]);
    EXPECT_FLOAT_EQ(dst7f[5], src7f[5]);
    EXPECT_FLOAT_EQ(dst7f[6], src7f[6]);
}
<commit_msg>Use ASSERT_XX() only for fatal errors<commit_after>// This file is distributed under the MIT license.
// See the LICENSE file for details.

#include <cstddef>
#include <sstream>

#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>

#include <visionaray/math/serialization.h>

#include <gtest/gtest.h>

using namespace visionaray;

TEST(Serialization, AABB)
{
    // use as input
    basic_aabb<int>    dsti;
    basic_aabb<float>  dstf;
    basic_aabb<double> dstd;

    // test some invalid boxes
    basic_aabb<int>    srci;
    basic_aabb<float>  srcf;
    basic_aabb<double> srcd;

    srci.invalidate();
    srcf.invalidate();
    srcd.invalidate();

    std::stringstream sstream;
    boost::archive::text_oarchive oa(sstream);

    oa << srci;
    oa << srcf;
    oa << srcd;

    boost::archive::text_iarchive ia(sstream);

    ia >> dsti;
    ia >> dstf;
    ia >> dstd;

    EXPECT_EQ(dsti.min, srci.min);
    EXPECT_EQ(dsti.max, srci.max);
    EXPECT_FLOAT_EQ(dstf.min.x, srcf.min.x);
    EXPECT_FLOAT_EQ(dstf.min.y, srcf.min.y);
    EXPECT_FLOAT_EQ(dstf.min.z, srcf.min.z);
    EXPECT_FLOAT_EQ(dstf.max.x, srcf.max.x);
    EXPECT_FLOAT_EQ(dstf.max.y, srcf.max.y);
    EXPECT_FLOAT_EQ(dstf.max.z, srcf.max.z);
    EXPECT_DOUBLE_EQ(dstd.min.x, srcd.min.x);
    EXPECT_DOUBLE_EQ(dstd.min.y, srcd.min.y);
    EXPECT_DOUBLE_EQ(dstd.min.z, srcd.min.z);
    EXPECT_DOUBLE_EQ(dstd.max.x, srcd.max.x);
    EXPECT_DOUBLE_EQ(dstd.max.y, srcd.max.y);
    EXPECT_DOUBLE_EQ(dstd.max.z, srcd.max.z);
}

TEST(Serialization, Matrix)
{
    matrix<3, 3, float>  dst3f;
    matrix<3, 3, double> dst3d;
    matrix<4, 4, float>  dst4f;
    matrix<4, 4, double> dst4d;

    matrix<3, 3, float>  src3f(
            0.0f, 1.0f, 2.0f,
            3.0f, 4.0f, 5.0f,
            6.0f, 7.0f, 8.0f
            );
    matrix<3, 3, double> src3d(
            0.0, 1.0, 2.0,
            3.0, 4.0, 5.0,
            6.0, 7.0, 8.0
            );
    matrix<4, 4, float> src4f(
             0.0f,  1.0f,  2.0f,  3.0f,
             4.0f,  5.0f,  6.0f,  7.0f,
             8.0f,  9.0f, 10.0f, 11.0f,
            12.0f, 13.0f, 14.0f, 15.0f
            );
    matrix<4, 4, double> src4d(
             0.0,  1.0,  2.0,  3.0,
             4.0,  5.0,  6.0,  7.0,
             8.0,  9.0, 10.0, 11.0,
            12.0, 13.0, 14.0, 15.0
            );

    std::stringstream sstream;
    boost::archive::text_oarchive oa(sstream);

    oa << src3f;
    oa << src3d;
    oa << src4f;
    oa << src4d;

    boost::archive::text_iarchive ia(sstream);

    ia >> dst3f;
    ia >> dst3d;
    ia >> dst4f;
    ia >> dst4d;

    for (size_t i = 0; i < 4; ++i)
    {
        for (size_t j = 0; j < 4; ++j)
        {
            if (i < 3 && j < 3)
            {
                EXPECT_FLOAT_EQ(dst3f(i, j), src3f(i, j));
                EXPECT_DOUBLE_EQ(dst3d(i, j), src3d(i, j));
            }
            EXPECT_FLOAT_EQ(dst4f(i, j), src4f(i, j));
            EXPECT_DOUBLE_EQ(dst4d(i, j), src4d(i, j));
        }
    }
}

TEST(Serialization, RectangleXYWH)
{
    rectangle<xywh_layout<int>, int>       dsti;
    rectangle<xywh_layout<float>, float>   dstf;
    rectangle<xywh_layout<double>, double> dstd;

    rectangle<xywh_layout<int>, int>       srci(0, 0, 1024, 768);
    rectangle<xywh_layout<float>, float>   srcf(-3.14f, -314.15f, 6.28f, 628.31f);
    rectangle<xywh_layout<double>, double> srcd(700.0, 700.0, 0.0, 0.0);

    std::stringstream sstream;
    boost::archive::text_oarchive oa(sstream);

    oa << srci;
    oa << srcf;
    oa << srcd;

    boost::archive::text_iarchive ia(sstream);

    ia >> dsti;
    ia >> dstf;
    ia >> dstd;

    EXPECT_EQ(dsti.x, srci.x);
    EXPECT_EQ(dsti.y, srci.y);
    EXPECT_EQ(dsti.w, srci.w);
    EXPECT_EQ(dsti.h, srci.h);

    EXPECT_FLOAT_EQ(dstf.x, srcf.x);
    EXPECT_FLOAT_EQ(dstf.y, srcf.y);
    EXPECT_FLOAT_EQ(dstf.w, srcf.w);
    EXPECT_FLOAT_EQ(dstf.h, srcf.h);

    EXPECT_DOUBLE_EQ(dstd.x, srcd.x);
    EXPECT_DOUBLE_EQ(dstd.y, srcd.y);
    EXPECT_DOUBLE_EQ(dstd.w, srcd.w);
    EXPECT_DOUBLE_EQ(dstd.h, srcd.h);
}

TEST(Serialization, Vector)
{
    float f1[] = { 3.14f };
    float f2[] = { 3.14f, 3.15f };
    float f3[] = { 3.14f, 3.15f, 3.16f };
    float f4[] = { 3.14f, 3.15f, 3.16f, 3.17f };
    float f5[] = { 3.14f, 3.15f, 3.16f, 3.17f, 3.18f };
    float f6[] = { 3.14f, 3.15f, 3.16f, 3.17f, 3.18f, 3.19f };
    float f7[] = { 3.14f, 3.15f, 3.16f, 3.17f, 3.18f, 3.19f, 3.20f };

    vector<1, float> src1f(f1);
    vector<2, float> src2f(f2);
    vector<3, float> src3f(f3);
    vector<4, float> src4f(f4);
    vector<5, float> src5f(f5);
    vector<6, float> src6f(f6);
    vector<7, float> src7f(f7);

    vector<1, float> dst1f;
    vector<2, float> dst2f;
    vector<3, float> dst3f;
    vector<4, float> dst4f;
    vector<5, float> dst5f;
    vector<6, float> dst6f;
    vector<7, float> dst7f;

    std::stringstream sstream;
    boost::archive::text_oarchive oa(sstream);

    oa << src1f;
    oa << src2f;
    oa << src3f;
    oa << src4f;
    oa << src5f;
    oa << src6f;
    oa << src7f;

    boost::archive::text_iarchive ia(sstream);

    ia >> dst1f;
    ia >> dst2f;
    ia >> dst3f;
    ia >> dst4f;
    ia >> dst5f;
    ia >> dst6f;
    ia >> dst7f;

    EXPECT_FLOAT_EQ(dst1f[0], src1f[0]);

    EXPECT_FLOAT_EQ(dst2f[0], src2f[0]);
    EXPECT_FLOAT_EQ(dst2f[1], src2f[1]);

    EXPECT_FLOAT_EQ(dst3f[0], src3f[0]);
    EXPECT_FLOAT_EQ(dst3f[1], src3f[1]);
    EXPECT_FLOAT_EQ(dst3f[2], src3f[2]);

    EXPECT_FLOAT_EQ(dst4f[0], src4f[0]);
    EXPECT_FLOAT_EQ(dst4f[1], src4f[1]);
    EXPECT_FLOAT_EQ(dst4f[2], src4f[2]);
    EXPECT_FLOAT_EQ(dst4f[3], src4f[3]);

    EXPECT_FLOAT_EQ(dst5f[0], src5f[0]);
    EXPECT_FLOAT_EQ(dst5f[1], src5f[1]);
    EXPECT_FLOAT_EQ(dst5f[2], src5f[2]);
    EXPECT_FLOAT_EQ(dst5f[3], src5f[3]);
    EXPECT_FLOAT_EQ(dst5f[4], src5f[4]);

    EXPECT_FLOAT_EQ(dst6f[0], src6f[0]);
    EXPECT_FLOAT_EQ(dst6f[1], src6f[1]);
    EXPECT_FLOAT_EQ(dst6f[2], src6f[2]);
    EXPECT_FLOAT_EQ(dst6f[3], src6f[3]);
    EXPECT_FLOAT_EQ(dst6f[4], src6f[4]);
    EXPECT_FLOAT_EQ(dst6f[5], src6f[5]);

    EXPECT_FLOAT_EQ(dst7f[0], src7f[0]);
    EXPECT_FLOAT_EQ(dst7f[1], src7f[1]);
    EXPECT_FLOAT_EQ(dst7f[2], src7f[2]);
    EXPECT_FLOAT_EQ(dst7f[3], src7f[3]);
    EXPECT_FLOAT_EQ(dst7f[4], src7f[4]);
    EXPECT_FLOAT_EQ(dst7f[5], src7f[5]);
    EXPECT_FLOAT_EQ(dst7f[6], src7f[6]);
}
<|endoftext|>


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

#include "net/base/network_change_notifier_win.h"

#include <iphlpapi.h>
#include <winsock2.h>

#pragma comment(lib, "iphlpapi.lib")

namespace net {

NetworkChangeNotifierWin::NetworkChangeNotifierWin() {
  memset(&addr_overlapped_, 0, sizeof addr_overlapped_);
  addr_overlapped_.hEvent = WSACreateEvent();
}

NetworkChangeNotifierWin::~NetworkChangeNotifierWin() {
  CancelIPChangeNotify(&addr_overlapped_);
  addr_watcher_.StopWatching();
  WSACloseEvent(addr_overlapped_.hEvent);
}

void NetworkChangeNotifierWin::OnObjectSignaled(HANDLE object) {
  NotifyObserversOfIPAddressChange();

  // Start watching for the next address change.
  WatchForAddressChange();
}

void NetworkChangeNotifierWin::WatchForAddressChange() {
  HANDLE handle = NULL;
  DWORD ret = NotifyAddrChange(&handle, &addr_overlapped_);
  CHECK(ret == ERROR_IO_PENDING);
  addr_watcher_.StartWatching(addr_overlapped_.hEvent, this);
}

}  // namespace net
<commit_msg>Fix regression where we didn't ever start watching for network address changes on Windows.<commit_after>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "net/base/network_change_notifier_win.h"

#include <iphlpapi.h>
#include <winsock2.h>

#pragma comment(lib, "iphlpapi.lib")

namespace net {

NetworkChangeNotifierWin::NetworkChangeNotifierWin() {
  memset(&addr_overlapped_, 0, sizeof addr_overlapped_);
  addr_overlapped_.hEvent = WSACreateEvent();
  WatchForAddressChange();
}

NetworkChangeNotifierWin::~NetworkChangeNotifierWin() {
  CancelIPChangeNotify(&addr_overlapped_);
  addr_watcher_.StopWatching();
  WSACloseEvent(addr_overlapped_.hEvent);
}

void NetworkChangeNotifierWin::OnObjectSignaled(HANDLE object) {
  NotifyObserversOfIPAddressChange();

  // Start watching for the next address change.
  WatchForAddressChange();
}

void NetworkChangeNotifierWin::WatchForAddressChange() {
  HANDLE handle = NULL;
  DWORD ret = NotifyAddrChange(&handle, &addr_overlapped_);
  CHECK(ret == ERROR_IO_PENDING);
  addr_watcher_.StartWatching(addr_overlapped_.hEvent, this);
}

}  // namespace net
<|endoftext|>


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

#include "base/ref_counted.h"
#include "net/base/host_resolver_unittest.h"
#include "net/http/http_network_layer.h"
#include "net/http/http_transaction_unittest.h"
#include "net/proxy/proxy_service.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/platform_test.h"

class HttpNetworkLayerTest : public PlatformTest {
 public:
  HttpNetworkLayerTest()
      : host_mapper_(new net::RuleBasedHostMapper()),
        scoped_host_mapper_(host_mapper_.get()) {
    // TODO(darin): kill this exception once we have a way to test out the
    // HttpNetworkLayer class using loopback connections.
    host_mapper_->AllowDirectLookup("www.google.com");
  }

 private:
  scoped_refptr<net::RuleBasedHostMapper> host_mapper_;
  net::ScopedHostMapper scoped_host_mapper_;
};

TEST_F(HttpNetworkLayerTest, CreateAndDestroy) {
  net::HostResolver host_resolver;
  scoped_ptr<net::ProxyService> proxy_service(net::ProxyService::CreateNull());
  net::HttpNetworkLayer factory(&host_resolver, proxy_service.get());

  scoped_ptr<net::HttpTransaction> trans(factory.CreateTransaction());
}

TEST_F(HttpNetworkLayerTest, Suspend) {
  net::HostResolver host_resolver;
  scoped_ptr<net::ProxyService> proxy_service(net::ProxyService::CreateNull());
  net::HttpNetworkLayer factory(&host_resolver, proxy_service.get());

  scoped_ptr<net::HttpTransaction> trans(factory.CreateTransaction());
  trans.reset();

  factory.Suspend(true);

  trans.reset(factory.CreateTransaction());
  ASSERT_TRUE(trans == NULL);

  factory.Suspend(false);

  trans.reset(factory.CreateTransaction());
}

TEST_F(HttpNetworkLayerTest, GoogleGET) {
  net::HostResolver host_resolver;
  scoped_ptr<net::ProxyService> proxy_service(net::ProxyService::CreateNull());
  net::HttpNetworkLayer factory(&host_resolver, proxy_service.get());

  TestCompletionCallback callback;

  scoped_ptr<net::HttpTransaction> trans(factory.CreateTransaction());

  net::HttpRequestInfo request_info;
  request_info.url = GURL("http://www.google.com/");
  request_info.method = "GET";
  request_info.user_agent = "Foo/1.0";
  request_info.load_flags = net::LOAD_NORMAL;

  int rv = trans->Start(&request_info, &callback);
  if (rv == net::ERR_IO_PENDING)
    rv = callback.WaitForResult();
  EXPECT_EQ(net::OK, rv);

  std::string contents;
  rv = ReadTransaction(trans.get(), &contents);
  EXPECT_EQ(net::OK, rv);
}
<commit_msg>Fix segfault in HttpNetworkLayerTest.GoogleGET when network connection fails.<commit_after>// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/ref_counted.h"
#include "net/base/host_resolver_unittest.h"
#include "net/http/http_network_layer.h"
#include "net/http/http_transaction_unittest.h"
#include "net/proxy/proxy_service.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/platform_test.h"

class HttpNetworkLayerTest : public PlatformTest {
 public:
  HttpNetworkLayerTest()
      : host_mapper_(new net::RuleBasedHostMapper()),
        scoped_host_mapper_(host_mapper_.get()) {
    // TODO(darin): kill this exception once we have a way to test out the
    // HttpNetworkLayer class using loopback connections.
    host_mapper_->AllowDirectLookup("www.google.com");
  }

 private:
  scoped_refptr<net::RuleBasedHostMapper> host_mapper_;
  net::ScopedHostMapper scoped_host_mapper_;
};

TEST_F(HttpNetworkLayerTest, CreateAndDestroy) {
  net::HostResolver host_resolver;
  scoped_ptr<net::ProxyService> proxy_service(net::ProxyService::CreateNull());
  net::HttpNetworkLayer factory(&host_resolver, proxy_service.get());

  scoped_ptr<net::HttpTransaction> trans(factory.CreateTransaction());
}

TEST_F(HttpNetworkLayerTest, Suspend) {
  net::HostResolver host_resolver;
  scoped_ptr<net::ProxyService> proxy_service(net::ProxyService::CreateNull());
  net::HttpNetworkLayer factory(&host_resolver, proxy_service.get());

  scoped_ptr<net::HttpTransaction> trans(factory.CreateTransaction());
  trans.reset();

  factory.Suspend(true);

  trans.reset(factory.CreateTransaction());
  ASSERT_TRUE(trans == NULL);

  factory.Suspend(false);

  trans.reset(factory.CreateTransaction());
}

TEST_F(HttpNetworkLayerTest, GoogleGET) {
  net::HostResolver host_resolver;
  scoped_ptr<net::ProxyService> proxy_service(net::ProxyService::CreateNull());
  net::HttpNetworkLayer factory(&host_resolver, proxy_service.get());

  TestCompletionCallback callback;

  scoped_ptr<net::HttpTransaction> trans(factory.CreateTransaction());

  net::HttpRequestInfo request_info;
  request_info.url = GURL("http://www.google.com/");
  request_info.method = "GET";
  request_info.user_agent = "Foo/1.0";
  request_info.load_flags = net::LOAD_NORMAL;

  int rv = trans->Start(&request_info, &callback);
  if (rv == net::ERR_IO_PENDING)
    rv = callback.WaitForResult();
  ASSERT_EQ(net::OK, rv);

  std::string contents;
  rv = ReadTransaction(trans.get(), &contents);
  EXPECT_EQ(net::OK, rv);
}
<|endoftext|>