<commit_before>/*
 * Copyright (c) 2015 - 2021, Intel Corporation
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in
 *       the documentation and/or other materials provided with the
 *       distribution.
 *
 *     * Neither the name of Intel Corporation nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY LOG OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "config.h"
#include "BatchClient.hpp"

#include <signal.h>
#include <unistd.h>

#include "geopm/Helper.hpp"
#include "geopm/SharedMemory.hpp"
#include "geopm/Exception.hpp"
#include "geopm/PlatformIO.hpp"
#include "BatchServer.hpp"
#include "BatchStatus.hpp"

namespace geopm
{
    std::unique_ptr<BatchClient> BatchClient::make_unique(const std::string &server_key,
                                                          double timeout,
                                                          int num_signal,
                                                          int num_control)
    {
        return geopm::make_unique<BatchClientImp>(server_key, timeout,
                                                  num_signal, num_control);
    }

    BatchClientImp::BatchClientImp(const std::string &server_key, double timeout,
                                   int num_signal, int num_control)
        : BatchClientImp(num_signal, num_control,
                         BatchStatus::make_unique_client(server_key),
                         num_signal == 0 ? nullptr :
                         SharedMemory::make_unique_user(BatchServer::M_SHMEM_PREFIX +
                                                        server_key + "-signal",
                                                        timeout),
                         num_control == 0 ? nullptr :
                         SharedMemory::make_unique_user(BatchServer::M_SHMEM_PREFIX +
                                                        server_key + "-control",
                                                        timeout))
    {

    }

    BatchClientImp::BatchClientImp(int num_signal, int num_control,
                                   std::shared_ptr<BatchStatus> batch_status,
                                   std::shared_ptr<SharedMemory> signal_shmem,
                                   std::shared_ptr<SharedMemory> control_shmem)
        : m_num_signal(num_signal)
        , m_num_control(num_control)
        , m_batch_status(batch_status)
        , m_signal_shmem(signal_shmem)
        , m_control_shmem(control_shmem)
    {

    }

    std::vector<double> BatchClientImp::read_batch(void)
    {
        if (m_num_signal == 0) {
            return {};
        }
        m_batch_status->send_message(BatchStatus::M_MESSAGE_READ);
        m_batch_status->receive_message(BatchStatus::M_MESSAGE_CONTINUE);
        double *buffer = (double *)m_signal_shmem->pointer();
        std::vector<double> result(buffer, buffer + m_num_signal);
        return result;
    }

    void BatchClientImp::write_batch(std::vector<double> settings)
    {
        if (settings.size() != (size_t)m_num_control) {
            throw Exception("BatchClientImp::write_batch(): settings vector length does not match the number of configured controls",
                            GEOPM_ERROR_INVALID, __FILE__, __LINE__);
        }
        if (m_num_control == 0) {
            return;
        }
        double *buffer = (double *)m_control_shmem->pointer();
        std::copy(settings.begin(), settings.end(), buffer);
        m_batch_status->send_message(BatchStatus::M_MESSAGE_WRITE);
        m_batch_status->receive_message(BatchStatus::M_MESSAGE_CONTINUE);
    }

    void BatchClientImp::stop_batch(void)
    {
        m_batch_status->send_message(BatchStatus::M_MESSAGE_QUIT);
        m_batch_status->receive_message(BatchStatus::M_MESSAGE_QUIT);
    }
}
<commit_msg>Add a comment about how even the quit request is blocking<commit_after>/*
 * Copyright (c) 2015 - 2021, Intel Corporation
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in
 *       the documentation and/or other materials provided with the
 *       distribution.
 *
 *     * Neither the name of Intel Corporation nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY LOG OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "config.h"
#include "BatchClient.hpp"

#include <signal.h>
#include <unistd.h>

#include "geopm/Helper.hpp"
#include "geopm/SharedMemory.hpp"
#include "geopm/Exception.hpp"
#include "geopm/PlatformIO.hpp"
#include "BatchServer.hpp"
#include "BatchStatus.hpp"

namespace geopm
{
    std::unique_ptr<BatchClient> BatchClient::make_unique(const std::string &server_key,
                                                          double timeout,
                                                          int num_signal,
                                                          int num_control)
    {
        return geopm::make_unique<BatchClientImp>(server_key, timeout,
                                                  num_signal, num_control);
    }

    BatchClientImp::BatchClientImp(const std::string &server_key, double timeout,
                                   int num_signal, int num_control)
        : BatchClientImp(num_signal, num_control,
                         BatchStatus::make_unique_client(server_key),
                         num_signal == 0 ? nullptr :
                         SharedMemory::make_unique_user(BatchServer::M_SHMEM_PREFIX +
                                                        server_key + "-signal",
                                                        timeout),
                         num_control == 0 ? nullptr :
                         SharedMemory::make_unique_user(BatchServer::M_SHMEM_PREFIX +
                                                        server_key + "-control",
                                                        timeout))
    {

    }

    BatchClientImp::BatchClientImp(int num_signal, int num_control,
                                   std::shared_ptr<BatchStatus> batch_status,
                                   std::shared_ptr<SharedMemory> signal_shmem,
                                   std::shared_ptr<SharedMemory> control_shmem)
        : m_num_signal(num_signal)
        , m_num_control(num_control)
        , m_batch_status(batch_status)
        , m_signal_shmem(signal_shmem)
        , m_control_shmem(control_shmem)
    {

    }

    std::vector<double> BatchClientImp::read_batch(void)
    {
        if (m_num_signal == 0) {
            return {};
        }
        m_batch_status->send_message(BatchStatus::M_MESSAGE_READ);
        m_batch_status->receive_message(BatchStatus::M_MESSAGE_CONTINUE);
        double *buffer = (double *)m_signal_shmem->pointer();
        std::vector<double> result(buffer, buffer + m_num_signal);
        return result;
    }

    void BatchClientImp::write_batch(std::vector<double> settings)
    {
        if (settings.size() != (size_t)m_num_control) {
            throw Exception("BatchClientImp::write_batch(): settings vector length does not match the number of configured controls",
                            GEOPM_ERROR_INVALID, __FILE__, __LINE__);
        }
        if (m_num_control == 0) {
            return;
        }
        double *buffer = (double *)m_control_shmem->pointer();
        std::copy(settings.begin(), settings.end(), buffer);
        m_batch_status->send_message(BatchStatus::M_MESSAGE_WRITE);
        m_batch_status->receive_message(BatchStatus::M_MESSAGE_CONTINUE);
    }

    void BatchClientImp::stop_batch(void)
    {
        // Note that all requests sent to the batch server block on
        // the client side until the server has completed the
        // request. This is even true for the request to quit.
        m_batch_status->send_message(BatchStatus::M_MESSAGE_QUIT);
        m_batch_status->receive_message(BatchStatus::M_MESSAGE_QUIT);
    }
}
<|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/spdy/spdy_stream.h"

#include "base/logging.h"
#include "base/message_loop.h"
#include "base/singleton.h"
#include "net/spdy/spdy_session.h"

namespace net {

SpdyStream::SpdyStream(
    SpdySession* session, spdy::SpdyStreamId stream_id, bool pushed)
    : continue_buffering_data_(true),
      stream_id_(stream_id),
      priority_(0),
      stalled_by_flow_control_(false),
      send_window_size_(spdy::kInitialWindowSize),
      pushed_(pushed),
      metrics_(Singleton<BandwidthMetrics>::get()),
      response_received_(false),
      session_(session),
      delegate_(NULL),
      request_time_(base::Time::Now()),
      response_(new spdy::SpdyHeaderBlock),
      io_state_(STATE_NONE),
      response_status_(OK),
      cancelled_(false),
      send_bytes_(0),
      recv_bytes_(0) {
}

SpdyStream::~SpdyStream() {
  DLOG(INFO) << "Deleting SpdyStream for stream " << stream_id_;
  UpdateHistograms();
}

void SpdyStream::SetDelegate(Delegate* delegate) {
  CHECK(delegate);
  delegate_ = delegate;

  if (pushed_) {
    CHECK(response_received());
    MessageLoop::current()->PostTask(
        FROM_HERE, NewRunnableMethod(this,
                                     &SpdyStream::PushedStreamReplayData));
  } else {
    continue_buffering_data_ = false;
  }
}

void SpdyStream::PushedStreamReplayData() {
  if (cancelled_ || delegate_ == NULL)
    return;

  delegate_->OnResponseReceived(*response_, response_time_, OK);

  continue_buffering_data_ = false;
  std::vector<scoped_refptr<IOBufferWithSize> > buffers;
  buffers.swap(pending_buffers_);
  for (size_t i = 0; i < buffers.size(); ++i) {
    if (delegate_){
      if (buffers[i])
        delegate_->OnDataReceived(buffers[i]->data(), buffers[i]->size());
      else
        delegate_->OnDataReceived(NULL, 0);
    }
  }
}

void SpdyStream::DetachDelegate() {
  delegate_ = NULL;
  if (!closed())
    Cancel();
}

const linked_ptr<spdy::SpdyHeaderBlock>& SpdyStream::spdy_headers() const {
  return request_;
}

void SpdyStream::set_spdy_headers(
    const linked_ptr<spdy::SpdyHeaderBlock>& headers) {
  request_ = headers;
}

void SpdyStream::IncreaseSendWindowSize(int delta_window_size) {
  DCHECK_GE(delta_window_size, 1);
  int new_window_size = send_window_size_ + delta_window_size;

  // We should ignore WINDOW_UPDATEs received before or after this state,
  // since before means we've not written SYN_STREAM yet (i.e. it's too
  // early) and after means we've written a DATA frame with FIN bit.
  if (io_state_ != STATE_SEND_BODY_COMPLETE)
    return;

  // it's valid for send_window_size_ to become negative (via an incoming
  // SETTINGS), in which case incoming WINDOW_UPDATEs will eventually make
  // it positive; however, if send_window_size_ is positive and incoming
  // WINDOW_UPDATE makes it negative, we have an overflow.
  if (send_window_size_ > 0 && new_window_size < 0) {
    LOG(WARNING) << "Received WINDOW_UPDATE [delta:" << delta_window_size
                 << "] for stream " << stream_id_
                 << " overflows send_window_size_ [current:"
                 << send_window_size_ << "]";
    session_->ResetStream(stream_id_, spdy::FLOW_CONTROL_ERROR);
    return;
  }

  LOG(INFO) << "Increasing stream " << stream_id_
            << " send_window_size_ [current:" << send_window_size_ << "]"
            << " by " << delta_window_size << " bytes";
  send_window_size_ = new_window_size;

  if (stalled_by_flow_control_) {
    stalled_by_flow_control_ = false;
    io_state_ = STATE_SEND_BODY;
    DoLoop(OK);
  }
}

void SpdyStream::DecreaseSendWindowSize(int delta_window_size) {
  // we only call this method when sending a frame, therefore
  // |delta_window_size| should be within the valid frame size range.
  DCHECK_GE(delta_window_size, 1);
  DCHECK_LE(delta_window_size, kMaxSpdyFrameChunkSize);

  // |send_window_size_| should have been at least |delta_window_size| for
  // this call to happen.
  DCHECK_GE(send_window_size_, delta_window_size);

  LOG(INFO) << "Decreasing stream " << stream_id_
            << " send_window_size_ [current:" << send_window_size_ << "]"
            << " by " << delta_window_size  << " bytes";
  send_window_size_ -= delta_window_size;
}

base::Time SpdyStream::GetRequestTime() const {
  return request_time_;
}

void SpdyStream::SetRequestTime(base::Time t) {
  request_time_ = t;
}

int SpdyStream::OnResponseReceived(const spdy::SpdyHeaderBlock& response) {
  int rv = OK;
  LOG(INFO) << "Received response for stream " << stream_id_;

  metrics_.StartStream();

  DCHECK(response_->empty());
  *response_ = response;  // TODO(ukai): avoid copy.

  recv_first_byte_time_ = base::TimeTicks::Now();
  response_time_ = base::Time::Now();

  // If we receive a response before we are in STATE_WAITING_FOR_RESPONSE, then
  // the server has sent the SYN_REPLY too early.
  if (!pushed_ && io_state_ != STATE_WAITING_FOR_RESPONSE)
    return ERR_SPDY_PROTOCOL_ERROR;
  if (pushed_)
    CHECK(io_state_ == STATE_NONE);
  io_state_ = STATE_OPEN;

  if (delegate_)
    rv = delegate_->OnResponseReceived(*response_, response_time_, rv);
  // If delegate_ is not yet attached, we'll call OnResponseReceived after the
  // delegate gets attached to the stream.

  return rv;
}

void SpdyStream::OnDataReceived(const char* data, int length) {
  DCHECK_GE(length, 0);
  LOG(INFO) << "SpdyStream: Data (" << length << " bytes) received for "
            << stream_id_;

  if (!delegate_ || continue_buffering_data_) {
    // It should be valid for this to happen in the server push case.
    // We'll return received data when delegate gets attached to the stream.
    if (length > 0) {
      IOBufferWithSize* buf = new IOBufferWithSize(length);
      memcpy(buf->data(), data, length);
      pending_buffers_.push_back(buf);
    } else {
      pending_buffers_.push_back(NULL);
      metrics_.StopStream();
      session_->CloseStream(stream_id_, net::OK);
      // Note: |this| may be deleted after calling CloseStream.
    }
    return;
  }

 CHECK(!closed());

  // If we don't have a response, then the SYN_REPLY did not come through.
  // We cannot pass data up to the caller unless the reply headers have been
  // received.
  if (!response_received()) {
    session_->CloseStream(stream_id_, ERR_SYN_REPLY_NOT_RECEIVED);
    return;
  }

  // A zero-length read means that the stream is being closed.
  if (!length) {
    metrics_.StopStream();
    session_->CloseStream(stream_id_, net::OK);
    // Note: |this| may be deleted after calling CloseStream.
    return;
  }

  // Track our bandwidth.
  metrics_.RecordBytes(length);
  recv_bytes_ += length;
  recv_last_byte_time_ = base::TimeTicks::Now();

  if (!delegate_) {
    // It should be valid for this to happen in the server push case.
    // We'll return received data when delegate gets attached to the stream.
    IOBufferWithSize* buf = new IOBufferWithSize(length);
    memcpy(buf->data(), data, length);
    pending_buffers_.push_back(buf);
    return;
  }

  delegate_->OnDataReceived(data, length);
}

// This function is only called when an entire frame is written.
void SpdyStream::OnWriteComplete(int bytes) {
  DCHECK_LE(0, bytes);
  send_bytes_ += bytes;
  if (cancelled() || closed())
    return;
  DoLoop(bytes);
}

void SpdyStream::OnClose(int status) {
  io_state_ = STATE_DONE;
  response_status_ = status;
  Delegate* delegate = delegate_;
  delegate_ = NULL;
  if (delegate)
    delegate->OnClose(status);
}

void SpdyStream::Cancel() {
  if (cancelled())
    return;

  cancelled_ = true;
  if (session_->IsStreamActive(stream_id_))
    session_->ResetStream(stream_id_, spdy::CANCEL);
}

int SpdyStream::SendRequest(bool has_upload_data) {
  // Pushed streams do not send any data, and should always be in STATE_OPEN or
  // STATE_DONE. However, we still want to return IO_PENDING to mimic non-push
  // behavior.
  has_upload_data_ = has_upload_data;
  if (pushed_) {
    send_time_ = base::TimeTicks::Now();
    DCHECK(!has_upload_data_);
    DCHECK(response_received());
    return ERR_IO_PENDING;
  }
  CHECK_EQ(STATE_NONE, io_state_);
  io_state_ = STATE_SEND_HEADERS;
  return DoLoop(OK);
}

int SpdyStream::WriteStreamData(IOBuffer* data, int length,
                                spdy::SpdyDataFlags flags) {
  return session_->WriteStreamData(stream_id_, data, length, flags);
}

bool SpdyStream::GetSSLInfo(SSLInfo* ssl_info, bool* was_npn_negotiated) {
  return session_->GetSSLInfo(ssl_info, was_npn_negotiated);
}

bool SpdyStream::GetSSLCertRequestInfo(SSLCertRequestInfo* cert_request_info) {
  return session_->GetSSLCertRequestInfo(cert_request_info);
}

int SpdyStream::DoLoop(int result) {
  do {
    State state = io_state_;
    io_state_ = STATE_NONE;
    switch (state) {
      // State machine 1: Send headers and body.
      case STATE_SEND_HEADERS:
        CHECK_EQ(OK, result);
        net_log_.BeginEvent(NetLog::TYPE_SPDY_STREAM_SEND_HEADERS, NULL);
        result = DoSendHeaders();
        break;
      case STATE_SEND_HEADERS_COMPLETE:
        net_log_.EndEvent(NetLog::TYPE_SPDY_STREAM_SEND_HEADERS, NULL);
        result = DoSendHeadersComplete(result);
        break;
      case STATE_SEND_BODY:
        CHECK_EQ(OK, result);
        net_log_.BeginEvent(NetLog::TYPE_SPDY_STREAM_SEND_BODY, NULL);
        result = DoSendBody();
        break;
      case STATE_SEND_BODY_COMPLETE:
        net_log_.EndEvent(NetLog::TYPE_SPDY_STREAM_SEND_BODY, NULL);
        result = DoSendBodyComplete(result);
        break;
      // This is an intermediary waiting state. This state is reached when all
      // data has been sent, but no data has been received.
      case STATE_WAITING_FOR_RESPONSE:
        io_state_ = STATE_WAITING_FOR_RESPONSE;
        result = ERR_IO_PENDING;
        break;
      // State machine 2: connection is established.
      // In STATE_OPEN, OnResponseReceived has already been called.
      // OnDataReceived, OnClose and OnWriteCompelte can be called.
      // Only OnWriteCompletee calls DoLoop(().
      //
      // For HTTP streams, no data is sent from the client while in the OPEN
      // state, so OnWriteComplete is never called here.  The HTTP body is
      // handled in the OnDataReceived callback, which does not call into
      // DoLoop.
      //
      // For WebSocket streams, which are bi-directional, we'll send and
      // receive data once the connection is established.  Received data is
      // handled in OnDataReceived.  Sent data is handled in OnWriteComplete,
      // which calls DoOpen().
      case STATE_OPEN:
        result = DoOpen(result);
        break;

      case STATE_DONE:
        DCHECK(result != ERR_IO_PENDING);
        break;
      default:
        NOTREACHED() << io_state_;
        break;
    }
  } while (result != ERR_IO_PENDING && io_state_ != STATE_NONE &&
           io_state_ != STATE_OPEN);

  return result;
}

int SpdyStream::DoSendHeaders() {
  CHECK(!cancelled_);

  spdy::SpdyControlFlags flags = spdy::CONTROL_FLAG_NONE;
  if (!has_upload_data_)
    flags = spdy::CONTROL_FLAG_FIN;

  CHECK(request_.get());
  int result = session_->WriteSynStream(
      stream_id_, static_cast<RequestPriority>(priority_), flags,
      request_);
  if (result != ERR_IO_PENDING)
    return result;

  send_time_ = base::TimeTicks::Now();
  io_state_ = STATE_SEND_HEADERS_COMPLETE;
  return ERR_IO_PENDING;
}

int SpdyStream::DoSendHeadersComplete(int result) {
  if (result < 0)
    return result;

  CHECK_GT(result, 0);

  if (!delegate_)
    return ERR_UNEXPECTED;

  // There is no body, skip that state.
  if (delegate_->OnSendHeadersComplete(result)) {
    io_state_ = STATE_WAITING_FOR_RESPONSE;
    return OK;
  }

  io_state_ = STATE_SEND_BODY;
  return OK;
}

// DoSendBody is called to send the optional body for the request.  This call
// will also be called as each write of a chunk of the body completes.
int SpdyStream::DoSendBody() {
  // If we're already in the STATE_SENDING_BODY state, then we've already
  // sent a portion of the body.  In that case, we need to first consume
  // the bytes written in the body stream.  Note that the bytes written is
  // the number of bytes in the frame that were written, only consume the
  // data portion, of course.
  io_state_ = STATE_SEND_BODY_COMPLETE;
  if (!delegate_)
    return ERR_UNEXPECTED;
  return delegate_->OnSendBody();
}

int SpdyStream::DoSendBodyComplete(int result) {
  if (result < 0)
    return result;

  CHECK_NE(result, 0);

  if (!delegate_)
    return ERR_UNEXPECTED;

  if (!delegate_->OnSendBodyComplete(result))
    io_state_ = STATE_SEND_BODY;
  else
    io_state_ = STATE_WAITING_FOR_RESPONSE;

  return OK;
}

int SpdyStream::DoOpen(int result) {
  if (delegate_)
    delegate_->OnDataSent(result);
  io_state_ = STATE_OPEN;
  return result;
}

void SpdyStream::UpdateHistograms() {
  // We need all timers to be filled in, otherwise metrics can be bogus.
  if (send_time_.is_null() || recv_first_byte_time_.is_null() ||
      recv_last_byte_time_.is_null())
    return;

  UMA_HISTOGRAM_TIMES("Net.SpdyStreamTimeToFirstByte",
      recv_first_byte_time_ - send_time_);
  UMA_HISTOGRAM_TIMES("Net.SpdyStreamDownloadTime",
      recv_last_byte_time_ - recv_first_byte_time_);
  UMA_HISTOGRAM_TIMES("Net.SpdyStreamTime",
      recv_last_byte_time_ - send_time_);

  UMA_HISTOGRAM_COUNTS("Net.SpdySendBytes", send_bytes_);
  UMA_HISTOGRAM_COUNTS("Net.SpdyRecvBytes", recv_bytes_);
}

}  // namespace net
<commit_msg>Keep push streams open until they are claimed.  The previous change I did changed this (so that streams are closed when they get EOF, even though the unclaimed stream is active), but it breaks the invariants for spdy_http_stream and checking when a stream is closed.  Both models have advantages, going back to the original mechanism.<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/spdy/spdy_stream.h"

#include "base/logging.h"
#include "base/message_loop.h"
#include "base/singleton.h"
#include "net/spdy/spdy_session.h"

namespace net {

SpdyStream::SpdyStream(
    SpdySession* session, spdy::SpdyStreamId stream_id, bool pushed)
    : continue_buffering_data_(true),
      stream_id_(stream_id),
      priority_(0),
      stalled_by_flow_control_(false),
      send_window_size_(spdy::kInitialWindowSize),
      pushed_(pushed),
      metrics_(Singleton<BandwidthMetrics>::get()),
      response_received_(false),
      session_(session),
      delegate_(NULL),
      request_time_(base::Time::Now()),
      response_(new spdy::SpdyHeaderBlock),
      io_state_(STATE_NONE),
      response_status_(OK),
      cancelled_(false),
      send_bytes_(0),
      recv_bytes_(0) {
}

SpdyStream::~SpdyStream() {
  DLOG(INFO) << "Deleting SpdyStream for stream " << stream_id_;
  UpdateHistograms();
}

void SpdyStream::SetDelegate(Delegate* delegate) {
  CHECK(delegate);
  delegate_ = delegate;

  if (pushed_) {
    CHECK(response_received());
    MessageLoop::current()->PostTask(
        FROM_HERE, NewRunnableMethod(this,
                                     &SpdyStream::PushedStreamReplayData));
  } else {
    continue_buffering_data_ = false;
  }
}

void SpdyStream::PushedStreamReplayData() {
  if (cancelled_ || !delegate_)
    return;

  delegate_->OnResponseReceived(*response_, response_time_, OK);

  continue_buffering_data_ = false;
  std::vector<scoped_refptr<IOBufferWithSize> > buffers;
  buffers.swap(pending_buffers_);
  for (size_t i = 0; i < buffers.size(); ++i) {
    // It is always possible that a callback to the delegate results in
    // the delegate no longer being available.
    if (!delegate_)
      break;
    if (buffers[i]) {
      delegate_->OnDataReceived(buffers[i]->data(), buffers[i]->size());
    } else {
      delegate_->OnDataReceived(NULL, 0);
      session_->CloseStream(stream_id_, net::OK);
      // Note: |this| may be deleted after calling CloseStream.
      DCHECK_EQ(buffers.size() - 1, i);
    }
  }
}

void SpdyStream::DetachDelegate() {
  delegate_ = NULL;
  if (!closed())
    Cancel();
}

const linked_ptr<spdy::SpdyHeaderBlock>& SpdyStream::spdy_headers() const {
  return request_;
}

void SpdyStream::set_spdy_headers(
    const linked_ptr<spdy::SpdyHeaderBlock>& headers) {
  request_ = headers;
}

void SpdyStream::IncreaseSendWindowSize(int delta_window_size) {
  DCHECK_GE(delta_window_size, 1);
  int new_window_size = send_window_size_ + delta_window_size;

  // We should ignore WINDOW_UPDATEs received before or after this state,
  // since before means we've not written SYN_STREAM yet (i.e. it's too
  // early) and after means we've written a DATA frame with FIN bit.
  if (io_state_ != STATE_SEND_BODY_COMPLETE)
    return;

  // it's valid for send_window_size_ to become negative (via an incoming
  // SETTINGS), in which case incoming WINDOW_UPDATEs will eventually make
  // it positive; however, if send_window_size_ is positive and incoming
  // WINDOW_UPDATE makes it negative, we have an overflow.
  if (send_window_size_ > 0 && new_window_size < 0) {
    LOG(WARNING) << "Received WINDOW_UPDATE [delta:" << delta_window_size
                 << "] for stream " << stream_id_
                 << " overflows send_window_size_ [current:"
                 << send_window_size_ << "]";
    session_->ResetStream(stream_id_, spdy::FLOW_CONTROL_ERROR);
    return;
  }

  LOG(INFO) << "Increasing stream " << stream_id_
            << " send_window_size_ [current:" << send_window_size_ << "]"
            << " by " << delta_window_size << " bytes";
  send_window_size_ = new_window_size;

  if (stalled_by_flow_control_) {
    stalled_by_flow_control_ = false;
    io_state_ = STATE_SEND_BODY;
    DoLoop(OK);
  }
}

void SpdyStream::DecreaseSendWindowSize(int delta_window_size) {
  // we only call this method when sending a frame, therefore
  // |delta_window_size| should be within the valid frame size range.
  DCHECK_GE(delta_window_size, 1);
  DCHECK_LE(delta_window_size, kMaxSpdyFrameChunkSize);

  // |send_window_size_| should have been at least |delta_window_size| for
  // this call to happen.
  DCHECK_GE(send_window_size_, delta_window_size);

  LOG(INFO) << "Decreasing stream " << stream_id_
            << " send_window_size_ [current:" << send_window_size_ << "]"
            << " by " << delta_window_size  << " bytes";
  send_window_size_ -= delta_window_size;
}

base::Time SpdyStream::GetRequestTime() const {
  return request_time_;
}

void SpdyStream::SetRequestTime(base::Time t) {
  request_time_ = t;
}

int SpdyStream::OnResponseReceived(const spdy::SpdyHeaderBlock& response) {
  int rv = OK;
  LOG(INFO) << "Received response for stream " << stream_id_;

  metrics_.StartStream();

  DCHECK(response_->empty());
  *response_ = response;  // TODO(ukai): avoid copy.

  recv_first_byte_time_ = base::TimeTicks::Now();
  response_time_ = base::Time::Now();

  // If we receive a response before we are in STATE_WAITING_FOR_RESPONSE, then
  // the server has sent the SYN_REPLY too early.
  if (!pushed_ && io_state_ != STATE_WAITING_FOR_RESPONSE)
    return ERR_SPDY_PROTOCOL_ERROR;
  if (pushed_)
    CHECK(io_state_ == STATE_NONE);
  io_state_ = STATE_OPEN;

  if (delegate_)
    rv = delegate_->OnResponseReceived(*response_, response_time_, rv);
  // If delegate_ is not yet attached, we'll call OnResponseReceived after the
  // delegate gets attached to the stream.

  return rv;
}

void SpdyStream::OnDataReceived(const char* data, int length) {
  DCHECK_GE(length, 0);
  LOG(INFO) << "SpdyStream: Data (" << length << " bytes) received for "
            << stream_id_;

  if (!delegate_ || continue_buffering_data_) {
    // It should be valid for this to happen in the server push case.
    // We'll return received data when delegate gets attached to the stream.
    if (length > 0) {
      IOBufferWithSize* buf = new IOBufferWithSize(length);
      memcpy(buf->data(), data, length);
      pending_buffers_.push_back(buf);
    } else {
      pending_buffers_.push_back(NULL);
      metrics_.StopStream();
      // Note: we leave the stream open in the session until the stream
      //       is claimed.
    }
    return;
  }

 CHECK(!closed());

  // If we don't have a response, then the SYN_REPLY did not come through.
  // We cannot pass data up to the caller unless the reply headers have been
  // received.
  if (!response_received()) {
    session_->CloseStream(stream_id_, ERR_SYN_REPLY_NOT_RECEIVED);
    return;
  }

  // A zero-length read means that the stream is being closed.
  if (!length) {
    metrics_.StopStream();
    session_->CloseStream(stream_id_, net::OK);
    // Note: |this| may be deleted after calling CloseStream.
    return;
  }

  // Track our bandwidth.
  metrics_.RecordBytes(length);
  recv_bytes_ += length;
  recv_last_byte_time_ = base::TimeTicks::Now();

  if (!delegate_) {
    // It should be valid for this to happen in the server push case.
    // We'll return received data when delegate gets attached to the stream.
    IOBufferWithSize* buf = new IOBufferWithSize(length);
    memcpy(buf->data(), data, length);
    pending_buffers_.push_back(buf);
    return;
  }

  delegate_->OnDataReceived(data, length);
}

// This function is only called when an entire frame is written.
void SpdyStream::OnWriteComplete(int bytes) {
  DCHECK_LE(0, bytes);
  send_bytes_ += bytes;
  if (cancelled() || closed())
    return;
  DoLoop(bytes);
}

void SpdyStream::OnClose(int status) {
  io_state_ = STATE_DONE;
  response_status_ = status;
  Delegate* delegate = delegate_;
  delegate_ = NULL;
  if (delegate)
    delegate->OnClose(status);
}

void SpdyStream::Cancel() {
  if (cancelled())
    return;

  cancelled_ = true;
  if (session_->IsStreamActive(stream_id_))
    session_->ResetStream(stream_id_, spdy::CANCEL);
}

int SpdyStream::SendRequest(bool has_upload_data) {
  // Pushed streams do not send any data, and should always be in STATE_OPEN or
  // STATE_DONE. However, we still want to return IO_PENDING to mimic non-push
  // behavior.
  has_upload_data_ = has_upload_data;
  if (pushed_) {
    send_time_ = base::TimeTicks::Now();
    DCHECK(!has_upload_data_);
    DCHECK(response_received());
    return ERR_IO_PENDING;
  }
  CHECK_EQ(STATE_NONE, io_state_);
  io_state_ = STATE_SEND_HEADERS;
  return DoLoop(OK);
}

int SpdyStream::WriteStreamData(IOBuffer* data, int length,
                                spdy::SpdyDataFlags flags) {
  return session_->WriteStreamData(stream_id_, data, length, flags);
}

bool SpdyStream::GetSSLInfo(SSLInfo* ssl_info, bool* was_npn_negotiated) {
  return session_->GetSSLInfo(ssl_info, was_npn_negotiated);
}

bool SpdyStream::GetSSLCertRequestInfo(SSLCertRequestInfo* cert_request_info) {
  return session_->GetSSLCertRequestInfo(cert_request_info);
}

int SpdyStream::DoLoop(int result) {
  do {
    State state = io_state_;
    io_state_ = STATE_NONE;
    switch (state) {
      // State machine 1: Send headers and body.
      case STATE_SEND_HEADERS:
        CHECK_EQ(OK, result);
        net_log_.BeginEvent(NetLog::TYPE_SPDY_STREAM_SEND_HEADERS, NULL);
        result = DoSendHeaders();
        break;
      case STATE_SEND_HEADERS_COMPLETE:
        net_log_.EndEvent(NetLog::TYPE_SPDY_STREAM_SEND_HEADERS, NULL);
        result = DoSendHeadersComplete(result);
        break;
      case STATE_SEND_BODY:
        CHECK_EQ(OK, result);
        net_log_.BeginEvent(NetLog::TYPE_SPDY_STREAM_SEND_BODY, NULL);
        result = DoSendBody();
        break;
      case STATE_SEND_BODY_COMPLETE:
        net_log_.EndEvent(NetLog::TYPE_SPDY_STREAM_SEND_BODY, NULL);
        result = DoSendBodyComplete(result);
        break;
      // This is an intermediary waiting state. This state is reached when all
      // data has been sent, but no data has been received.
      case STATE_WAITING_FOR_RESPONSE:
        io_state_ = STATE_WAITING_FOR_RESPONSE;
        result = ERR_IO_PENDING;
        break;
      // State machine 2: connection is established.
      // In STATE_OPEN, OnResponseReceived has already been called.
      // OnDataReceived, OnClose and OnWriteCompelte can be called.
      // Only OnWriteCompletee calls DoLoop(().
      //
      // For HTTP streams, no data is sent from the client while in the OPEN
      // state, so OnWriteComplete is never called here.  The HTTP body is
      // handled in the OnDataReceived callback, which does not call into
      // DoLoop.
      //
      // For WebSocket streams, which are bi-directional, we'll send and
      // receive data once the connection is established.  Received data is
      // handled in OnDataReceived.  Sent data is handled in OnWriteComplete,
      // which calls DoOpen().
      case STATE_OPEN:
        result = DoOpen(result);
        break;

      case STATE_DONE:
        DCHECK(result != ERR_IO_PENDING);
        break;
      default:
        NOTREACHED() << io_state_;
        break;
    }
  } while (result != ERR_IO_PENDING && io_state_ != STATE_NONE &&
           io_state_ != STATE_OPEN);

  return result;
}

int SpdyStream::DoSendHeaders() {
  CHECK(!cancelled_);

  spdy::SpdyControlFlags flags = spdy::CONTROL_FLAG_NONE;
  if (!has_upload_data_)
    flags = spdy::CONTROL_FLAG_FIN;

  CHECK(request_.get());
  int result = session_->WriteSynStream(
      stream_id_, static_cast<RequestPriority>(priority_), flags,
      request_);
  if (result != ERR_IO_PENDING)
    return result;

  send_time_ = base::TimeTicks::Now();
  io_state_ = STATE_SEND_HEADERS_COMPLETE;
  return ERR_IO_PENDING;
}

int SpdyStream::DoSendHeadersComplete(int result) {
  if (result < 0)
    return result;

  CHECK_GT(result, 0);

  if (!delegate_)
    return ERR_UNEXPECTED;

  // There is no body, skip that state.
  if (delegate_->OnSendHeadersComplete(result)) {
    io_state_ = STATE_WAITING_FOR_RESPONSE;
    return OK;
  }

  io_state_ = STATE_SEND_BODY;
  return OK;
}

// DoSendBody is called to send the optional body for the request.  This call
// will also be called as each write of a chunk of the body completes.
int SpdyStream::DoSendBody() {
  // If we're already in the STATE_SENDING_BODY state, then we've already
  // sent a portion of the body.  In that case, we need to first consume
  // the bytes written in the body stream.  Note that the bytes written is
  // the number of bytes in the frame that were written, only consume the
  // data portion, of course.
  io_state_ = STATE_SEND_BODY_COMPLETE;
  if (!delegate_)
    return ERR_UNEXPECTED;
  return delegate_->OnSendBody();
}

int SpdyStream::DoSendBodyComplete(int result) {
  if (result < 0)
    return result;

  CHECK_NE(result, 0);

  if (!delegate_)
    return ERR_UNEXPECTED;

  if (!delegate_->OnSendBodyComplete(result))
    io_state_ = STATE_SEND_BODY;
  else
    io_state_ = STATE_WAITING_FOR_RESPONSE;

  return OK;
}

int SpdyStream::DoOpen(int result) {
  if (delegate_)
    delegate_->OnDataSent(result);
  io_state_ = STATE_OPEN;
  return result;
}

void SpdyStream::UpdateHistograms() {
  // We need all timers to be filled in, otherwise metrics can be bogus.
  if (send_time_.is_null() || recv_first_byte_time_.is_null() ||
      recv_last_byte_time_.is_null())
    return;

  UMA_HISTOGRAM_TIMES("Net.SpdyStreamTimeToFirstByte",
      recv_first_byte_time_ - send_time_);
  UMA_HISTOGRAM_TIMES("Net.SpdyStreamDownloadTime",
      recv_last_byte_time_ - recv_first_byte_time_);
  UMA_HISTOGRAM_TIMES("Net.SpdyStreamTime",
      recv_last_byte_time_ - send_time_);

  UMA_HISTOGRAM_COUNTS("Net.SpdySendBytes", send_bytes_);
  UMA_HISTOGRAM_COUNTS("Net.SpdyRecvBytes", recv_bytes_);
}

}  // namespace net
<|endoftext|>


<commit_before><commit_msg>Simple fix for a crashing bug.<commit_after><|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/test/test_server.h"

#include <algorithm>
#include <string>
#include <vector>

#include "build/build_config.h"

#if defined(OS_MACOSX)
#include "net/base/x509_certificate.h"
#endif

#include "base/command_line.h"
#include "base/file_util.h"
#include "base/leak_annotations.h"
#include "base/logging.h"
#include "base/path_service.h"
#include "base/string_number_conversions.h"
#include "base/utf_string_conversions.h"
#include "googleurl/src/gurl.h"
#include "net/base/cert_test_util.h"
#include "net/base/host_port_pair.h"
#include "net/base/host_resolver.h"
#include "net/base/test_completion_callback.h"
#include "net/socket/tcp_client_socket.h"
#include "net/test/python_utils.h"
#include "testing/platform_test.h"

namespace {

// Number of connection attempts for tests.
const int kServerConnectionAttempts = 10;

// Connection timeout in milliseconds for tests.
const int kServerConnectionTimeoutMs = 1000;

const char kTestServerShardFlag[] = "test-server-shard";

int GetPortBase(net::TestServer::Type type) {
  switch (type) {
    case net::TestServer::TYPE_FTP:
      return 3117;
    case net::TestServer::TYPE_HTTP:
      return 1337;
    case net::TestServer::TYPE_HTTPS:
    case net::TestServer::TYPE_HTTPS_CLIENT_AUTH:
    case net::TestServer::TYPE_HTTPS_EXPIRED_CERTIFICATE:
      return 9443;
    case net::TestServer::TYPE_HTTPS_MISMATCHED_HOSTNAME:
      return 9666;
    default:
      NOTREACHED();
  }
  return -1;
}

int GetPort(net::TestServer::Type type) {
  int port = GetPortBase(type);
  if (CommandLine::ForCurrentProcess()->HasSwitch(kTestServerShardFlag)) {
    std::string shard_str(CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
                              kTestServerShardFlag));
    int shard = -1;
    if (base::StringToInt(shard_str, &shard)) {
      port += shard;
    } else {
      LOG(FATAL) << "Got invalid " << kTestServerShardFlag << " flag value. "
                 << "An integer is expected.";
    }
  }
  return port;
}

std::string GetHostname(net::TestServer::Type type) {
  if (type == net::TestServer::TYPE_HTTPS_MISMATCHED_HOSTNAME) {
    // Return a different hostname string that resolves to the same hostname.
    return "localhost";
  }

  return "127.0.0.1";
}

}  // namespace

namespace net {

#if defined(OS_MACOSX)
void SetMacTestCertificate(X509Certificate* cert);
#endif

TestServer::TestServer(Type type, const FilePath& document_root)
    : host_port_pair_(GetHostname(type), GetPort(type)),
      process_handle_(base::kNullProcessHandle),
      type_(type) {
  FilePath src_dir;
  PathService::Get(base::DIR_SOURCE_ROOT, &src_dir);

  document_root_ = src_dir.Append(document_root);

  certificates_dir_ = src_dir.Append(FILE_PATH_LITERAL("net"))
                       .Append(FILE_PATH_LITERAL("data"))
                       .Append(FILE_PATH_LITERAL("ssl"))
                       .Append(FILE_PATH_LITERAL("certificates"));
}

TestServer::~TestServer() {
#if defined(OS_MACOSX)
  SetMacTestCertificate(NULL);
#endif
  Stop();
}

bool TestServer::Start() {
  if (GetScheme() == "https") {
    if (!LoadTestRootCert())
      return false;
    if (!CheckCATrusted())
      return false;
  }

  // Get path to python server script
  FilePath testserver_path;
  if (!PathService::Get(base::DIR_SOURCE_ROOT, &testserver_path)) {
    LOG(ERROR) << "Failed to get DIR_SOURCE_ROOT";
    return false;
  }
  testserver_path = testserver_path
      .Append(FILE_PATH_LITERAL("net"))
      .Append(FILE_PATH_LITERAL("tools"))
      .Append(FILE_PATH_LITERAL("testserver"))
      .Append(FILE_PATH_LITERAL("testserver.py"));

  if (!SetPythonPath())
    return false;

  if (!LaunchPython(testserver_path))
    return false;

  if (!WaitToStart()) {
    Stop();
    return false;
  }

  return true;
}

bool TestServer::Stop() {
  if (!process_handle_)
    return true;

  // First check if the process has already terminated.
  bool ret = base::WaitForSingleProcess(process_handle_, 0);
  if (!ret)
    ret = base::KillProcess(process_handle_, 1, true);

  if (ret) {
    base::CloseProcessHandle(process_handle_);
    process_handle_ = base::kNullProcessHandle;
  } else {
    LOG(INFO) << "Kill failed?";
  }

  return ret;
}

bool TestServer::WaitToFinish(int timeout_ms) {
  if (!process_handle_)
    return true;

  bool ret = base::WaitForSingleProcess(process_handle_, timeout_ms);
  if (ret) {
    base::CloseProcessHandle(process_handle_);
    process_handle_ = base::kNullProcessHandle;
  } else {
    LOG(ERROR) << "Timed out.";
  }
  return ret;
}

std::string TestServer::GetScheme() const {
  switch (type_) {
    case TYPE_FTP:
      return "ftp";
    case TYPE_HTTP:
      return "http";
    case TYPE_HTTPS:
    case TYPE_HTTPS_CLIENT_AUTH:
    case TYPE_HTTPS_MISMATCHED_HOSTNAME:
    case TYPE_HTTPS_EXPIRED_CERTIFICATE:
      return "https";
    default:
      NOTREACHED();
  }
  return std::string();
}

bool TestServer::GetAddressList(AddressList* address_list) const {
  DCHECK(address_list);

  scoped_refptr<HostResolver> resolver(
      CreateSystemHostResolver(HostResolver::kDefaultParallelism, NULL));
  HostResolver::RequestInfo info(host_port_pair_);
  int rv = resolver->Resolve(info, address_list, NULL, NULL, BoundNetLog());
  if (rv != net::OK) {
    LOG(ERROR) << "Failed to resolve hostname: " << host_port_pair_.host();
    return false;
  }
  return true;
}

GURL TestServer::GetURL(const std::string& path) {
  return GURL(GetScheme() + "://" + host_port_pair_.ToString() +
              "/" + path);
}

GURL TestServer::GetURLWithUser(const std::string& path,
                                const std::string& user) {
  return GURL(GetScheme() + "://" + user + "@" +
              host_port_pair_.ToString() +
              "/" + path);
}

GURL TestServer::GetURLWithUserAndPassword(const std::string& path,
                                           const std::string& user,
                                           const std::string& password) {
  return GURL(GetScheme() + "://" + user + ":" + password +
              "@" + host_port_pair_.ToString() +
              "/" + path);
}

bool TestServer::SetPythonPath() {
  FilePath third_party_dir;
  if (!PathService::Get(base::DIR_SOURCE_ROOT, &third_party_dir)) {
    LOG(ERROR) << "Failed to get DIR_SOURCE_ROOT";
    return false;
  }
  third_party_dir = third_party_dir.Append(FILE_PATH_LITERAL("third_party"));

  AppendToPythonPath(third_party_dir.Append(FILE_PATH_LITERAL("tlslite")));
  AppendToPythonPath(third_party_dir.Append(FILE_PATH_LITERAL("pyftpdlib")));

  // Locate the Python code generated by the protocol buffers compiler.
  FilePath generated_code_dir;
  if (!PathService::Get(base::DIR_EXE, &generated_code_dir)) {
    LOG(ERROR) << "Failed to get DIR_EXE";
    return false;
  }
  generated_code_dir = generated_code_dir.Append(FILE_PATH_LITERAL("pyproto"));
  AppendToPythonPath(generated_code_dir);
  AppendToPythonPath(generated_code_dir.Append(FILE_PATH_LITERAL("sync_pb")));

  return true;
}

FilePath TestServer::GetRootCertificatePath() {
  return certificates_dir_.AppendASCII("root_ca_cert.crt");
}

bool TestServer::LoadTestRootCert() {
#if defined(USE_NSS)
  if (cert_)
    return true;

  // TODO(dkegel): figure out how to get this to only happen once?

  // This currently leaks a little memory.
  // TODO(dkegel): fix the leak and remove the entry in
  // tools/valgrind/memcheck/suppressions.txt
  ANNOTATE_SCOPED_MEMORY_LEAK;  // Tell heap checker about the leak.
  cert_ = LoadTemporaryRootCert(GetRootCertificatePath());
  return (cert_ != NULL);
#elif defined(OS_MACOSX)
  X509Certificate* cert = LoadTemporaryRootCert(GetRootCertificatePath());
  if (!cert)
    return false;
  SetMacTestCertificate(cert);
  return true;
#else
  return true;
#endif
}

FilePath TestServer::GetCertificatePath() {
  switch (type_) {
    case TYPE_FTP:
    case TYPE_HTTP:
      return FilePath();
    case TYPE_HTTPS:
    case TYPE_HTTPS_CLIENT_AUTH:
    case TYPE_HTTPS_MISMATCHED_HOSTNAME:
      return certificates_dir_.AppendASCII("ok_cert.pem");
    case TYPE_HTTPS_EXPIRED_CERTIFICATE:
      return certificates_dir_.AppendASCII("expired_cert.pem");
    default:
      NOTREACHED();
  }
  return FilePath();
}

}  // namespace net
<commit_msg>GTTF: Use different port number for all types of TestServer.<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/test/test_server.h"

#include <algorithm>
#include <string>
#include <vector>

#include "build/build_config.h"

#if defined(OS_MACOSX)
#include "net/base/x509_certificate.h"
#endif

#include "base/command_line.h"
#include "base/file_util.h"
#include "base/leak_annotations.h"
#include "base/logging.h"
#include "base/path_service.h"
#include "base/string_number_conversions.h"
#include "base/utf_string_conversions.h"
#include "googleurl/src/gurl.h"
#include "net/base/cert_test_util.h"
#include "net/base/host_port_pair.h"
#include "net/base/host_resolver.h"
#include "net/base/test_completion_callback.h"
#include "net/socket/tcp_client_socket.h"
#include "net/test/python_utils.h"
#include "testing/platform_test.h"

namespace {

// Number of connection attempts for tests.
const int kServerConnectionAttempts = 10;

// Connection timeout in milliseconds for tests.
const int kServerConnectionTimeoutMs = 1000;

const char kTestServerShardFlag[] = "test-server-shard";

int GetPortBase(net::TestServer::Type type) {
  switch (type) {
    case net::TestServer::TYPE_FTP:
      return 3117;
    case net::TestServer::TYPE_HTTP:
      return 1337;
    case net::TestServer::TYPE_HTTPS:
      return 9443;
    case net::TestServer::TYPE_HTTPS_CLIENT_AUTH:
      return 9543;
    case net::TestServer::TYPE_HTTPS_EXPIRED_CERTIFICATE:
      // TODO(phajdan.jr): Some tests rely on this hardcoded value.
      // Some uses of this are actually in .html/.js files.
      return 9666;
    case net::TestServer::TYPE_HTTPS_MISMATCHED_HOSTNAME:
      return 9643;
    default:
      NOTREACHED();
  }
  return -1;
}

int GetPort(net::TestServer::Type type) {
  int port = GetPortBase(type);
  if (CommandLine::ForCurrentProcess()->HasSwitch(kTestServerShardFlag)) {
    std::string shard_str(CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
                              kTestServerShardFlag));
    int shard = -1;
    if (base::StringToInt(shard_str, &shard)) {
      port += shard;
    } else {
      LOG(FATAL) << "Got invalid " << kTestServerShardFlag << " flag value. "
                 << "An integer is expected.";
    }
  }
  return port;
}

std::string GetHostname(net::TestServer::Type type) {
  if (type == net::TestServer::TYPE_HTTPS_MISMATCHED_HOSTNAME) {
    // Return a different hostname string that resolves to the same hostname.
    return "localhost";
  }

  return "127.0.0.1";
}

}  // namespace

namespace net {

#if defined(OS_MACOSX)
void SetMacTestCertificate(X509Certificate* cert);
#endif

TestServer::TestServer(Type type, const FilePath& document_root)
    : host_port_pair_(GetHostname(type), GetPort(type)),
      process_handle_(base::kNullProcessHandle),
      type_(type) {
  FilePath src_dir;
  PathService::Get(base::DIR_SOURCE_ROOT, &src_dir);

  document_root_ = src_dir.Append(document_root);

  certificates_dir_ = src_dir.Append(FILE_PATH_LITERAL("net"))
                       .Append(FILE_PATH_LITERAL("data"))
                       .Append(FILE_PATH_LITERAL("ssl"))
                       .Append(FILE_PATH_LITERAL("certificates"));
}

TestServer::~TestServer() {
#if defined(OS_MACOSX)
  SetMacTestCertificate(NULL);
#endif
  Stop();
}

bool TestServer::Start() {
  if (GetScheme() == "https") {
    if (!LoadTestRootCert())
      return false;
    if (!CheckCATrusted())
      return false;
  }

  // Get path to python server script
  FilePath testserver_path;
  if (!PathService::Get(base::DIR_SOURCE_ROOT, &testserver_path)) {
    LOG(ERROR) << "Failed to get DIR_SOURCE_ROOT";
    return false;
  }
  testserver_path = testserver_path
      .Append(FILE_PATH_LITERAL("net"))
      .Append(FILE_PATH_LITERAL("tools"))
      .Append(FILE_PATH_LITERAL("testserver"))
      .Append(FILE_PATH_LITERAL("testserver.py"));

  if (!SetPythonPath())
    return false;

  if (!LaunchPython(testserver_path))
    return false;

  if (!WaitToStart()) {
    Stop();
    return false;
  }

  return true;
}

bool TestServer::Stop() {
  if (!process_handle_)
    return true;

  // First check if the process has already terminated.
  bool ret = base::WaitForSingleProcess(process_handle_, 0);
  if (!ret)
    ret = base::KillProcess(process_handle_, 1, true);

  if (ret) {
    base::CloseProcessHandle(process_handle_);
    process_handle_ = base::kNullProcessHandle;
  } else {
    LOG(INFO) << "Kill failed?";
  }

  return ret;
}

bool TestServer::WaitToFinish(int timeout_ms) {
  if (!process_handle_)
    return true;

  bool ret = base::WaitForSingleProcess(process_handle_, timeout_ms);
  if (ret) {
    base::CloseProcessHandle(process_handle_);
    process_handle_ = base::kNullProcessHandle;
  } else {
    LOG(ERROR) << "Timed out.";
  }
  return ret;
}

std::string TestServer::GetScheme() const {
  switch (type_) {
    case TYPE_FTP:
      return "ftp";
    case TYPE_HTTP:
      return "http";
    case TYPE_HTTPS:
    case TYPE_HTTPS_CLIENT_AUTH:
    case TYPE_HTTPS_MISMATCHED_HOSTNAME:
    case TYPE_HTTPS_EXPIRED_CERTIFICATE:
      return "https";
    default:
      NOTREACHED();
  }
  return std::string();
}

bool TestServer::GetAddressList(AddressList* address_list) const {
  DCHECK(address_list);

  scoped_refptr<HostResolver> resolver(
      CreateSystemHostResolver(HostResolver::kDefaultParallelism, NULL));
  HostResolver::RequestInfo info(host_port_pair_);
  int rv = resolver->Resolve(info, address_list, NULL, NULL, BoundNetLog());
  if (rv != net::OK) {
    LOG(ERROR) << "Failed to resolve hostname: " << host_port_pair_.host();
    return false;
  }
  return true;
}

GURL TestServer::GetURL(const std::string& path) {
  return GURL(GetScheme() + "://" + host_port_pair_.ToString() +
              "/" + path);
}

GURL TestServer::GetURLWithUser(const std::string& path,
                                const std::string& user) {
  return GURL(GetScheme() + "://" + user + "@" +
              host_port_pair_.ToString() +
              "/" + path);
}

GURL TestServer::GetURLWithUserAndPassword(const std::string& path,
                                           const std::string& user,
                                           const std::string& password) {
  return GURL(GetScheme() + "://" + user + ":" + password +
              "@" + host_port_pair_.ToString() +
              "/" + path);
}

bool TestServer::SetPythonPath() {
  FilePath third_party_dir;
  if (!PathService::Get(base::DIR_SOURCE_ROOT, &third_party_dir)) {
    LOG(ERROR) << "Failed to get DIR_SOURCE_ROOT";
    return false;
  }
  third_party_dir = third_party_dir.Append(FILE_PATH_LITERAL("third_party"));

  AppendToPythonPath(third_party_dir.Append(FILE_PATH_LITERAL("tlslite")));
  AppendToPythonPath(third_party_dir.Append(FILE_PATH_LITERAL("pyftpdlib")));

  // Locate the Python code generated by the protocol buffers compiler.
  FilePath generated_code_dir;
  if (!PathService::Get(base::DIR_EXE, &generated_code_dir)) {
    LOG(ERROR) << "Failed to get DIR_EXE";
    return false;
  }
  generated_code_dir = generated_code_dir.Append(FILE_PATH_LITERAL("pyproto"));
  AppendToPythonPath(generated_code_dir);
  AppendToPythonPath(generated_code_dir.Append(FILE_PATH_LITERAL("sync_pb")));

  return true;
}

FilePath TestServer::GetRootCertificatePath() {
  return certificates_dir_.AppendASCII("root_ca_cert.crt");
}

bool TestServer::LoadTestRootCert() {
#if defined(USE_NSS)
  if (cert_)
    return true;

  // TODO(dkegel): figure out how to get this to only happen once?

  // This currently leaks a little memory.
  // TODO(dkegel): fix the leak and remove the entry in
  // tools/valgrind/memcheck/suppressions.txt
  ANNOTATE_SCOPED_MEMORY_LEAK;  // Tell heap checker about the leak.
  cert_ = LoadTemporaryRootCert(GetRootCertificatePath());
  return (cert_ != NULL);
#elif defined(OS_MACOSX)
  X509Certificate* cert = LoadTemporaryRootCert(GetRootCertificatePath());
  if (!cert)
    return false;
  SetMacTestCertificate(cert);
  return true;
#else
  return true;
#endif
}

FilePath TestServer::GetCertificatePath() {
  switch (type_) {
    case TYPE_FTP:
    case TYPE_HTTP:
      return FilePath();
    case TYPE_HTTPS:
    case TYPE_HTTPS_CLIENT_AUTH:
    case TYPE_HTTPS_MISMATCHED_HOSTNAME:
      return certificates_dir_.AppendASCII("ok_cert.pem");
    case TYPE_HTTPS_EXPIRED_CERTIFICATE:
      return certificates_dir_.AppendASCII("expired_cert.pem");
    default:
      NOTREACHED();
  }
  return FilePath();
}

}  // namespace net
<|endoftext|>


<commit_before>//
// Created by pkanev on 12/8/2017.
//

#include <include/v8.h>
#include <assert.h>
#include <android/log.h>
#include <cstdlib>
#include <chrono>
#include <iomanip>
#include <sstream>
#include <V8GlobalHelpers.h>
#include <v8_inspector/src/inspector/v8-log-agent-impl.h>
#include <JsV8InspectorClient.h>
#include "Console.h"

namespace tns {

v8::Local<v8::Object> Console::createConsole(v8::Local<v8::Context> context, const std::string& filesPath) {
    v8::Context::Scope contextScope(context);
    v8::Isolate* isolate = context->GetIsolate();

    v8::Local<v8::Object> console = v8::Object::New(isolate);
    bool success = console->SetPrototype(context, v8::Object::New(isolate)).FromMaybe(false);

    assert(success);

    std::map<std::string, double> timersMap;
    Console::s_isolateToConsoleTimersMap.insert(std::make_pair(context->GetIsolate(), timersMap));

    bindFunctionProperty(context, console, "assert", assertCallback);
    bindFunctionProperty(context, console, "error", errorCallback);
    bindFunctionProperty(context, console, "info", infoCallback);
    bindFunctionProperty(context, console, "log", logCallback);
    bindFunctionProperty(context, console, "warn", warnCallback);
    bindFunctionProperty(context, console, "dir", dirCallback);
    bindFunctionProperty(context, console, "trace", traceCallback);
    bindFunctionProperty(context, console, "time", timeCallback);
    bindFunctionProperty(context, console, "timeEnd", timeEndCallback);

    return console;
}

void Console::sendToADBLogcat(const std::string& message, android_LogPriority logPriority) {
    // split strings into chunks of 4000 characters
    // __android_log_write can't send more than 4000 to the stdout at a time

    auto messageLength = message.length();
    int maxStringLength = 4000;

    if (messageLength < maxStringLength) {
        __android_log_write(logPriority, Console::LOG_TAG, message.c_str());
    } else {
        for (int i = 0; i < messageLength; i += maxStringLength) {
            auto messagePart = message.substr(i, maxStringLength);

            __android_log_write(logPriority, Console::LOG_TAG, messagePart.c_str());
        }
    }
}

void Console::sendToDevToolsFrontEnd(v8::Isolate* isolate, const std::string& message, const std::string& logLevel) {
    if (!JsV8InspectorClient::inspectorIsConnected()) {
        return;
    }

    auto stack = v8::StackTrace::CurrentStackTrace(isolate, 1, v8::StackTrace::StackTraceOptions::kDetailed);

    auto frame = stack->GetFrame(0);

    // will be no-op in non-debuggable builds
    v8_inspector::V8LogAgentImpl::EntryAdded(message, logLevel, ArgConverter::ConvertToString(frame->GetScriptNameOrSourceURL()), frame->GetLineNumber());
}

const std::string buildLogString(const v8::FunctionCallbackInfo<v8::Value>& info) {
    auto isolate = info.GetIsolate();

    v8::HandleScope scope(isolate);

    std::stringstream ss;

    auto argLen = info.Length();
    if (argLen) {
        for (int i = 0; i < argLen; i++) {
            v8::Local<v8::String> argString;
            if (info[i]->IsFunction()) {
                info[i]->ToDetailString(isolate->GetCurrentContext()).ToLocal(&argString);
            } else if (info[i]->IsObject()) {
                v8::Local<v8::Object> obj = info[i].As<v8::Object>();
                argString = JsonStringifyObject(isolate, obj);
            } else {
                info[i]->ToDetailString(isolate->GetCurrentContext()).ToLocal(&argString);
            }

            // separate args with a space
            if (i != 0) {
                ss << " ";
            }

            ss << ArgConverter::ConvertToString(argString);
        }
    } else {
        ss << std::endl;
    }

    std::string stringResult = ss.str();

    return stringResult;
}

void Console::assertCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
    auto isolate = info.GetIsolate();

    auto argLen = info.Length();
    auto expressionPasses = argLen && info[0]->BooleanValue();

    if (!expressionPasses) {
        std::stringstream assertionError;

        assertionError << "Assertion failed: ";

        if (argLen > 1) {
            v8::Local<v8::String> argString;
            info[1]->ToDetailString(isolate->GetCurrentContext()).ToLocal(&argString);

            assertionError << ArgConverter::ConvertToString(argString);
        } else {
            assertionError << "console.assert";
        }

        std::string log = assertionError.str();
        sendToADBLogcat(log, ANDROID_LOG_ERROR);
        sendToDevToolsFrontEnd(isolate, log, "error");
    }
}

void Console::errorCallback(const v8::FunctionCallbackInfo <v8::Value>& info) {
    std::string log = buildLogString(info);

    sendToADBLogcat(log, ANDROID_LOG_ERROR);
    sendToDevToolsFrontEnd(info.GetIsolate(), log, "error");
}

void Console::infoCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
    std::string log = buildLogString(info);

    sendToADBLogcat(log, ANDROID_LOG_INFO);
    sendToDevToolsFrontEnd(info.GetIsolate(), log, "info");
}

void Console::logCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
    std::string log = buildLogString(info);

    sendToADBLogcat(log, ANDROID_LOG_INFO);
    sendToDevToolsFrontEnd(info.GetIsolate(), log, "info");
}

void Console::warnCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
    std::string log = buildLogString(info);

    sendToADBLogcat(log, ANDROID_LOG_WARN);
    sendToDevToolsFrontEnd(info.GetIsolate(), log, "warning");
}

void Console::dirCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
    auto isolate = info.GetIsolate();
    auto context = isolate->GetCurrentContext();

    v8::HandleScope scope(isolate);

    std::stringstream ss;

    auto argLen = info.Length();
    if (argLen) {
        if (info[0]->IsObject()) {
            ss << "==== object dump start ====" << std::endl;
            v8::Local<v8::Object> argObject = info[0].As<v8::Object>();

            v8::Local<v8::Array> propNames;
            argObject->GetPropertyNames(context).ToLocal(&propNames);

            auto propertiesLen = propNames->Length();
            for (int i = 0; i < propertiesLen; i++) {
                auto propertyName = propNames->Get(context, i).ToLocalChecked();
                auto propertyValue = argObject->Get(context, propertyName).ToLocalChecked();

                auto propIsFunction = propertyValue->IsFunction();

                ss << ArgConverter::ConvertToString(propertyName->ToString(isolate));

                if (propIsFunction) {
                    ss << "()";
                }

                ss << std::endl;
            }

            ss << "==== object dump end ====" << std::endl;
        } else {
            v8::Local<v8::String> argString;
            info[0]->ToString(isolate->GetCurrentContext()).ToLocal(&argString);

            ss << ArgConverter::ConvertToString(argString);
        }
    }

    std::string log = ss.str();

    sendToADBLogcat(log, ANDROID_LOG_INFO);
    sendToDevToolsFrontEnd(isolate, log, "info");
}

const std::string buildStacktraceFrameLocationPart(v8::Local<v8::StackFrame> frame) {
    std::stringstream ss;

    auto scriptName = frame->GetScriptNameOrSourceURL();
    auto scriptNameConverted = ArgConverter::ConvertToString(scriptName);
    if (scriptNameConverted.length() < 1) {
        ss << "VM";
    } else {
        ss << scriptNameConverted << ":" << frame->GetLineNumber() << ":" << frame->GetColumn();
    }

    std::string stringResult = ss.str();

    return stringResult;
}

const std::string buildStacktraceFrameMessage(v8::Local<v8::StackFrame> frame) {
    std::stringstream ss;

    auto functionName = frame->GetFunctionName();
    auto functionNameConverted = ArgConverter::ConvertToString(functionName);
    if (functionNameConverted.length() < 1) {
        functionNameConverted = "<anonymous>";
    }

    if (frame->IsConstructor()) {
        ss << "at new " << functionNameConverted << " (" << buildStacktraceFrameLocationPart(frame) << ")";
    } else if (frame->IsEval()) {
        ss << "eval at " << buildStacktraceFrameLocationPart(frame) << std::endl;
    } else {
        ss << "at " << functionNameConverted << " (" << buildStacktraceFrameLocationPart(frame) << ")";
    }

    std::string stringResult = ss.str();

    return stringResult;
}

void Console::traceCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
    auto isolate = info.GetIsolate();
    std::stringstream ss;

    std::string logString = buildLogString(info);

    if (logString.compare("\n") == 0) {
        ss << "Trace";
    } else {
        ss << "Trace: " << logString;
    }

    ss << std::endl;

    v8::HandleScope scope(isolate);

    auto stack = v8::StackTrace::CurrentStackTrace(isolate, 10, v8::StackTrace::StackTraceOptions::kDetailed);

    auto framesCount = stack->GetFrameCount();

    for (int i = 0; i < framesCount; i++) {
        auto frame = stack->GetFrame(i);

        ss << buildStacktraceFrameMessage(frame) << std::endl;
    }

    std::string log = ss.str();
    __android_log_write(ANDROID_LOG_ERROR, LOG_TAG, log.c_str());
    sendToDevToolsFrontEnd(isolate, log, "error");
}

void Console::timeCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
    auto isolate = info.GetIsolate();

    v8::HandleScope scope(isolate);

    auto argLen = info.Length();
    std::string label = "default";

    v8::Local<v8::String> argString;
    if (argLen && info[0]->ToString(isolate->GetCurrentContext()).ToLocal(&argString)) {
        label = ArgConverter::ConvertToString(argString);
    }

    auto it = Console::s_isolateToConsoleTimersMap.find(isolate);
    if (it == Console::s_isolateToConsoleTimersMap.end()) {
        // throw?
    }

    auto nano = std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::system_clock::now());
    double timeStamp = nano.time_since_epoch().count();

    it->second.insert(std::make_pair(label, timeStamp));
}

void Console::timeEndCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
    auto isolate = info.GetIsolate();

    auto argLen = info.Length();
    std::string label = "default";

    v8::Local<v8::String> argString;
    if (argLen && info[0]->ToString(isolate->GetCurrentContext()).ToLocal(&argString)) {
        label = ArgConverter::ConvertToString(argString);
    }

    auto it = Console::s_isolateToConsoleTimersMap.find(isolate);
    if (it == Console::s_isolateToConsoleTimersMap.end()) {
        // throw?
    }

    std::map<std::string, double> timersMap = it->second;

    auto itTimersMap = timersMap.find(label);
    if (itTimersMap == timersMap.end()) {
        std::string warning = std::string("No such label '" + label + "' for console.timeEnd()");

        __android_log_write(ANDROID_LOG_WARN, LOG_TAG, warning.c_str());
        sendToDevToolsFrontEnd(isolate, warning, "warning");

        return;
    }

    auto nano = std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::system_clock::now());
    double endTimeStamp = nano.time_since_epoch().count();
    double startTimeStamp = itTimersMap->second;

    it->second.erase(label);

    auto diff = endTimeStamp - startTimeStamp;

    std::stringstream ss;
    ss << label << ": " << std::fixed << std::setprecision(2) << diff << "ms" ;
    std::string log = ss.str();

    __android_log_write(ANDROID_LOG_INFO, LOG_TAG, log.c_str());
    sendToDevToolsFrontEnd(isolate, log, "info");
}

const char* Console::LOG_TAG = "JS";
std::map<v8::Isolate*, std::map<std::string, double>> Console::s_isolateToConsoleTimersMap;
}<commit_msg>refactor console.assert to use console.log string building logic when assertion fails<commit_after>//
// Created by pkanev on 12/8/2017.
//

#include <include/v8.h>
#include <assert.h>
#include <android/log.h>
#include <cstdlib>
#include <chrono>
#include <iomanip>
#include <sstream>
#include <V8GlobalHelpers.h>
#include <v8_inspector/src/inspector/v8-log-agent-impl.h>
#include <JsV8InspectorClient.h>
#include "Console.h"

namespace tns {

v8::Local<v8::Object> Console::createConsole(v8::Local<v8::Context> context, const std::string& filesPath) {
    v8::Context::Scope contextScope(context);
    v8::Isolate* isolate = context->GetIsolate();

    v8::Local<v8::Object> console = v8::Object::New(isolate);
    bool success = console->SetPrototype(context, v8::Object::New(isolate)).FromMaybe(false);

    assert(success);

    std::map<std::string, double> timersMap;
    Console::s_isolateToConsoleTimersMap.insert(std::make_pair(context->GetIsolate(), timersMap));

    bindFunctionProperty(context, console, "assert", assertCallback);
    bindFunctionProperty(context, console, "error", errorCallback);
    bindFunctionProperty(context, console, "info", infoCallback);
    bindFunctionProperty(context, console, "log", logCallback);
    bindFunctionProperty(context, console, "warn", warnCallback);
    bindFunctionProperty(context, console, "dir", dirCallback);
    bindFunctionProperty(context, console, "trace", traceCallback);
    bindFunctionProperty(context, console, "time", timeCallback);
    bindFunctionProperty(context, console, "timeEnd", timeEndCallback);

    return console;
}

void Console::sendToADBLogcat(const std::string& message, android_LogPriority logPriority) {
    // split strings into chunks of 4000 characters
    // __android_log_write can't send more than 4000 to the stdout at a time

    auto messageLength = message.length();
    int maxStringLength = 4000;

    if (messageLength < maxStringLength) {
        __android_log_write(logPriority, Console::LOG_TAG, message.c_str());
    } else {
        for (int i = 0; i < messageLength; i += maxStringLength) {
            auto messagePart = message.substr(i, maxStringLength);

            __android_log_write(logPriority, Console::LOG_TAG, messagePart.c_str());
        }
    }
}

void Console::sendToDevToolsFrontEnd(v8::Isolate* isolate, const std::string& message, const std::string& logLevel) {
    if (!JsV8InspectorClient::inspectorIsConnected()) {
        return;
    }

    auto stack = v8::StackTrace::CurrentStackTrace(isolate, 1, v8::StackTrace::StackTraceOptions::kDetailed);

    auto frame = stack->GetFrame(0);

    // will be no-op in non-debuggable builds
    v8_inspector::V8LogAgentImpl::EntryAdded(message, logLevel, ArgConverter::ConvertToString(frame->GetScriptNameOrSourceURL()), frame->GetLineNumber());
}

const std::string buildLogString(const v8::FunctionCallbackInfo<v8::Value>& info, int startingIndex = 0) {
    auto isolate = info.GetIsolate();

    v8::HandleScope scope(isolate);

    std::stringstream ss;

    auto argLen = info.Length();
    if (argLen) {
        for (int i = startingIndex; i < argLen; i++) {
            v8::Local<v8::String> argString;
            if (info[i]->IsFunction()) {
                info[i]->ToDetailString(isolate->GetCurrentContext()).ToLocal(&argString);
            } else if (info[i]->IsObject()) {
                v8::Local<v8::Object> obj = info[i].As<v8::Object>();
                argString = JsonStringifyObject(isolate, obj);
            } else {
                info[i]->ToDetailString(isolate->GetCurrentContext()).ToLocal(&argString);
            }

            // separate args with a space
            if (i != 0) {
                ss << " ";
            }

            ss << ArgConverter::ConvertToString(argString);
        }
    } else {
        ss << std::endl;
    }

    std::string stringResult = ss.str();

    return stringResult;
}

void Console::assertCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
    auto isolate = info.GetIsolate();

    auto argLen = info.Length();
    auto expressionPasses = argLen && info[0]->BooleanValue();

    if (!expressionPasses) {
        std::stringstream assertionError;

        assertionError << "Assertion failed: ";

        if (argLen > 1) {
            assertionError << buildLogString(info, 1);
        } else {
            assertionError << "console.assert";
        }

        std::string log = assertionError.str();
        sendToADBLogcat(log, ANDROID_LOG_ERROR);
        sendToDevToolsFrontEnd(isolate, log, "error");
    }
}

void Console::errorCallback(const v8::FunctionCallbackInfo <v8::Value>& info) {
    std::string log = buildLogString(info);

    sendToADBLogcat(log, ANDROID_LOG_ERROR);
    sendToDevToolsFrontEnd(info.GetIsolate(), log, "error");
}

void Console::infoCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
    std::string log = buildLogString(info);

    sendToADBLogcat(log, ANDROID_LOG_INFO);
    sendToDevToolsFrontEnd(info.GetIsolate(), log, "info");
}

void Console::logCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
    std::string log = buildLogString(info);

    sendToADBLogcat(log, ANDROID_LOG_INFO);
    sendToDevToolsFrontEnd(info.GetIsolate(), log, "info");
}

void Console::warnCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
    std::string log = buildLogString(info);

    sendToADBLogcat(log, ANDROID_LOG_WARN);
    sendToDevToolsFrontEnd(info.GetIsolate(), log, "warning");
}

void Console::dirCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
    auto isolate = info.GetIsolate();
    auto context = isolate->GetCurrentContext();

    v8::HandleScope scope(isolate);

    std::stringstream ss;

    auto argLen = info.Length();
    if (argLen) {
        if (info[0]->IsObject()) {
            ss << "==== object dump start ====" << std::endl;
            v8::Local<v8::Object> argObject = info[0].As<v8::Object>();

            v8::Local<v8::Array> propNames;
            argObject->GetPropertyNames(context).ToLocal(&propNames);

            auto propertiesLen = propNames->Length();
            for (int i = 0; i < propertiesLen; i++) {
                auto propertyName = propNames->Get(context, i).ToLocalChecked();
                auto propertyValue = argObject->Get(context, propertyName).ToLocalChecked();

                auto propIsFunction = propertyValue->IsFunction();

                ss << ArgConverter::ConvertToString(propertyName->ToString(isolate));

                if (propIsFunction) {
                    ss << "()";
                }

                ss << std::endl;
            }

            ss << "==== object dump end ====" << std::endl;
        } else {
            std::string logString = buildLogString(info);

            ss << logString;
        }
    } else {
        ss << std::endl;
    }

    std::string log = ss.str();

    sendToADBLogcat(log, ANDROID_LOG_INFO);
    sendToDevToolsFrontEnd(isolate, log, "info");
}

const std::string buildStacktraceFrameLocationPart(v8::Local<v8::StackFrame> frame) {
    std::stringstream ss;

    auto scriptName = frame->GetScriptNameOrSourceURL();
    auto scriptNameConverted = ArgConverter::ConvertToString(scriptName);
    if (scriptNameConverted.length() < 1) {
        ss << "VM";
    } else {
        ss << scriptNameConverted << ":" << frame->GetLineNumber() << ":" << frame->GetColumn();
    }

    std::string stringResult = ss.str();

    return stringResult;
}

const std::string buildStacktraceFrameMessage(v8::Local<v8::StackFrame> frame) {
    std::stringstream ss;

    auto functionName = frame->GetFunctionName();
    auto functionNameConverted = ArgConverter::ConvertToString(functionName);
    if (functionNameConverted.length() < 1) {
        functionNameConverted = "<anonymous>";
    }

    if (frame->IsConstructor()) {
        ss << "at new " << functionNameConverted << " (" << buildStacktraceFrameLocationPart(frame) << ")";
    } else if (frame->IsEval()) {
        ss << "eval at " << buildStacktraceFrameLocationPart(frame) << std::endl;
    } else {
        ss << "at " << functionNameConverted << " (" << buildStacktraceFrameLocationPart(frame) << ")";
    }

    std::string stringResult = ss.str();

    return stringResult;
}

void Console::traceCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
    auto isolate = info.GetIsolate();
    std::stringstream ss;

    std::string logString = buildLogString(info);

    if (logString.compare("\n") == 0) {
        ss << "Trace";
    } else {
        ss << "Trace: " << logString;
    }

    ss << std::endl;

    v8::HandleScope scope(isolate);

    auto stack = v8::StackTrace::CurrentStackTrace(isolate, 10, v8::StackTrace::StackTraceOptions::kDetailed);

    auto framesCount = stack->GetFrameCount();

    for (int i = 0; i < framesCount; i++) {
        auto frame = stack->GetFrame(i);

        ss << buildStacktraceFrameMessage(frame) << std::endl;
    }

    std::string log = ss.str();
    __android_log_write(ANDROID_LOG_ERROR, LOG_TAG, log.c_str());
    sendToDevToolsFrontEnd(isolate, log, "error");
}

void Console::timeCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
    auto isolate = info.GetIsolate();

    v8::HandleScope scope(isolate);

    auto argLen = info.Length();
    std::string label = "default";

    v8::Local<v8::String> argString;
    if (argLen && info[0]->ToString(isolate->GetCurrentContext()).ToLocal(&argString)) {
        label = ArgConverter::ConvertToString(argString);
    }

    auto it = Console::s_isolateToConsoleTimersMap.find(isolate);
    if (it == Console::s_isolateToConsoleTimersMap.end()) {
        // throw?
    }

    auto nano = std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::system_clock::now());
    double timeStamp = nano.time_since_epoch().count();

    it->second.insert(std::make_pair(label, timeStamp));
}

void Console::timeEndCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
    auto isolate = info.GetIsolate();

    auto argLen = info.Length();
    std::string label = "default";

    v8::Local<v8::String> argString;
    if (argLen && info[0]->ToString(isolate->GetCurrentContext()).ToLocal(&argString)) {
        label = ArgConverter::ConvertToString(argString);
    }

    auto it = Console::s_isolateToConsoleTimersMap.find(isolate);
    if (it == Console::s_isolateToConsoleTimersMap.end()) {
        // throw?
    }

    std::map<std::string, double> timersMap = it->second;

    auto itTimersMap = timersMap.find(label);
    if (itTimersMap == timersMap.end()) {
        std::string warning = std::string("No such label '" + label + "' for console.timeEnd()");

        __android_log_write(ANDROID_LOG_WARN, LOG_TAG, warning.c_str());
        sendToDevToolsFrontEnd(isolate, warning, "warning");

        return;
    }

    auto nano = std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::system_clock::now());
    double endTimeStamp = nano.time_since_epoch().count();
    double startTimeStamp = itTimersMap->second;

    it->second.erase(label);

    auto diff = endTimeStamp - startTimeStamp;

    std::stringstream ss;
    ss << label << ": " << std::fixed << std::setprecision(2) << diff << "ms" ;
    std::string log = ss.str();

    __android_log_write(ANDROID_LOG_INFO, LOG_TAG, log.c_str());
    sendToDevToolsFrontEnd(isolate, log, "info");
}

const char* Console::LOG_TAG = "JS";
std::map<v8::Isolate*, std::map<std::string, double>> Console::s_isolateToConsoleTimersMap;
}<|endoftext|>


<commit_before><commit_msg>gtk: Don't wrap around when scrolling the tab strip using the scroll event.  This matches native gtk behavior.<commit_after><|endoftext|>