• 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

38.24
/stackslib/src/net/http/mod.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
pub mod common;
18
pub mod error;
19
pub mod request;
20
pub mod response;
21
pub mod stream;
22

23
#[cfg(test)]
24
mod tests;
25

26
use std::collections::BTreeMap;
27
use std::io::Write;
28
use std::str::FromStr;
29
use std::{fmt, io};
30

31
use serde_json;
32
use stacks_common::codec::Error as CodecError;
33

34
pub use crate::net::http::common::{
35
    parse_bytes, parse_bytestream, parse_json, HttpReservedHeader, HttpVersion,
36
    HTTP_PREAMBLE_MAX_NUM_HEADERS,
37
};
38
pub use crate::net::http::error::{
39
    http_error_from_code_and_text, http_reason, HttpBadRequest, HttpError, HttpErrorResponse,
40
    HttpForbidden, HttpMethodNotAllowed, HttpNotFound, HttpNotImplemented, HttpPaymentRequired,
41
    HttpRequestTimeout, HttpServerError, HttpServiceUnavailable, HttpUnauthorized,
42
};
43
pub use crate::net::http::request::{
44
    HttpRequest, HttpRequestContents, HttpRequestPayload, HttpRequestPreamble,
45
};
46
pub use crate::net::http::response::{
47
    HttpResponse, HttpResponseContents, HttpResponsePayload, HttpResponsePreamble,
48
};
49
pub use crate::net::http::stream::HttpChunkGenerator;
50

51
#[derive(Debug)]
52
pub enum Error {
53
    /// Serde failed to serialize or deserialize
54
    JsonError(serde_json::Error),
55
    /// We failed to decode something
56
    DecodeError(String),
57
    /// The underlying StacksMessageCodec failed
58
    CodecError(CodecError),
59
    /// Failed to write()
60
    WriteError(io::Error),
61
    /// Failed to read()
62
    ReadError(io::Error),
63
    /// Not enough bytes to parse
64
    UnderflowError(String),
65
    /// Http error response
66
    Http(u16, String),
67
    /// Http 405 error response with the list of allowed methods (for Allow header)
68
    HttpMethodNotAllowed(Vec<String>),
69
    /// Application error
70
    AppError(String),
71
}
72

73
impl fmt::Display for Error {
74
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
×
75
        match self {
×
76
            Error::JsonError(json_error) => fmt::Display::fmt(&json_error, f),
×
77
            Error::DecodeError(msg) => write!(f, "{}", &msg),
×
78
            Error::CodecError(codec_error) => fmt::Display::fmt(&codec_error, f),
×
79
            Error::WriteError(io_error) => fmt::Display::fmt(&io_error, f),
×
80
            Error::ReadError(io_error) => fmt::Display::fmt(&io_error, f),
×
81
            Error::UnderflowError(msg) => write!(f, "{}", msg),
×
82
            Error::Http(code, msg) => write!(f, "code={}, msg={}", code, msg),
×
83
            Error::HttpMethodNotAllowed(methods) => {
×
84
                write!(f, "code=405, allowed={}", methods.join(", "))
×
85
            }
86
            Error::AppError(msg) => write!(f, "{}", &msg),
×
87
        }
88
    }
×
89
}
90

91
impl std::error::Error for Error {
92
    fn cause(&self) -> Option<&dyn std::error::Error> {
×
93
        match self {
×
94
            Error::JsonError(json_error) => Some(json_error),
×
95
            Error::DecodeError(_) => None,
×
96
            Error::CodecError(codec_error) => Some(codec_error),
×
97
            Error::WriteError(io_error) => Some(io_error),
×
98
            Error::ReadError(io_error) => Some(io_error),
×
99
            Error::UnderflowError(_) => None,
×
100
            Error::Http(..) => None,
×
101
            Error::HttpMethodNotAllowed(_) => None,
×
102
            Error::AppError(_) => None,
×
103
        }
104
    }
×
105
}
106

107
impl From<serde_json::Error> for Error {
108
    fn from(e: serde_json::Error) -> Error {
×
109
        Error::JsonError(e)
×
110
    }
×
111
}
112

113
impl From<CodecError> for Error {
114
    fn from(e: CodecError) -> Error {
×
115
        Error::CodecError(e)
×
116
    }
×
117
}
118

119
impl Error {
120
    /// Convert to an HTTP error
121
    pub fn into_http_error(self) -> Box<dyn HttpErrorResponse> {
9✔
122
        match self {
9✔
123
            Error::JsonError(x) => Box::new(HttpBadRequest::new(format!(
×
124
                "Failed to encode or decode JSON: {:?}",
×
125
                &x
×
126
            ))),
×
127
            Error::DecodeError(x) => {
×
128
                Box::new(HttpBadRequest::new(format!("Failed to decode: {}", &x)))
×
129
            }
130
            Error::CodecError(x) => Box::new(HttpBadRequest::new(format!(
×
131
                "Failed to decode due to SIP-003 codec error: {:?}",
×
132
                &x
×
133
            ))),
×
134
            Error::WriteError(x) => Box::new(HttpServerError::new(format!(
×
135
                "Failed to write data: {:?}",
×
136
                &x
×
137
            ))),
×
138
            Error::ReadError(x) => Box::new(HttpServerError::new(format!(
×
139
                "Failed to read data: {:?}",
×
140
                &x
×
141
            ))),
×
142
            Error::UnderflowError(x) => Box::new(HttpBadRequest::new(format!(
×
143
                "Failed to parse data (underflow): {:?}",
×
144
                &x
×
145
            ))),
×
146
            Error::Http(code, msg) => http_error_from_code_and_text(code, msg),
9✔
147
            Error::HttpMethodNotAllowed(methods) => {
×
148
                Box::new(HttpMethodNotAllowed::with_allowed_methods(methods))
×
149
            }
150
            Error::AppError(x) => Box::new(HttpServerError::new(format!(
×
151
                "Unhandled application error: {:?}",
×
152
                &x
×
153
            ))),
×
154
        }
155
    }
9✔
156
}
157

158
/// supported HTTP content types
159
#[derive(Debug, Clone, PartialEq, Copy)]
160
pub enum HttpContentType {
161
    Bytes,
162
    Text,
163
    JSON,
164
}
165

166
impl fmt::Display for HttpContentType {
167
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
27,480,064✔
168
        write!(f, "{}", self.as_str())
27,480,064✔
169
    }
27,480,064✔
170
}
171

172
impl HttpContentType {
173
    pub fn as_str(&self) -> &'static str {
27,480,064✔
174
        match *self {
27,480,064✔
175
            HttpContentType::Bytes => "application/octet-stream",
5,438,753✔
176
            HttpContentType::Text => "text/plain",
5,587,013✔
177
            HttpContentType::JSON => "application/json",
16,454,298✔
178
        }
179
    }
27,480,064✔
180
}
181

182
impl FromStr for HttpContentType {
183
    type Err = CodecError;
184

185
    fn from_str(header: &str) -> Result<HttpContentType, CodecError> {
1,375,709✔
186
        let s = header.to_string().to_lowercase();
1,375,709✔
187
        if s == "application/octet-stream" {
1,375,709✔
188
            Ok(HttpContentType::Bytes)
138,307✔
189
        } else if s == "text/plain" || s.starts_with("text/plain;") {
1,237,402✔
190
            Ok(HttpContentType::Text)
×
191
        } else if s == "application/json" || s.starts_with("application/json;") {
1,237,402✔
192
            Ok(HttpContentType::JSON)
1,237,402✔
193
        } else {
194
            Err(CodecError::DeserializeError(format!(
×
195
                "Unsupported HTTP content type: {header}"
×
196
            )))
×
197
        }
198
    }
1,375,709✔
199
}
200

201
/// Write out a set of HTTP headers to the given Write implementation
202
pub fn write_headers<W: Write>(
11,657,378✔
203
    fd: &mut W,
11,657,378✔
204
    headers: &BTreeMap<String, String>,
11,657,378✔
205
) -> Result<(), CodecError> {
11,657,378✔
206
    for (key, value) in headers.iter() {
11,857,835✔
207
        fd.write_all(key.as_str().as_bytes())
10,292,700✔
208
            .map_err(CodecError::WriteError)?;
10,292,700✔
209
        fd.write_all(": ".as_bytes())
10,292,700✔
210
            .map_err(CodecError::WriteError)?;
10,292,700✔
211
        fd.write_all(value.as_str().as_bytes())
10,292,700✔
212
            .map_err(CodecError::WriteError)?;
10,292,700✔
213
        fd.write_all("\r\n".as_bytes())
10,292,700✔
214
            .map_err(CodecError::WriteError)?;
10,292,700✔
215
    }
216
    Ok(())
11,657,378✔
217
}
11,657,378✔
218

219
/// Create the default accept header
220
pub fn default_accept_header() -> String {
5,280,959✔
221
    format!(
5,280,959✔
222
        "Accept: {}, {}, {}\r\n",
223
        HttpContentType::Bytes,
224
        HttpContentType::JSON,
225
        HttpContentType::Text
226
    )
227
}
5,280,959✔
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