• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

stacks-network / stacks-core / 25903914664-1

15 May 2026 06:28AM UTC coverage: 47.122% (-38.8%) from 85.959%
25903914664-1

Pull #7199

github

94e391
web-flow
Merge 109f2828c into 1c7b8e6ac
Pull Request #7199: Feat: L1 and L2 early unlocks, updating signer

103343 of 219309 relevant lines covered (47.12%)

12880462.62 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

65.99
/stackslib/src/net/server.rs
1
// Copyright (C) 2013-2020 Blockstack PBC, a public benefit corporation
2
// Copyright (C) 2020-2023 Stacks Open Internet Foundation
3
//
4
// This program is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8
//
9
// This program is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13
//
14
// You should have received a copy of the GNU General Public License
15
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
16

17
use std::collections::HashMap;
18

19
use mio::net as mio_net;
20
use stacks_common::types::net::{PeerAddress, PeerHost};
21
use stacks_common::util::get_epoch_time_secs;
22

23
use crate::net::connection::*;
24
use crate::net::http::*;
25
use crate::net::httpcore::*;
26
use crate::net::p2p::PeerNetwork;
27
use crate::net::poll::*;
28
use crate::net::rpc::*;
29
use crate::net::{Error as net_error, *};
30

31
#[derive(Debug)]
32
pub struct HttpPeer {
33
    /// ongoing http conversations (either they reached out to us, or we to them)
34
    pub peers: HashMap<usize, ConversationHttp>,
35
    pub sockets: HashMap<usize, mio_net::TcpStream>,
36

37
    /// outbound connections that are pending connection
38
    pub connecting: HashMap<
39
        usize,
40
        (
41
            mio_net::TcpStream,
42
            Option<UrlString>,
43
            Option<StacksHttpRequest>,
44
            u64,
45
        ),
46
    >,
47

48
    /// server network handle
49
    pub http_server_handle: usize,
50

51
    /// server socket address
52
    pub http_server_addr: SocketAddr,
53

54
    /// connection options
55
    pub connection_opts: ConnectionOptions,
56
}
57

