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

mattwparas / steel / 17772690385

16 Sep 2025 04:36PM UTC coverage: 43.331% (+0.04%) from 43.289%
17772690385

Pull #519

github

web-flow
Merge 98d4fd22c into 3c10433b9
Pull Request #519: fix a bunch more clippy lints

56 of 123 new or added lines in 30 files covered. (45.53%)

8 existing lines in 3 files now uncovered.

12416 of 28654 relevant lines covered (43.33%)

2985398.75 hits per line

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

17.11
/crates/steel-core/src/primitives/http.rs
1
use crate::{
2
    gc::{Gc, ShareableMut},
3
    rerrs::ErrorKind,
4
    rvals::{AsRefSteelVal, Custom, IntoSteelVal, SteelByteVector, SteelHashMap, SteelString},
5
    steel_vm::builtin::BuiltInModule,
6
    SteelErr, SteelVal,
7
};
8

9
use crate::rvals::Result;
10

11
pub struct Header {
12
    pub name: String,
13
    pub value: Vec<u8>,
14
}
15

16
pub struct SteelRequest {
17
    method: SteelString,
18
    path: SteelString,
19
    version: SteelString,
20
    // Offset into the buffer where the body starts
21
    body_offset: usize,
22
    // Probably just... push down directly into a hashmap?
23
    // or keep it some kind of key value pair store?
24
    headers: Vec<Header>,
25
}
26

27
impl Custom for SteelRequest {}
28

29
pub struct SteelResponse {
30
    pub version: u8,
31
    /// The response code, such as `200`.
32
    pub code: u16,
33
    /// The response reason-phrase, such as `OK`.
34
    ///
35
    /// Contains an empty string if the reason-phrase was missing or contained invalid characters.
36
    pub reason: String,
37
    /// The response headers.
38
    pub headers: Vec<Header>,
39

40
    pub body_offset: usize,
41
}
42

43
impl Custom for SteelResponse {}
44

45
#[steel_derive::function(name = "http-request-method")]
46
pub fn method(value: &SteelVal) -> Result<SteelVal> {
×
47
    SteelRequest::as_ref(value)
×
48
        .map(|x| x.method.clone())
×
49
        .map(SteelVal::StringV)
×
50
}
51

52
#[steel_derive::function(name = "http-request-path")]
53
pub fn path(value: &SteelVal) -> Result<SteelVal> {
×
54
    SteelRequest::as_ref(value)
×
55
        .map(|x| x.path.clone())
×
56
        .map(SteelVal::StringV)
×
57
}
58

59
#[steel_derive::function(name = "http-request-version")]
60
pub fn version(value: &SteelVal) -> Result<SteelVal> {
×
61
    SteelRequest::as_ref(value)
×
62
        .map(|x| x.version.clone())
×
63
        .map(SteelVal::StringV)
×
64
}
65

66
/// Download file from a URL
67
#[steel_derive::function(name = "download-file!")]
68
fn download_file(_url: &SteelString, _file: &SteelString) -> Result<SteelVal> {
×
69
    #[cfg(not(feature = "ureq"))]
70
    {
71
        Err(SteelErr::new(
72
            ErrorKind::BadSyntax,
73
            "download-file! is not implemented".to_string(),
74
        ))
75
    }
76

77
    #[cfg(feature = "ureq")]
78
    {
79
        use std::{fs, path::PathBuf};
80

81
        let url = _url.as_str();
×
82
        let file = PathBuf::from(_file.as_str());
×
83
        let contents = ureq::get(url)
×
84
            .call()
85
            .map_err(|err| {
×
86
                SteelErr::new(ErrorKind::Io, format!("failed to call http method: {err}"))
×
87
            })?
88
            .body_mut()
89
            .read_to_vec()
90
            .map_err(|err| SteelErr::new(ErrorKind::Io, format!("http request failed: {err}")))?;
×
91

92
        fs::write(file, contents)
93
            .map_err(|err| SteelErr::new(ErrorKind::Io, format!("failed to write: {err}")))?;
×
94

95
        Ok(().into())
×
96
    }
97
}
98

99
#[steel_derive::function(name = "http-request-body-offset")]
100
pub fn body_offset(value: &SteelVal) -> Result<SteelVal> {
×
101
    SteelRequest::as_ref(value)
×
102
        .map(|x| x.body_offset as isize)
×
103
        .map(SteelVal::IntV)
×
104
}
105

106
#[steel_derive::function(name = "http-request-headers")]
107
pub fn headers(value: &SteelVal) -> Result<SteelVal> {
×
108
    let req = SteelRequest::as_ref(value)?;
×
109

110
    Ok(SteelVal::HashMapV(SteelHashMap(Gc::new(
111
        req.headers
112
            .iter()
113
            .map(|x| {
×
114
                (
115
                    SteelVal::StringV(x.name.clone().into()),
×
116
                    SteelVal::ByteVector(SteelByteVector::new(x.value.clone())),
×
117
                )
118
            })
119
            .collect::<crate::values::HashMap<_, _>>(),
120
    ))))
121
}
122

