Coverage Report

Created: 2026-10-15 06:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/curl_fuzzer/proto_fuzzer/mock_server.h
Line
Count
Source
1
/*
2
 * Copyright (C) Max Dymond, <cmeister2@gmail.com>, et al.
3
 *
4
 * SPDX-License-Identifier: curl
5
 */
6
7
/// @file
8
/// @brief MockConnection and MockServer — the in-process peer that feeds
9
///        canned response bytes to libcurl over a socketpair.
10
11
#ifndef PROTO_FUZZER_MOCK_SERVER_H_
12
#define PROTO_FUZZER_MOCK_SERVER_H_
13
14
#include <curl/curl.h>
15
16
#include <array>
17
#include <cstddef>
18
#include <memory>
19
#include <string>
20
#include <vector>
21
22
#include "curl_fuzzer.pb.h"
23
#include "proto_fuzzer/incoming_data_observer.h"
24
#include "proto_fuzzer/mock_server_base.h"
25
#include "proto_fuzzer/scenario_limits.h"
26
27
namespace proto_fuzzer {
28
29
class MockConnection {
30
 public:
31
  MockConnection();
32
  virtual ~MockConnection();
33
34
  MockConnection(const MockConnection&) = delete;
35
  MockConnection& operator=(const MockConnection&) = delete;
36
37
  virtual bool ok() const;
38
  curl_socket_t take_client_fd();
39
  int server_fd() const;
40
41
  std::size_t client_send_buffer_size() const;
42
43
  bool EnsureClientSendBufferSize(std::size_t minimum);
44
45
  virtual bool WriteAll(const unsigned char* data, std::size_t size);
46
  virtual std::size_t DrainIncoming();
47
  void SetIncomingDataObserver(IncomingDataObserver* observer);
48
  void ReadAvailable(std::string* out);
49
  virtual void ShutdownWrite();
50
51
  void ApplyBackpressure(int recv_buf_bytes, std::size_t drain_limit);
52
53
 protected:
54
  void NotifyIncomingData(const unsigned char* data, std::size_t size);
55
56
 private:
57
  int server_fd_;
58
  int client_fd_;
59
  std::size_t drain_limit_;
60
  IncomingDataObserver* incoming_data_observer_;
61
};
62
63
/// @class proto_fuzzer::MockServer
64
/// @brief HTTP in-process mock peer. Assigns one bounded response script to
65
///        each socket curl opens, allowing redirects and authentication
66
///        retries to progress without permitting an unbounded connection
67
///        graph. WebSocketMockServer keeps its separate single-socket model.
68
class MockServer : public MockServerBase {
69
 public:
70
  explicit MockServer(MultiDrivePolicy drive_policy = MultiDrivePolicy::kPerform);
71
  ~MockServer() override;
72
73
  void SetScripts(const curl::fuzzer::proto::Scenario& scenario);
74
75
  void SetKeepConnectionsOpen(bool keep_open);
76
77
  CURLcode DriveEasyScenario(CURL* easy, const curl::fuzzer::proto::Scenario& scenario,
78
                             bool use_events = false) override;
79
80
  ConnectOnlyRunStats DriveConnectOnlyScenario(CURL* easy, const curl::fuzzer::proto::Scenario& scenario) override;
81
82
  bool DeliverNextChunk();
83
  bool has_more_chunks() const;
84
85
  bool ServiceConnections();
86
87
  std::size_t opened_connection_count() const;
88
89
 protected:
90
  virtual std::unique_ptr<MockConnection> CreateConnection();
91
92
  void ResetConnections();
93
94
  virtual void ObserveActiveTransfer(CURL* easy);
95
96
  curl_socket_t HandleOpenSocket(curlsocktype purpose = CURLSOCKTYPE_IPCXN,
97
                                 struct curl_sockaddr* address = nullptr) override;
98
  void RunLoop(CURLM* multi, CURL* easy, const curl::fuzzer::proto::Scenario& scenario) override;
99
100
 private:
101
  /// One socket's borrowed response configuration plus its delivery cursor.
102
  /// The fixed script array is fully populated before curl runs, so both this
103
  /// object and its pointer into the caller-owned Scenario remain stable across
104
  /// callbacks. Counts record the exact runtime-visible prefix: raw chunks come
105
  /// first and structured frames consume only the remaining shared budget.
106
  struct ConnectionScript {
107
    const curl::fuzzer::proto::Connection* connection = nullptr;
108
    std::size_t raw_chunk_count = 0;
109
    std::size_t frame_chunk_count = 0;
110
    std::size_t next_chunk = 0;
111
112
    /// @return Number of raw and structured chunks visible to the runtime.
113
3.01M
    std::size_t chunk_count() const { return raw_chunk_count + frame_chunk_count; }
114
  };
115
116
  std::size_t DrainIncomingConnections();
117
118
  void RunSocketActionLoop(CURLM* multi, CURL* easy);
119
120
  std::array<ConnectionScript, scenario_limits::kMaxConnections> scripts_;
121
  std::size_t script_count_;
122
  std::size_t next_script_;
123
  ConnectionScript* active_script_;
124
125
  /// True only while HandleOpenSocket is preparing a synchronous easy drive.
126
  /// Every response chunk must be queued before the callback returns because
127
  /// curl_easy_perform does not yield control to the mock.
128
  bool preload_all_chunks_;
129
130
  /// Suppress response-side half-close after the final scripted chunk. This
131
  /// is false for every historical target and enabled only by MultiPlan.
132
  bool keep_connections_open_;
133
134
  /// Old server halves must outlive their active role: libcurl owns the client
135
  /// fds and may close or briefly revisit them after opening the next socket.
136
  /// Destroying a MockConnection at that boundary would turn valid lifecycle
137
  /// traffic into harness-generated ECONNRESET/SIGPIPE behavior.
138
  std::vector<std::unique_ptr<MockConnection>> previous_connections_;
139
};
140
141
}  // namespace proto_fuzzer
142
143
#endif  // PROTO_FUZZER_MOCK_SERVER_H_