58
impl HttpPeer {
59
    pub fn new(
2,757✔
60
        conn_opts: ConnectionOptions,
2,757✔
61
        server_handle: usize,
2,757✔
62
        server_addr: SocketAddr,
2,757✔
63
    ) -> HttpPeer {
2,757✔
64
        HttpPeer {
2,757✔
65
            peers: HashMap::new(),
2,757✔
66
            sockets: HashMap::new(),
2,757✔
67

2,757✔
68
            connecting: HashMap::new(),
2,757✔
69
            http_server_handle: server_handle,
2,757✔
70
            http_server_addr: server_addr,
2,757✔
71

2,757✔
72
            connection_opts: conn_opts,
2,757✔
73
        }
2,757✔
74
    }
2,757✔
75

76
    pub fn set_server_handle(&mut self, h: usize, addr: SocketAddr) {
2,757✔
77
        self.http_server_handle = h;
2,757✔
78
        self.http_server_addr = addr;
2,757✔
79
    }
2,757✔
80

81
    /// Is there a HTTP conversation open to this data_url that is not in progress?
82
    #[cfg_attr(test, mutants::skip)]
83
    pub fn find_free_conversation(&self, data_url: &UrlString) -> Option<usize> {
26,833✔
84
        for (event_id, convo) in self.peers.iter() {
60,521✔
85
            if let Some(url) = convo.get_url() {
60,115✔
86
                if url == data_url && !convo.is_request_inflight() {
26,695✔
87
                    return Some(*event_id);
20,387✔
88
                }
6,308✔
89
            }
33,420✔
90
        }
91
        None
6,446✔
92
    }
26,833✔
93

94
    /// Get a mut ref to a conversation
95
    #[cfg_attr(test, mutants::skip)]
96
    pub fn get_conversation(&mut self, event_id: usize) -> Option<&mut ConversationHttp> {
72,428✔
97
        self.peers.get_mut(&event_id)
72,428✔
98
    }
72,428✔
99

100
    /// Get a mut ref to a conversation and its socket
101
    pub fn get_conversation_and_socket(
69,581✔
102
        &mut self,
69,581✔
103
        event_id: usize,
69,581✔
104
    ) -> (
69,581✔
105
        Option<&mut ConversationHttp>,
69,581✔
106
        Option<&mut mio::net::TcpStream>,
69,581✔
107
    ) {
69,581✔
108
        (
69,581✔
109
            self.peers.get_mut(&event_id),
69,581✔
110
            self.sockets.get_mut(&event_id),
69,581✔
111
        )
69,581✔
112
    }
69,581✔
113

114
    /// Connect to a new remote HTTP endpoint, given the data URL and a (resolved) socket address to
115
    /// its origin.  Once connected, optionally send the given request.
116
    /// Idempotent -- will not re-connect if already connected and there is a free conversation channel open
117
    /// (will return Error::AlreadyConnected with the event ID)
118
    pub fn connect_http(
26,833✔
119
        &mut self,
26,833✔
120
        network_state: &mut NetworkState,
26,833✔
121
        network: &PeerNetwork,
26,833✔
122
        data_url: UrlString,
26,833✔
123
        addr: SocketAddr,
26,833✔
124
        request: Option<StacksHttpRequest>,
26,833✔
125
    ) -> Result<usize, net_error> {
26,833✔
126
        if let Some(event_id) = self.find_free_conversation(&data_url) {
26,833✔
127
            let http_nk = NeighborKey {
20,387✔
128
                peer_version: network.burnchain.peer_version,
20,387✔
129
                network_id: network.local_peer.network_id,
20,387✔
130
                addrbytes: PeerAddress::from_socketaddr(&addr),
20,387✔
131
                port: addr.port(),
20,387✔
132
            };
20,387✔
133
            return Err(net_error::AlreadyConnected(event_id, http_nk));
20,387✔
134
        }
6,446✔
135

136
        let sock = NetworkState::connect(
6,446✔
137
            &addr,
6,446✔
138
            network.connection_opts.socket_send_buffer_size,
6,446✔
139
            network.connection_opts.socket_recv_buffer_size,
6,446✔
140
        )?;
×
141
        let hint_event_id = network_state.next_event_id()?;
6,446✔
142
        let next_event_id =
6,446✔
143
            network_state.register(self.http_server_handle, hint_event_id, &sock)?;
6,446✔
144

145
        self.connecting.insert(
6,446✔
146
            next_event_id,
6,446✔
147
            (sock, Some(data_url), request, get_epoch_time_secs()),
6,446✔
148
        );
149
        Ok(next_event_id)
6,446✔
150
    }
26,833✔
151

152
    /// How many conversations are connected from this IP address?
153
    fn count_inbound_ip_addrs(&self, peer_addr: &SocketAddr) -> u64 {
1,059,036✔
154
        let mut count = 0;
1,059,036✔
155
        for (_, convo) in self.peers.iter() {
6,613,024✔
156
            if convo.get_url().is_none() && convo.get_peer_addr().ip() == peer_addr.ip() {
6,539,870✔
157
                count += 1;
6,256,430✔
158
            }
6,257,096✔
159
        }
160
        count
1,059,036✔
161
    }
1,059,036✔
162

163
    /// Can we register this socket?
164
    #[cfg_attr(test, mutants::skip)]
165
    fn can_register_http(
1,059,045✔
166
        &self,
1,059,045✔
167
        peer_addr: &SocketAddr,
1,059,045✔
168
        outbound_url: Option<&UrlString>,
1,059,045✔
169
    ) -> Result<(), net_error> {
1,059,045✔
170
        if outbound_url.is_none()
1,059,045✔
171
            && (self.peers.len() as u64) + 1 > self.connection_opts.max_http_clients
1,052,609✔
172
        {
173
            // inbound
174
            debug!(
9✔
175
                "HTTP: too many inbound peers total (max is {})",
176
                self.connection_opts.max_http_clients
177
            );
178
            return Err(net_error::TooManyPeers);
9✔
179
        }
1,059,036✔
180

181
        // how many other conversations are connected?
182
        let num_inbound = self.count_inbound_ip_addrs(peer_addr);
1,059,036✔
183
        if num_inbound > self.connection_opts.max_http_clients {
1,059,036✔
184
            // too many
185
            debug!(
×
186
                "HTTP: too many inbound HTTP peers from {:?} ({} > {})",
187
                peer_addr, num_inbound, self.connection_opts.max_http_clients
188
            );
189
            return Err(net_error::TooManyPeers);
×
190
        }
1,059,036✔
191

192
        debug!(
1,059,036✔
193
            "HTTP: Have {} peers now (max {}) inbound={}, including {} from host of {:?}",
194
            self.peers.len(),
×
195
            self.connection_opts.max_http_clients,
196
            outbound_url.is_none(),
×
197
            num_inbound,
198
            peer_addr
199
        );
200
        Ok(())
1,059,036✔
201
    }
1,059,045✔
202

203
    /// Low-level method to register a socket/event pair on the p2p network interface.
204
    /// Call only once the socket is connected (called once the socket triggers ready).
205
    /// Will destroy the socket if we can't register for whatever reason.
206
    #[cfg_attr(test, mutants::skip)]
207
    fn register_http(
1,059,054✔
208
        &mut self,
1,059,054✔
209
        network_state: &mut NetworkState,
1,059,054✔
210
        node_state: &mut StacksNodeState,
1,059,054✔
211
        event_id: usize,
1,059,054✔
212
        mut socket: mio_net::TcpStream,
1,059,054✔
213
        outbound_url: Option<UrlString>,
1,059,054✔
214
        initial_request: Option<StacksHttpRequest>,
1,059,054✔
215
    ) -> Result<(), net_error> {
1,059,054✔
216
        let send_buffer_size = node_state
1,059,054✔
217
            .with_node_state(|network, _, _, _, _| network.connection_opts.socket_send_buffer_size);
1,059,054✔
218

219
        let client_addr = match socket.peer_addr() {
1,059,054✔
220
            Ok(addr) => addr,
1,059,045✔
221
            Err(e) => {
9✔
222
                warn!("Failed to get peer address of {:?}: {:?}", &socket, &e);
9✔
223
                let _ = network_state.deregister(event_id, &socket);
9✔
224
                return Err(net_error::SocketError);
9✔
225
            }
226
        };
227

228
        match self.can_register_http(&client_addr, outbound_url.as_ref()) {
1,059,045✔
229
            Ok(_) => {}
1,059,036✔
230
            Err(e) => {
9✔
231
                let _ = network_state.deregister(event_id, &socket);
9✔
232
                return Err(e);
9✔
233
            }
234
        }
235

236
        let peer_host = match outbound_url {
1,059,036✔
237
            Some(ref url_str) => {
6,436✔
238
                PeerHost::try_from_url(url_str).unwrap_or(PeerHost::from_socketaddr(&client_addr))
6,436✔
239
            }
240
            None => PeerHost::from_socketaddr(&client_addr),
1,052,600✔
241
        };
242

243
        let mut new_convo = ConversationHttp::new(
1,059,036✔
244
            client_addr,
1,059,036✔
245
            outbound_url.clone(),
1,059,036✔
246
            peer_host,
1,059,036✔
247
            &self.connection_opts,
1,059,036✔
248
            event_id,
1,059,036✔
249
            send_buffer_size,
1,059,036✔
250
        );
251

252
        debug!(
1,059,036✔
253
            "Registered HTTP {:?} as event {} (outbound={:?})",
254
            &socket, event_id, &outbound_url
×
255
        );
256

257
        if let Some(request) = initial_request {
1,059,036✔
258
            test_debug!("Sending initial HTTP request to {:?}", &socket);
6,139✔
259
            match new_convo.send_request(request) {
6,139✔
260
                Ok(_) => {}
6,139✔
261
                Err(e) => {
×
262
                    let _ = network_state.deregister(event_id, &socket);
×
263
                    return Err(e);
×
264
                }
265
            }
266

267
            // prime the socket
268
            if let Err(e) = HttpPeer::saturate_http_socket(&mut socket, &mut new_convo) {
6,139✔
269
                let _ = network_state.deregister(event_id, &socket);
54✔
270
                return Err(e);
54✔
271
            }
6,085✔
272
        }
1,052,897✔
273

274
        self.sockets.insert(event_id, socket);
1,058,982✔
275
        self.peers.insert(event_id, new_convo);
1,058,982✔
276
        Ok(())
1,058,982✔
277
    }
1,059,054✔
278

279
    /// Deregister a socket/event pair
280
    #[cfg_attr(test, mutants::skip)]
281
    pub fn deregister_http(&mut self, network_state: &mut NetworkState, event_id: usize) {
1,049,297✔
282
        test_debug!("Remove HTTP event {}", event_id);
1,049,297✔
283
        self.peers.remove(&event_id);
1,049,297✔
284

285
        match self.sockets.remove(&event_id) {
1,049,297✔
286
            None => {}
27✔
287
            Some(sock) => {
1,049,270✔
288
                let _ = network_state.deregister(event_id, &sock);
1,049,270✔
289
            }
1,049,270✔
290
        }
291
        match self.connecting.remove(&event_id) {
1,049,297✔
292
            None => {}
1,049,297✔
293
            Some((sock, ..)) => {
×
294
                let _ = network_state.deregister(event_id, &sock);
×
295
            }
×
296
        }
297
    }
1,049,297✔
298

299
    /// Remove slow/unresponsive peers
300
    fn disconnect_unresponsive(&mut self, network_state: &mut NetworkState) {
10,820,549✔
301
        let now = get_epoch_time_secs();
10,820,549✔
302
        let mut to_remove = vec![];
10,820,549✔
303
        for (event_id, (socket, data_url_opt, _, ts)) in self.connecting.iter() {
10,820,549✔
304
            if ts + self.connection_opts.connect_timeout < now {
×
305
                debug!(
×
306
                    "Disconnect unresponsive connecting HTTP peer {socket:?} event {event_id} to {data_url_opt:?}"
307
                );
308
                to_remove.push(*event_id);
×
309
            }
×
310
        }
311

312
        for (event_id, convo) in self.peers.iter() {
45,129,165✔
313
            let mut last_request_time = convo.get_last_request_time();
43,540,704✔
314
            if last_request_time == 0 {
43,540,704✔
315
                // never got a request
1,084,780✔
316
                last_request_time = convo.get_connection_time();
1,084,780✔
317
            }
42,497,378✔
318

319
            let mut last_response_time = convo.get_last_response_time();
43,540,704✔
320
            if last_response_time == 0 {
43,540,704✔
321
                // never sent a response
1,054,345✔
322
                last_response_time = convo.get_connection_time();
1,054,345✔
323
            }
42,527,957✔
324

325
            if last_request_time + self.connection_opts.timeout < now
43,540,704✔
326
                && last_response_time + self.connection_opts.idle_timeout < now
19,781✔
327
            {
328
                // it's been too long
329
                debug!("Removing idle HTTP conversation {convo:?} event {event_id}");
19,320✔
330
                to_remove.push(*event_id);
19,320✔
331
            }
43,521,384✔
332
        }
333

334
        for event_id in to_remove.into_iter() {
10,820,549✔
335
            self.deregister_http(network_state, event_id);
19,320✔
336
        }
19,320✔
337
    }
10,820,549✔
338

339
    /// Saturate a conversation's socket -- either sends the whole request, or fills the socket
340
    /// buffer.
341
    pub fn saturate_http_socket(
6,499,301✔
342
        client_sock: &mut mio::net::TcpStream,
6,499,301✔
343
        convo: &mut ConversationHttp,
6,499,301✔
344
    ) -> Result<(), net_error> {
6,499,301✔
345
        // saturate the socket
346
        loop {
347
            let send_res = convo.send(client_sock);
12,902,530✔
348
            match send_res {
12,902,530✔
349
                Err(e) => {
×
350
                    debug!("Failed to send data to socket {:?}: {:?}", &client_sock, &e);
×
351
                    return Err(e);
×
352
                }
353
                Ok(sz) => {
12,902,530✔
354
                    if sz == 0 {
12,902,530✔
355
                        break;
6,499,301✔
356
                    }
6,403,229✔
357
                }
358
            }
359
        }
360

361
        Ok(())
6,499,301✔
362
    }
6,499,301✔
363

364
    /// Process new inbound HTTP connections we just accepted.
365
    /// Returns the event IDs of sockets we need to register
366
    fn process_new_sockets(
10,820,612✔
367
        &mut self,
10,820,612✔
368
        network_state: &mut NetworkState,
10,820,612✔
369
        node_state: &mut StacksNodeState,
10,820,612✔
370
        poll_state: &mut NetworkPollState,
10,820,612✔
371
    ) -> Vec<usize> {
10,820,612✔
372
        let mut registered = vec![];
10,820,612✔
373

374
        for (hint_event_id, client_sock) in poll_state.new.drain() {
10,820,620✔
375
            let event_id = match network_state.register(
1,052,555✔
376
                self.http_server_handle,
1,052,555✔
377
                hint_event_id,
1,052,555✔
378
                &client_sock,
1,052,555✔
379
            ) {
1,052,555✔
380
                Ok(event_id) => event_id,
1,052,555✔
381
                Err(e) => {
×
382
                    warn!(
×
383
                        "Failed to register HTTP connection {:?}: {:?}",
384
                        &client_sock, &e
×
385
                    );
386
                    continue;
×
387
                }
388
            };
389

390
            // event ID already used?
391
            if self.peers.contains_key(&event_id) {
1,052,555✔
392
                warn!(
×
393
                    "Already have an event {}: {:?}",
394
                    event_id,
395
                    self.peers.get(&event_id)
×
396
                );
397
                let _ = network_state.deregister(event_id, &client_sock);
×
398
                continue;
×
399
            }
1,052,555✔
400

401
            let sock_fmt = format!("{client_sock:?}");
1,052,555✔
402
            if let Err(e) =
9✔
403
                self.register_http(network_state, node_state, event_id, client_sock, None, None)
1,052,555✔
404
            {
405
                info!("Failed to register inbound HTTP connection ({event_id}, {sock_fmt}): {e:?}",);
9✔
406
                // NOTE: register_http will deregister the socket for us
407
                continue;
9✔
408
            }
1,052,546✔
409
            registered.push(event_id);
1,052,546✔
410
        }
411

412
        registered
10,820,612✔
413
    }
10,820,612✔
414

415
    /// Process network traffic on a HTTP conversation.
416
    /// Returns whether or not the convo is still alive, as well as any message(s) that need to be
417
    /// forwarded to the peer network.
418
    fn process_http_conversation(
6,763,039✔
419
        node_state: &mut StacksNodeState,
6,763,039✔
420
        event_id: usize,
6,763,039✔
421
        client_sock: &mut mio_net::TcpStream,
6,763,039✔
422
        convo: &mut ConversationHttp,
6,763,039✔
423
    ) -> (bool, Vec<StacksMessageType>) {
6,763,039✔
424
        // get incoming bytes and update the state of this conversation.
425
        let mut convo_dead = false;
6,763,039✔
426
        let recv_res = convo.recv(client_sock);
6,763,039✔
427
        if let Err(e) = recv_res {
6,763,039✔
428
            match e {
338,792✔
429
                net_error::PermanentlyDrained => {
430
                    // socket got closed, but we might still have pending unsolicited messages
431
                    debug!(
338,792✔
432
                        "Remote HTTP peer disconnected event {} (socket {:?})",
433
                        event_id, &client_sock
×
434
                    );
435
                    convo_dead = true;
338,792✔
436
                }
437
                net_error::InvalidMessage => {
438
                    // got sent bad data.  If this was an inbound conversation, send it a HTTP
439
                    // 400 and close the socket.
440
                    info!("Got a bad HTTP message on socket {client_sock:?}");
×
441
                    match convo.reply_error(StacksHttpResponse::new_empty_error(
×
442
                        &HttpBadRequest::new(
×
443
                            "Received an HTTP message that the node could not decode".to_string(),
×
444
                        ),
×
445
                    )) {
×
446
                        Ok(_) => {
447
                            // prime the socket
448
                            if let Err(e) = HttpPeer::saturate_http_socket(client_sock, convo) {
×
449
                                info!("Failed to flush HTTP 400 to socket {client_sock:?}: {e:?}",);
×
450
                                // convo_dead = true;
451
                            }
×
452
                        }
453
                        Err(e) => {
×
454
                            info!("Failed to reply HTTP 400 to socket {client_sock:?}: {e:?}",);
×
455
                            convo_dead = true;
×
456
                        }
457
                    }
458
                }
459
                _ => {
460
                    info!(
×
461
                        "Failed to receive HTTP data on event {event_id} (socket {client_sock:?}): {e:?}",
462
                    );
463
                    convo_dead = true;
×
464
                }
465
            }
466
        }
6,424,247✔
467

468
        // react to inbound messages -- do we need to send something out, or fulfill requests
469
        // to other threads?  Try to chat even if the recv() failed, since we'll want to at
470
        // least drain the conversation inbox.
471
        let msgs = match convo.chat(node_state) {
6,763,039✔
472
            Ok(msgs) => msgs,
6,763,039✔
473
            Err(e) => {
×
474
                info!(
×
475
                    "Failed to converse HTTP on event {event_id} (socket {client_sock:?}): {e:?}",
476
                );
477
                convo_dead = true;
×
478
                vec![]
×
479
            }
480
        };
481

482
        if !convo_dead {
6,763,039✔
483
            // (continue) sending out data in this conversation, if the conversation is still
484
            // ongoing
485
            if let Err(e) = HttpPeer::saturate_http_socket(client_sock, convo) {
6,424,247✔
486
                info!(
×
487
                    "Failed to send HTTP data to event {event_id} (socket {client_sock:?}): {e:?}",
488
                );
489
                convo_dead = true;
×
490
            }
6,424,247✔
491
        }
338,792✔
492

493
        (!convo_dead, msgs)
6,763,039✔
494
    }
6,763,039✔
495

496
    /// Is an event in the process of connecting?
497
    pub fn is_connecting(&self, event_id: usize) -> bool {
6,761✔
498
        self.connecting.contains_key(&event_id)
6,761✔
499
    }
6,761✔
500

501
    /// Process newly-connected sockets
502
    fn process_connecting_sockets(
10,820,558✔
503
        &mut self,
10,820,558✔
504
        network_state: &mut NetworkState,
10,820,558✔
505
        node_state: &mut StacksNodeState,
10,820,558✔
506
        poll_state: &mut NetworkPollState,
10,820,558✔
507
    ) {
10,820,558✔
508
        for event_id in poll_state.ready.iter() {
12,012,429✔
509
            if self.connecting.contains_key(event_id) {
6,763,048✔
510
                let (socket, data_url, initial_request_opt, _) =
6,445✔
511
                    self.connecting.remove(event_id).unwrap();
6,445✔
512

513
                debug!("HTTP event {event_id} connected ({data_url:?})");
6,445✔
514

515
                if let Err(e) = self.register_http(
6,445✔
516
                    network_state,
6,445✔
517
                    node_state,
6,445✔
518
                    *event_id,
6,445✔
519
                    socket,
6,445✔
520
                    data_url.clone(),
6,445✔
521
                    initial_request_opt,
6,445✔
522
                ) {
6,445✔
523
                    info!(
9✔
524
                        "Failed to register connecting HTTP conversation to ({event_id}, {data_url:?}): {e:?}",
525
                    );
526
                }
6,436✔
527
            }
6,756,603✔
528
        }
529
    }
10,820,558✔
530

531
    /// Process sockets that are ready, but specifically inbound or outbound only.
532
    /// Advance the state of all such conversations with remote peers.
533
    /// Return the list of events that correspond to failed conversations, as well as the list of
534
    /// peer network messages we'll need to forward
535
    #[cfg_attr(test, mutants::skip)]
536
    fn process_ready_sockets(
10,820,558✔
537
        &mut self,
10,820,558✔
538
        poll_state: &mut NetworkPollState,
10,820,558✔
539
        node_state: &mut StacksNodeState,
10,820,558✔
540
    ) -> (Vec<StacksMessageType>, Vec<usize>) {
10,820,558✔
541
        let mut to_remove = vec![];
10,820,558✔
542
        let mut msgs = vec![];
10,820,558✔
543
        for event_id in &poll_state.ready {
12,012,429✔
544
            let Some(client_sock) = self.sockets.get_mut(event_id) else {
6,763,048✔
545
                debug!("Rogue HTTP socket event {event_id}");
9✔
546
                to_remove.push(*event_id);
9✔
547
                continue;
9✔
548
            };
549

550
            match self.peers.get_mut(event_id) {
6,763,039✔
551
                Some(ref mut convo) => {
6,763,039✔
552
                    // activity on a http socket
553
                    debug!("Process HTTP data from {convo:?}");
6,763,039✔
554
                    let (alive, mut new_msgs) = HttpPeer::process_http_conversation(
6,763,039✔
555
                        node_state,
6,763,039✔
556
                        *event_id,
6,763,039✔
557
                        client_sock,
6,763,039✔
558
                        convo,
6,763,039✔
559
                    );
6,763,039✔
560
                    if !alive {
6,763,039✔
561
                        debug!("HTTP convo {convo:?} is no longer alive");
338,792✔
562
                        to_remove.push(*event_id);
338,792✔
563
                    }
6,424,247✔
564
                    msgs.append(&mut new_msgs);
6,763,039✔
565
                }
566
                None => {
567
                    debug!("Rogue HTTP event {event_id} for socket {client_sock:?}");
×
568
                    to_remove.push(*event_id);
×
569
                }
570
            }
571
        }
572

573
        (msgs, to_remove)
10,820,558✔
574
    }
10,820,558✔
575

576
    /// Flush outgoing replies, but don't block.
577
    /// Drop broken handles.
578
    /// Return the list of conversation event IDs to close (i.e. they're broken, or the request is done)
579
    #[cfg_attr(test, mutants::skip)]
580
    fn flush_conversations(&mut self) -> Vec<usize> {
10,820,549✔
581
        let mut close = vec![];
10,820,549✔
582

583
        // flush each outgoing conversation
584
        for (event_id, ref mut convo) in self.peers.iter_mut() {
45,778,372✔
585
            if let Err(e) = convo.try_flush() {
44,231,871✔
586
                info!("Broken HTTP connection {convo:?} event {event_id}: {e:?}",);
×
587
                close.push(*event_id);
×
588
            }
44,231,871✔
589
            if convo.is_drained() && !convo.is_keep_alive() {
44,231,871✔
590
                // did some work, but nothing more to do and we're not keep-alive
591
                debug!("Close drained non-keepalive HTTP connection {convo:?} event {event_id}",);
691,167✔
592
                close.push(*event_id);
691,167✔
593
            }
43,540,704✔
594
        }
595

596
        close
10,820,549✔
597
    }
10,820,549✔
598

599
    /// Update HTTP server state
600
    /// -- accept new connections
601
    /// -- send data on ready sockets
602
    /// -- receive data on ready sockets
603
    /// -- clear out timed-out requests
604
    /// Returns the list of messages to forward along to the peer network.
605
    #[cfg_attr(test, mutants::skip)]
606
    pub fn run(
10,820,612✔
607
        &mut self,
10,820,612✔
608
        network_state: &mut NetworkState,
10,820,612✔
609
        node_state: &mut StacksNodeState,
10,820,612✔
610
        mut poll_state: NetworkPollState,
10,820,612✔
611
    ) -> Vec<StacksMessageType> {
10,820,612✔
612
        // set up new inbound conversations
613
        self.process_new_sockets(network_state, node_state, &mut poll_state);
10,820,612✔
614

615
        // set up connected sockets
616
        self.process_connecting_sockets(network_state, node_state, &mut poll_state);
10,820,612✔
617

618
        // run existing conversations, clear out broken ones, and get back messages forwarded to us
619
        let (stacks_msgs, error_events) = self.process_ready_sockets(&mut poll_state, node_state);
10,820,612✔
620
        for error_event in error_events {
10,820,612✔
621
            debug!("Failed HTTP connection on event {}", error_event);
338,738✔
622
            self.deregister_http(network_state, error_event);
338,738✔
623
        }
624

625
        // move conversations along
626
        let close_events = self.flush_conversations();
10,820,612✔
627
        for close_event in close_events {
10,820,613✔
628
            debug!("Close HTTP connection on event {}", close_event);
691,104✔
629
            self.deregister_http(network_state, close_event);
691,104✔
630
        }
631

632
        // remove timed-out requests
633
        for (_, convo) in self.peers.iter_mut() {
45,129,102✔
634
            convo.clear_timeouts();
43,540,641✔
635
        }
43,540,641✔
636

637
        // clear out slow or non-responsive peers
638
        self.disconnect_unresponsive(network_state);
10,820,612✔
639

640
        stacks_msgs
10,820,612✔
641
    }
10,820,612✔
642
}
643