123
#[steel_derive::function(name = "http-response-headers")]
124
pub fn resp_headers(value: &SteelVal) -> Result<SteelVal> {
×
125
    let resp = SteelResponse::as_ref(value)?;
×
126

127
    Ok(SteelVal::HashMapV(SteelHashMap(Gc::new(
128
        resp.headers
129
            .iter()
130
            .map(|x| {
×
131
                (
132
                    SteelVal::StringV(x.name.clone().into()),
×
133
                    SteelVal::ByteVector(SteelByteVector::new(x.value.clone())),
×
134
                )
135
            })
136
            .collect::<crate::values::HashMap<_, _>>(),
137
    ))))
138
}
139

140
// If not complete, try again?
141
fn parse_request(buf: &[u8]) -> Result<SteelVal> {
×
142
    // Pull more bytes from the stream?
143
    let mut headers = [httparse::EMPTY_HEADER; 16];
×
144
    let mut req = httparse::Request::new(&mut headers);
×
NEW
145
    let res = req.parse(buf).unwrap();
×
146
    if res.is_complete() {
×
147
        let request = SteelRequest {
148
            method: req.method.unwrap().to_string().into(),
×
149
            path: req.path.unwrap().to_string().into(),
×
150
            version: req.version.unwrap().to_string().into(),
×
151
            body_offset: res.unwrap(),
×
152
            headers: headers
×
153
                .iter()
154
                .filter_map(|x| {
155
                    if *x != httparse::EMPTY_HEADER {
156
                        Some(Header {
157
                            name: x.name.to_string(),
158
                            value: x.value.to_vec(),
159
                        })
160
                    } else {
161
                        None
162
                    }
163
                })
164
                .collect(),
165
        };
166

167
        request.into_steelval()
×
168
    } else {
169
        Ok(SteelVal::BoolV(false))
×
170
    }
171
}
172

173
fn parse_response(buf: &[u8]) -> Result<SteelVal> {
×
174
    // Pull more bytes from the stream?
175
    let mut headers = [httparse::EMPTY_HEADER; 64];
×
176
    let mut req = httparse::Response::new(&mut headers);
×
NEW
177
    let res = req.parse(buf).unwrap();
×
178
    if res.is_complete() {
×
179
        let request = SteelResponse {
180
            version: req.version.unwrap(),
×
181
            code: req.code.unwrap(),
×
182
            reason: req.reason.unwrap().to_string(),
×
183
            body_offset: res.unwrap(),
×
184
            headers: headers
×
185
                .iter()
186
                .filter_map(|x| {
187
                    if *x != httparse::EMPTY_HEADER {
188
                        Some(Header {
189
                            name: x.name.to_string(),
190
                            value: x.value.to_vec(),
191
                        })
192
                    } else {
193
                        None
194
                    }
195
                })
196
                .collect(),
197
        };
198

199
        request.into_steelval()
×
200
    } else {
201
        Ok(SteelVal::BoolV(false))
×
202
    }
203
}
204

205
#[steel_derive::function(name = "http-parse-request")]
206
pub fn parse_http_request(vector: &SteelByteVector) -> Result<SteelVal> {
×
207
    parse_request(&vector.vec.read())
×
208
}
209

210
#[steel_derive::function(name = "http-parse-response")]
211
pub fn parse_http_response(vector: &SteelByteVector) -> Result<SteelVal> {
×
212
    parse_response(&vector.vec.read())
×
213
}
214

215
pub fn http_module() -> BuiltInModule {
4✔
216
    let mut module = BuiltInModule::new("steel/http".to_string());
16✔
217

218
    module
36✔
219
        .register_native_fn_definition(PARSE_HTTP_REQUEST_DEFINITION)
32✔
220
        .register_native_fn_definition(METHOD_DEFINITION)
28✔
221
        .register_native_fn_definition(VERSION_DEFINITION)
24✔
222
        .register_native_fn_definition(PATH_DEFINITION)
20✔
223
        .register_native_fn_definition(BODY_OFFSET_DEFINITION)
16✔
224
        .register_native_fn_definition(HEADERS_DEFINITION)
12✔
225
        .register_native_fn_definition(RESP_HEADERS_DEFINITION)
8✔
226
        .register_native_fn_definition(PARSE_HTTP_RESPONSE_DEFINITION)
4✔
227
        .register_native_fn_definition(DOWNLOAD_FILE_DEFINITION);
8✔
228

229
    // module
230
    //     .register_native_fn_definition(TCP_CONNECT_DEFINITION)
231
    //     .register_native_fn_definition(TCP_INPUT_PORT_DEFINITION)
232
    //     .register_native_fn_definition(TCP_OUTPUT_PORT_DEFINITION)
233
    //     .register_native_fn_definition(TCP_BUFFERED_OUTPUT_PORT_DEFINITION)
234
    //     .register_native_fn_definition(TCP_LISTEN_DEFINITION)
235
    //     .register_native_fn_definition(TCP_ACCEPT_DEFINITION);
236

237
    module
4✔
238
}
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

© 2025 Coveralls, Inc