chromium/ppapi/proxy/udp_socket_filter.h

// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef PPAPI_PROXY_UDP_SOCKET_FILTER_H_
#define PPAPI_PROXY_UDP_SOCKET_FILTER_H_

#include <stddef.h>
#include <stdint.h>

#include <memory>
#include <string>
#include <unordered_map>

#include "base/compiler_specific.h"
#include "base/containers/queue.h"
#include "base/memory/ref_counted.h"
#include "ppapi/c/ppb_udp_socket.h"
#include "ppapi/c/private/ppb_net_address_private.h"
#include "ppapi/proxy/plugin_resource.h"
#include "ppapi/proxy/ppapi_proxy_export.h"
#include "ppapi/proxy/resource_message_filter.h"
#include "ppapi/shared_impl/tracked_callback.h"

namespace ppapi {
namespace proxy {

class ResourceMessageReplyParams;

// Handles receiving UDP packets on the IO thread so that when the recipient is
// not on the main thread, we can post directly to the appropriate thread.
class PPAPI_PROXY_EXPORT UDPSocketFilter : public ResourceMessageFilter {
 public:
  UDPSocketFilter();

  // All these are called on whatever thread the plugin wants, while already
  // holding the ppapi::ProxyLock. The "slot_available_callback" will be invoked
  // whenever we detect that a slot is now available, so that the client can
  // take appropriate action (like informing the host we can receive another
  // buffer). It will always be run with the ProxyLock.
  void AddUDPResource(PP_Instance instance,
                      PP_Resource resource,
                      bool private_api,
                      base::RepeatingClosure slot_available_callback);
  void RemoveUDPResource(PP_Resource resource);
  // Note, the slot_available_callback that was provided to AddUDPResource may
  // be invoked during the RequestData call; this gives the client the
  // opportunity to post a message to the host immediately.
  int32_t RequestData(PP_Resource resource,
                      int32_t num_bytes,
                      char* buffer,
                      PP_Resource* addr,
                      const scoped_refptr<TrackedCallback>& callback);

  // ResourceMessageFilter implementation.
  bool OnResourceReplyReceived(const ResourceMessageReplyParams& reply_params,
                               const IPC::Message& nested_msg) override;

  PP_NetAddress_Private GetLastAddrPrivate(PP_Resource resource) const;

  // The maximum number of bytes that each
  // PpapiPluginMsg_PPBUDPSocket_PushRecvResult message is allowed to carry.
  static const int32_t kMaxReadSize;
  // The maximum number that we allow for setting
  // PP_UDPSOCKET_OPTION_RECV_BUFFER_SIZE. This number is only for input
  // argument sanity check, it doesn't mean the browser guarantees to support
  // such a buffer size.
  static const int32_t kMaxReceiveBufferSize;
  // The maximum number of received packets that we allow instances of this
  // class to buffer.
  static const size_t kPluginReceiveBufferSlots;

 private:
  // The queue of received data intended for 1 UDPSocketResourceBase. All usage
  // must be protected by UDPSocketFilter::lock_.
  class RecvQueue {
   public:
    explicit RecvQueue(PP_Instance instance,
                       bool private_api,
                       base::RepeatingClosure slot_available_callback);
    ~RecvQueue();

    // Called on the IO thread when data is received. It will post |callback_|
    // if it's valid, otherwise push the data on buffers_.
    // The ppapi::ProxyLock should *not* be held, and won't be acquired.
    void DataReceivedOnIOThread(int32_t result,
                                const std::string& d,
                                const PP_NetAddress_Private& addr);
    // Called on whatever thread the plugin chooses. Must already hold the
    // PpapiProxyLock. Returns a code from pp_errors.h, or a positive number.
    //
    // Note, the out-params are owned by the plugin, and if the request can't be
    // handled immediately, they will be written later just before the callback
    // is invoked.
    int32_t RequestData(int32_t num_bytes,
                        char* buffer_out,
                        PP_Resource* addr_out,
                        const scoped_refptr<TrackedCallback>& callback);
    PP_NetAddress_Private GetLastAddrPrivate() const;

   private:
    struct RecvBuffer {
      int32_t result;
      std::string data;
      PP_NetAddress_Private addr;
    };
    base::queue<RecvBuffer> recv_buffers_;

    PP_Instance pp_instance_;
    scoped_refptr<ppapi::TrackedCallback> recvfrom_callback_;
    char* read_buffer_;
    int32_t bytes_to_read_;
    PP_Resource* recvfrom_addr_resource_;
    PP_NetAddress_Private last_recvfrom_addr_;
    bool private_api_;
    // Callback to invoke when a UDP receive slot is available.
    base::RepeatingClosure slot_available_callback_;
  };

 private:
  // This is deleted via RefCountedThreadSafe (see ResourceMessageFilter).
  ~UDPSocketFilter();
  void OnPluginMsgPushRecvResult(const ResourceMessageReplyParams& params,
                                 int32_t result,
                                 const std::string& data,
                                 const PP_NetAddress_Private& addr);

  // lock_ protects queues_.
  //
  // Lock order (if >1 acquired):
  // 1 ppapi::ProxyLock
  // \-->2 Filter lock_
  mutable base::Lock lock_;
  std::unordered_map<PP_Resource, std::unique_ptr<RecvQueue>> queues_;
};

}  // namespace proxy
}  // namespace ppapi

#endif  // PPAPI_PROXY_UDP_SOCKET_FILTER_H_