644
#[cfg(test)]
645
mod test {
646
    use std::cell::RefCell;
647
    use std::net::{SocketAddr, TcpStream};
648
    use std::sync::mpsc::sync_channel;
649
    use std::thread;
650

651
    use clarity::types::StacksEpochId;
652
    use stacks_common::codec::MAX_MESSAGE_LEN;
653
    use stacks_common::util::sleep_ms;
654

655
    use super::*;
656
    use crate::chainstate::burn::ConsensusHash;
657
    use crate::chainstate::stacks::db::blocks::test::*;
658
    use crate::chainstate::stacks::db::StacksChainState;
659
    use crate::chainstate::stacks::test::*;
660
    use crate::chainstate::stacks::{StacksBlockHeader, *};
661
    use crate::net::test::*;
662

663
    fn test_http_server<F, C>(
4✔
664
        test_name: &str,
4✔
665
        peer_p2p: u16,
4✔
666
        peer_http: u16,
4✔
667
        conn_opts: ConnectionOptions,
4✔
668
        num_clients: usize,
4✔
669
        client_sleep: u64,
4✔
670
        mut make_request: F,
4✔
671
        check_result: C,
4✔
672
    ) -> usize
4✔
673
    where
4✔
674
        F: FnMut(usize, &mut StacksChainState) -> Vec<u8>,
4✔
675
        C: Fn(usize, Result<Vec<u8>, net_error>) -> bool,
4✔
676
    {
677
        let mut peer_config = TestPeerConfig::new(test_name, peer_p2p, peer_http);
4✔
678
        peer_config.connection_opts = conn_opts;
4✔
679

680
        let mut peer = TestPeer::new(peer_config);
4✔
681
        let view = peer.get_burnchain_view().unwrap();
4✔
682
        let (http_sx, http_rx) = sync_channel(1);
4✔
683

684
        let network_id = peer.config.chain_config.network_id;
4✔
685
        let chainstate_path = peer.chain.chainstate_path.clone();
4✔
686

687
        let (num_events_sx, num_events_rx) = sync_channel(1);
4✔
688
        let http_thread = thread::spawn(move || {
4✔
689
            let view = peer.get_burnchain_view().unwrap();
4✔
690
            loop {
691
                test_debug!("http wakeup");
320✔
692

693
                peer.step().unwrap();
320✔
694

695
                // asked to yield?
696
                if http_rx.try_recv().is_ok() {
320✔
697
                    break;
4✔
698
                }
316✔
699
            }
700

701
            test_debug!("http server joined");
4✔
702
            let num_events = peer.network.network.as_ref().unwrap().num_events();
4✔
703
            let _ = num_events_sx.send(num_events);
4✔
704
        });
4✔
705

706
        let mut client_requests = vec![];
4✔
707
        let mut client_threads = vec![];
4✔
708
        let mut client_handles = vec![];
4✔
709
        let (mut chainstate, _) =
4✔
710
            StacksChainState::open(false, network_id, &chainstate_path, None).unwrap();
4✔
711
        for i in 0..num_clients {
31✔
712
            let request = make_request(i, &mut chainstate);
31✔
713
            client_requests.push(request);
31✔
714
        }
31✔
715

716
        for (i, request) in client_requests.into_iter().enumerate() {
31✔
717
            let (client_sx, client_rx) = sync_channel(1);
31✔
718
            let client = thread::spawn(move || {
31✔
719
                let mut sock = TcpStream::connect(
31✔
720
                    &format!("127.0.0.1:{}", peer_http)
31✔
721
                        .parse::<SocketAddr>()
31✔
722
                        .unwrap(),
31✔
723
                )
724
                .unwrap();
31✔
725

726
                if client_sleep > 0 {
31✔
727
                    sleep_ms(client_sleep * 1000);
1✔
728
                }
31✔
729

730
                match sock.write_all(&request) {
31✔
731
                    Ok(_) => {}
31✔
732
                    Err(e) => {
×
733
                        test_debug!("Client {} failed to write: {:?}", i, &e);
×
734
                        client_sx.send(Err(net_error::WriteError(e))).unwrap();
×
735
                        return;
×
736
                    }
737
                }
738

739
                let mut resp = vec![];
31✔
740
                match sock.read_to_end(&mut resp) {
31✔
741
                    Ok(_) => {
742
                        if resp.is_empty() {
31✔
743
                            test_debug!("Client {} did not receive any data", i);
10✔
744
                            client_sx.send(Err(net_error::PermanentlyDrained)).unwrap();
10✔
745
                            return;
10✔
746
                        }
21✔
747
                    }
748
                    Err(e) => {
×
749
                        test_debug!("Client {} failed to read: {:?}", i, &e);
×
750
                        client_sx.send(Err(net_error::ReadError(e))).unwrap();
×
751
                        return;
×
752
                    }
753
                }
754

755
                test_debug!("Client {} received {} bytes", i, resp.len());
21✔
756
                client_sx.send(Ok(resp)).unwrap();
21✔
757
            });
31✔
758
            client_threads.push(client);
31✔
759
            client_handles.push(client_rx);
31✔
760
        }
761

762
        for (i, client_thread) in client_threads.into_iter().enumerate() {
31✔
763
            test_debug!("Client join {}", i);
31✔
764
            client_thread.join().unwrap();
31✔
765
            let resp = client_handles[i].recv().unwrap();
31✔
766
            assert!(check_result(i, resp));
31✔
767
        }
768

769
        http_sx.send(true).unwrap();
4✔
770
        let num_events = num_events_rx.recv().unwrap();
4✔
771
        http_thread.join().unwrap();
4✔
772
        num_events
4✔
773
    }
4✔
774

775
    #[test]
776
    fn test_http_getinfo() {
×
777
        test_http_server(
×
778
            function_name!(),
×
779
            51000,
780
            51001,
781
            ConnectionOptions::default(),
×
782
            1,
783
            0,
784
            |client_id, _| {
×
785
                let mut request = StacksHttpRequest::new_for_peer(
×
786
                    PeerHost::from_host_port("127.0.0.1".to_string(), 51001),
×
787
                    "GET".to_string(),
×
788
                    "/v2/info".to_string(),
×
789
                    HttpRequestContents::new(),
×
790
                )
791
                .unwrap();
×
792
                request.preamble_mut().keep_alive = false;
×
793

794
                let request_bytes = request.try_serialize().unwrap();
×
795
                request_bytes
×
796
            },
×
797
            |client_id, http_response_bytes_res| {
×
798
                // should be a PeerInfo
799
                let http_response_bytes = http_response_bytes_res.unwrap();
×
800
                let response =
×
801
                    StacksHttp::parse_response("GET", "/v2/info", &http_response_bytes).unwrap();
×
802
                true
×
803
            },
×
804
        );
805
    }
×
806

807
    #[test]
808
    #[ignore]
809
    fn test_http_10_threads_getinfo() {
1✔
810
        test_http_server(
1✔
811
            function_name!(),
1✔
812
            51010,
813
            51011,
814
            ConnectionOptions::default(),
1✔
815
            10,
816
            0,
817
            |client_id, _| {
10✔
818
                let mut request = StacksHttpRequest::new_for_peer(
10✔
819
                    PeerHost::from_host_port("127.0.0.1".to_string(), 51011),
10✔
820
                    "GET".to_string(),
10✔
821
                    "/v2/info".to_string(),
10✔
822
                    HttpRequestContents::new(),
10✔
823
                )
824
                .unwrap();
10✔
825
                request.preamble_mut().keep_alive = false;
10✔
826

827
                let request_bytes = request.try_serialize().unwrap();
10✔
828
                request_bytes
10✔
829
            },
10✔
830
            |client_id, http_response_bytes_res| {
10✔
831
                // should be a PeerInfo
832
                let http_response_bytes = http_response_bytes_res.unwrap();
10✔
833
                let response =
10✔
834
                    StacksHttp::parse_response("GET", "/v2/info", &http_response_bytes).unwrap();
10✔
835
                true
10✔
836
            },
10✔
837
        );
838
    }
1✔
839

840
    #[test]
841
    fn test_http_getblock() {
×
842
        test_http_server(
×
843
            function_name!(),
×
844
            51020,
845
            51021,
846
            ConnectionOptions::default(),
×
847
            1,
848
            0,
849
            |client_id, ref mut chainstate| {
×
850
                let peer_server_block = make_codec_test_block(25, StacksEpochId::Epoch25);
×
851
                let peer_server_consensus_hash = ConsensusHash([(client_id + 1) as u8; 20]);
×
852
                let index_block_hash = StacksBlockHeader::make_index_block_hash(
×
853
                    &peer_server_consensus_hash,
×
854
                    &peer_server_block.block_hash(),
×
855
                );
856

857
                test_debug!("Store peer server index block {:?}", &index_block_hash);
×
858
                store_staging_block(
×
859
                    chainstate,
×
860
                    &peer_server_consensus_hash,
×
861
                    &peer_server_block,
×
862
                    &ConsensusHash([client_id as u8; 20]),
×
863
                    456,
864
                    123,
865
                );
866

867
                let mut request = StacksHttpRequest::new_for_peer(
×
868
                    PeerHost::from_host_port("127.0.0.1".to_string(), 51021),
×
869
                    "GET".to_string(),
×
870
                    format!("/v2/blocks/{}", &index_block_hash),
×
871
                    HttpRequestContents::new(),
×
872
                )
873
                .unwrap();
×
874
                request.preamble_mut().keep_alive = false;
×
875

876
                let request_bytes = request.try_serialize().unwrap();
×
877
                request_bytes
×
878
            },
×
879
            |client_id, http_response_bytes_res| {
×
880
                // should be a Block
881
                let http_response_bytes = http_response_bytes_res.unwrap();
×
882

883
                let peer_server_block = make_codec_test_block(25, StacksEpochId::Epoch25);
×
884
                let peer_server_consensus_hash = ConsensusHash([(client_id + 1) as u8; 20]);
×
885
                let index_block_hash = StacksBlockHeader::make_index_block_hash(
×
886
                    &peer_server_consensus_hash,
×
887
                    &peer_server_block.block_hash(),
×
888
                );
889

890
                let request_path = format!("/v2/blocks/{}", &index_block_hash);
×
891
                let response =
×
892
                    StacksHttp::parse_response("GET", &request_path, &http_response_bytes).unwrap();
×
893
                match response {
×
894
                    StacksHttpMessage::Response(stacks_http_response) => {
×
895
                        if let Ok(block) = StacksHttpResponse::decode_block(stacks_http_response) {
×
896
                            block == peer_server_block
×
897
                        } else {
898
                            false
×
899
                        }
900
                    }
901
                    _ => false,
×
902
                }
903
            },
×
904
        );
905
    }
×
906

907
    #[test]
908
    #[ignore]
909
    fn test_http_10_threads_getblock() {
1✔
910
        test_http_server(
1✔
911
            function_name!(),
1✔
912
            51030,
913
            51031,
914
            ConnectionOptions::default(),
1✔
915
            10,
916
            0,
917
            |client_id, ref mut chainstate| {
10✔
918
                let peer_server_block = make_codec_test_block(25, StacksEpochId::latest());
10✔
919
                let peer_server_consensus_hash = ConsensusHash([(client_id + 1) as u8; 20]);
10✔
920
                let index_block_hash = StacksBlockHeader::make_index_block_hash(
10✔
921
                    &peer_server_consensus_hash,
10✔
922
                    &peer_server_block.block_hash(),
10✔
923
                );
924

925
                test_debug!("Store peer server index block {:?}", &index_block_hash);
10✔
926
                store_staging_block(
10✔
927
                    chainstate,
10✔
928
                    &peer_server_consensus_hash,
10✔
929
                    &peer_server_block,
10✔
930
                    &ConsensusHash([client_id as u8; 20]),
10✔
931
                    456,
932
                    123,
933
                );
934

935
                let mut request = StacksHttpRequest::new_for_peer(
10✔
936
                    PeerHost::from_host_port("127.0.0.1".to_string(), 51031),
10✔
937
                    "GET".to_string(),
10✔
938
                    format!("/v2/blocks/{}", &index_block_hash),
10✔
939
                    HttpRequestContents::new(),
10✔
940
                )
941
                .unwrap();
10✔
942
                request.preamble_mut().keep_alive = false;
10✔
943

944
                let request_bytes = request.try_serialize().unwrap();
10✔
945
                request_bytes
10✔
946
            },
10✔
947
            |client_id, http_response_bytes_res| {
10✔
948
                // should be a Block
949
                let http_response_bytes = http_response_bytes_res.unwrap();
10✔
950

951
                let peer_server_block = make_codec_test_block(25, StacksEpochId::latest());
10✔
952
                let peer_server_consensus_hash = ConsensusHash([(client_id + 1) as u8; 20]);
10✔
953
                let index_block_hash = StacksBlockHeader::make_index_block_hash(
10✔
954
                    &peer_server_consensus_hash,
10✔
955
                    &peer_server_block.block_hash(),
10✔
956
                );
957

958
                let request_path = format!("/v2/blocks/{}", &index_block_hash);
10✔
959
                let response =
10✔
960
                    StacksHttp::parse_response("GET", &request_path, &http_response_bytes).unwrap();
10✔
961
                match response {
10✔
962
                    StacksHttpMessage::Response(stacks_http_response) => {
10✔
963
                        if let Ok(block) = StacksHttpResponse::decode_block(stacks_http_response) {
10✔
964
                            block == peer_server_block
10✔
965
                        } else {
966
                            false
×
967
                        }
968
                    }
969
                    _ => false,
×
970
                }
971
            },
10✔
972
        );
973
    }
1✔
974

975
    #[test]
976
    #[ignore]
977
    fn test_http_too_many_clients() {
1✔
978
        let mut conn_opts = ConnectionOptions::default();
1✔
979
        conn_opts.num_clients = 1;
1✔
980
        conn_opts.max_http_clients = 1;
1✔
981

982
        let have_success = RefCell::new(false);
1✔
983
        let have_error = RefCell::new(false);
1✔
984

985
        test_http_server(
1✔
986
            function_name!(),
1✔
987
            51040,
988
            51041,
989
            conn_opts,
1✔
990
            10,
991
            0,
992
            |client_id, _| {
10✔
993
                let mut request = StacksHttpRequest::new_for_peer(
10✔
994
                    PeerHost::from_host_port("127.0.0.1".to_string(), 51041),
10✔
995
                    "GET".to_string(),
10✔
996
                    "/v2/info".to_string(),
10✔
997
                    HttpRequestContents::new(),
10✔
998
                )
999
                .unwrap();
10✔
1000
                request.preamble_mut().keep_alive = false;
10✔
1001

1002
                let request_bytes = request.try_serialize().unwrap();
10✔
1003
                request_bytes
10✔
1004
            },
10✔
1005
            |client_id, http_response_bytes_res| {
10✔
1006
                match http_response_bytes_res {
10✔
1007
                    Ok(http_response_bytes) => {
1✔
1008
                        // should be a PeerInfo
1009
                        let response = StacksHttp::parse_response(
1✔
1010
                            "GET",
1✔
1011
                            "/v2/info",
1✔
1012
                            &http_response_bytes,
1✔
1013
                        ).unwrap_or_else(|e| panic!("Failed to parse /v2/info response from:\n{http_response_bytes:?}\n{e:?}"));
1✔
1014
                        *have_success.borrow_mut() = true;
1✔
1015
                        true
1✔
1016
                    }
1017
                    Err(err) => {
9✔
1018
                        // should have failed
1019
                        eprintln!("{:?}", &err);
9✔
1020
                        *have_error.borrow_mut() = true;
9✔
1021
                        true
9✔
1022
                    }
1023
                }
1024
            },
10✔
1025
        );
1026

1027
        assert!(*have_success.borrow());
1✔
1028
        assert!(*have_error.borrow());
1✔
1029
    }
1✔
1030

1031
    #[test]
1032
    #[ignore]
1033
    fn test_http_slow_client() {
1✔
1034
        let mut conn_opts = ConnectionOptions::default();
1✔
1035
        conn_opts.timeout = 3; // kill a connection after 3 seconds of idling
1✔
1036

1037
        test_http_server(
1✔
1038
            function_name!(),
1✔
1039
            51050,
1040
            51051,
1041
            conn_opts,
1✔
1042
            1,
1043
            30,
1044
            |client_id, _| {
1✔
1045
                let mut request = StacksHttpRequest::new_for_peer(
1✔
1046
                    PeerHost::from_host_port("127.0.0.1".to_string(), 51051),
1✔
1047
                    "GET".to_string(),
1✔
1048
                    "/v2/info".to_string(),
1✔
1049
                    HttpRequestContents::new(),
1✔
1050
                )
1051
                .unwrap();
1✔
1052
                request.preamble_mut().keep_alive = false;
1✔
1053

1054
                let request_bytes = request.try_serialize().unwrap();
1✔
1055
                request_bytes
1✔
1056
            },
1✔
1057
            |client_id, http_response_bytes_res| {
1✔
1058
                match http_response_bytes_res {
1✔
1059
                    Ok(bytes) => bytes.is_empty(), // should not have gotten any data
×
1060
                    Err(net_error::PermanentlyDrained) => true,
1✔
1061
                    Err(err) => {
×
1062
                        // should have failed
1063
                        eprintln!("{:?}", &err);
×
1064
                        false
×
1065
                    }
1066
                }
1067
            },
1✔
1068
        );
1069
    }
1✔
1070

1071
    #[test]
1072
    fn test_http_endless_data_client() {
×
1073
        let conn_opts = ConnectionOptions::default();
×
1074
        test_http_server(
×
1075
            function_name!(),
×
1076
            51060,
1077
            51061,
1078
            conn_opts,
×
1079
            1,
1080
            0,
1081
            |client_id, ref mut chainstate| {
×
1082
                // make a gigantic transaction
1083
                let mut big_contract_parts = vec![];
×
1084
                let mut total_len = 0;
×
1085
                while total_len < MAX_MESSAGE_LEN {
×
1086
                    let next_line = format!(
×
1087
                        "(define-constant meaningless-data-{} {})\n",
×
1088
                        total_len, total_len
×
1089
                    );
×
1090
                    total_len += next_line.len() as u32;
×
1091
                    big_contract_parts.push(next_line);
×
1092
                }
×
1093

1094
                let big_contract = big_contract_parts.join("");
×
1095

1096
                let privk_origin = StacksPrivateKey::from_hex(
×
1097
                    "027682d2f7b05c3801fe4467883ab4cff0568b5e36412b5289e83ea5b519de8a01",
×
1098
                )
1099
                .unwrap();
×
1100
                let auth_origin = TransactionAuth::from_p2pkh(&privk_origin).unwrap();
×
1101
                let mut tx_contract = StacksTransaction::new(
×
1102
                    TransactionVersion::Testnet,
×
1103
                    auth_origin,
×
1104
                    TransactionPayload::new_smart_contract("hello-world", &big_contract, None)
×
1105
                        .unwrap(),
×
1106
                );
1107

1108
                tx_contract.chain_id = chainstate.config().chain_id;
×
1109
                tx_contract.set_tx_fee(0);
×
1110

1111
                let mut signer = StacksTransactionSigner::new(&tx_contract);
×
1112
                signer.sign_origin(&privk_origin).unwrap();
×
1113

1114
                let signed_contract_tx = signer.get_tx().unwrap();
×
1115

1116
                let mut request = StacksHttpRequest::new_for_peer(
×
1117
                    PeerHost::from_host_port("127.0.0.1".to_string(), 51061),
×
1118
                    "POST".to_string(),
×
1119
                    "/v2/transactions".to_string(),
×
1120
                    HttpRequestContents::new().payload_stacks(&signed_contract_tx),
×
1121
                )
1122
                .unwrap();
×
1123
                request.preamble_mut().keep_alive = false;
×
1124

1125
                let request_bytes = request.try_serialize().unwrap();
×
1126
                request_bytes
×
1127
            },
×
1128
            |client_id, http_response_bytes_res| {
×
1129
                match http_response_bytes_res {
×
1130
                    Ok(bytes) => false,
×
1131
                    Err(err) => {
×
1132
                        // should have failed
1133
                        eprintln!("{:?}", &err);
×
1134
                        true
×
1135
                    }
1136
                }
1137
            },
×
1138
        );
1139
    }
×
1140

1141
    #[test]
1142
    fn test_http_400() {
×
1143
        test_http_server(
×
1144
            function_name!(),
×
1145
            51070,
1146
            51071,
1147
            ConnectionOptions::default(),
×
1148
            1,
1149
            0,
1150
            |client_id, _| {
×
1151
                // live example -- should fail because we don't support `Connection:
1152
                // upgrade`
1153
                let request_txt = "GET /favicon.ico HTTP/1.1\r\nConnection: upgrade\r\nHost: crashy-stacky.zone117x.com\r\nX-Real-IP: 213.127.17.55\r\nX-Forwarded-For: 213.127.17.55\r\nX-Forwarded-Proto: http\r\nX-Forwarded-Host: crashy-stacky.zone117x.com\r\nX-Forwarded-Port: 9001\r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.113 Safari/537.36\r\nAccept: image/webp,image/apng,image/*,*/*;q=0.8\r\nReferer: http://crashy-stacky.zone117x.com:9001/v2/info\r\nAccept-Encoding: gzip, deflate\r\nAccept-Language: en-US,en;q=0.9\r\n\r\n";
×
1154
                request_txt.as_bytes().to_vec()
×
1155
            },
×
1156
            |client_id, http_response_bytes_res| {
×
1157
                // should be a HTTP 400 error
1158
                eprintln!("{:?}", &http_response_bytes_res);
×
1159
                let http_response_bytes = http_response_bytes_res.unwrap();
×
1160
                let http_response_str = String::from_utf8(http_response_bytes).unwrap();
×
1161
                eprintln!("HTTP response\n{}", http_response_str);
×
1162
                assert!(http_response_str.find("400 Bad Request").is_some());
×
1163
                true
×
1164
            },
×
1165
        );
1166
    }
×
1167

1168
    #[test]
1169
    fn test_http_404() {
×
1170
        test_http_server(
×
1171
            function_name!(),
×
1172
            51072,
1173
            51073,
1174
            ConnectionOptions::default(),
×
1175
            1,
1176
            0,
1177
            |client_id, _| {
×
1178
                // live example -- should fail because /favicon.ico doesn't exist.
1179
                let request_txt = "GET /favicon.ico HTTP/1.1\r\nConnection: close\r\nHost: 127.0.0.1:20443\r\nuser-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36\r\nreferer: http://127.0.0.1:20443/v2/info\r\naccept: image/webp,image/apng,image/*,*/*;q=0.8\r\nsec-fetch-dest: empty\r\naccept-encoding: gzip, deflate, br\r\nsec-fetch-site: same-origin\r\naccept-language: en-US,en;q=0.9\r\ndnt: 1\r\nsec-fetch-mode: no-cors\r\n\r\n";
×
1180
                request_txt.as_bytes().to_vec()
×
1181
            },
×
1182
            |client_id, http_response_bytes_res| {
×
1183
                // should be a HTTP 404 error
1184
                eprintln!("{:?}", &http_response_bytes_res);
×
1185
                let http_response_bytes = http_response_bytes_res.unwrap();
×
1186
                let http_response_str = String::from_utf8(http_response_bytes).unwrap();
×
1187
                eprintln!("HTTP response\n{}", http_response_str);
×
1188
                assert!(http_response_str.find("404 Not Found").is_some());
×
1189
                true
×
1190
            },
×
1191
        );
1192
    }
×
1193

1194
    #[test]
1195
    fn test_http_no_connecting_event_id_leak() {
×
1196
        let mut conn_opts = ConnectionOptions::default();
×
1197
        conn_opts.timeout = 10;
×
1198
        conn_opts.connect_timeout = 10;
×
1199

1200
        let num_events = test_http_server(
×
1201
            function_name!(),
×
1202
            51082,
1203
            51083,
1204
            conn_opts,
×
1205
            1,
1206
            0,
1207
            |client_id, _| {
×
1208
                // open a socket and just sit there
1209
                use std::net::TcpStream;
1210
                let sock = TcpStream::connect("127.0.0.1:51083");
×
1211

1212
                sleep_ms(15_000);
×
1213

1214
                // send a different request
1215
                let mut request = StacksHttpRequest::new_for_peer(
×
1216
                    PeerHost::from_host_port("127.0.0.1".to_string(), 51083),
×
1217
                    "GET".to_string(),
×
1218
                    "/v2/info".to_string(),
×
1219
                    HttpRequestContents::new(),
×
1220
                )
1221
                .unwrap();
×
1222
                request.preamble_mut().keep_alive = false;
×
1223

1224
                let request_bytes = request.try_serialize().unwrap();
×
1225
                request_bytes
×
1226
            },
×
1227
            |client_id, res| true,
1228
        );
1229

1230
        assert_eq!(num_events, 2);
×
1231
    }
×
1232

1233
    #[test]
1234
    fn test_http_noop() {
×
1235
        if std::env::var("BLOCKSTACK_HTTP_TEST") != Ok("1".to_string()) {
×
1236
            eprintln!("Set BLOCKSTACK_HTTP_TEST=1 to use this test.");
×
1237
            eprintln!("To test, run `curl http://localhost:51081/v2/blocks/a3b82874a8bf02b91613f61bff41580dab439ecc14f5e71c7288d89623499dfa` to download a block");
×
1238
            return;
×
1239
        }
×
1240

1241
        // doesn't do anything; just runs a server for 10 minutes
1242
        let conn_opts = ConnectionOptions::default();
×
1243
        test_http_server(
×
1244
            function_name!(),
×
1245
            51080,
1246
            51081,
1247
            conn_opts,
×
1248
            1,
1249
            600,
1250
            |client_id, ref mut chainstate| {
×
1251
                let peer_server_block = make_codec_test_block(25, StacksEpochId::latest());
×
1252
                let peer_server_consensus_hash = ConsensusHash([(client_id + 1) as u8; 20]);
×
1253
                let index_block_hash = StacksBlockHeader::make_index_block_hash(
×
1254
                    &peer_server_consensus_hash,
×
1255
                    &peer_server_block.block_hash(),
×
1256
                );
1257

1258
                test_debug!("Store peer server index block {:?}", &index_block_hash);
×
1259
                store_staging_block(
×
1260
                    chainstate,
×
1261
                    &peer_server_consensus_hash,
×
1262
                    &peer_server_block,
×
1263
                    &ConsensusHash([client_id as u8; 20]),
×
1264
                    456,
1265
                    123,
1266
                );
1267

1268
                let mut request = StacksHttpRequest::new_for_peer(
×
1269
                    PeerHost::from_host_port("127.0.0.1".to_string(), 51071),
×
1270
                    "GET".to_string(),
×
1271
                    format!("/v2/blocks/{}", index_block_hash),
×
1272
                    HttpRequestContents::new(),
×
1273
                )
1274
                .unwrap();
×
1275
                request.preamble_mut().keep_alive = false;
×
1276

1277
                let request_bytes = request.try_serialize().unwrap();
×
1278
                request_bytes
×
1279
            },
×
1280
            |client_id, http_response_bytes_res| true,
1281
        );
1282
    }
×
1283
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc