1use std::mem::transmute;
15use std::slice::from_raw_parts_mut;
16
17use crate::ffi;
18use crate::ffi::VslTag;
19use crate::vcl::str_or_bytes::StrOrBytes;
20use crate::vcl::{VclResult, Workspace};
21
22const HDR_FIRST: u16 = ffi::HTTP_HDR_FIRST as u16;
25const HDR_METHOD: u16 = ffi::HTTP_HDR_METHOD as u16;
26const HDR_PROTO: u16 = ffi::HTTP_HDR_PROTO as u16;
27const HDR_REASON: u16 = ffi::HTTP_HDR_REASON as u16;
28const HDR_STATUS: u16 = ffi::HTTP_HDR_STATUS as u16;
29const HDR_UNSET: u16 = ffi::HTTP_HDR_UNSET as u16;
30const HDR_URL: u16 = ffi::HTTP_HDR_URL as u16;
31
32#[derive(Debug)]
34pub struct HttpHeaders<'a> {
35 pub raw: &'a mut ffi::http,
36}
37
38impl HttpHeaders<'_> {
39 pub(crate) fn from_ptr(p: ffi::VCL_HTTP) -> Option<Self> {
41 Some(HttpHeaders {
42 raw: unsafe { p.0.as_mut()? },
43 })
44 }
45
46 fn change_header<'a>(&mut self, idx: u16, value: impl Into<StrOrBytes<'a>>) -> VclResult<()> {
47 assert!(idx < self.raw.nhd);
48
49 let mut ws = Workspace::from_ptr(self.raw.ws);
51 unsafe {
52 let hd = self.raw.hd.offset(idx as isize).as_mut().unwrap();
53 *hd = ws.copy_bytes_with_null(value.into())?;
54 let hdf = self.raw.hdf.offset(idx as isize).as_mut().unwrap();
55 *hdf = 0;
56 }
57 Ok(())
58 }
59
60 pub fn set_header(&mut self, name: &str, value: &str) -> VclResult<()> {
63 assert!(self.raw.nhd <= self.raw.shd);
64 if self.raw.nhd == self.raw.shd {
65 return Err(c"no more header slot".into());
66 }
67
68 let idx = self.raw.nhd;
69 self.raw.nhd += 1;
70 let res = self.change_header(idx, &format!("{name}: {value}"));
72 if res.is_ok() {
73 unsafe {
74 ffi::VSLbt(
75 self.raw.vsl,
76 transmute::<u32, VslTag>((self.raw.logtag as u32) + u32::from(HDR_FIRST)),
77 *self.raw.hd.add(idx as usize),
78 );
79 }
80 } else {
81 self.raw.nhd -= 1;
82 }
83 res
84 }
85
86 pub fn unset_header(&mut self, name: &str) {
87 let hdrs = unsafe {
88 &from_raw_parts_mut(self.raw.hd, self.raw.nhd as usize)[(HDR_FIRST as usize)..]
89 };
90
91 let mut idx_empty = 0;
92 for (idx, hd) in hdrs.iter().enumerate() {
93 let (n, _) = hd.parse_header().unwrap();
94 if name.eq_ignore_ascii_case(n) {
95 unsafe {
96 ffi::VSLbt(
97 self.raw.vsl,
98 transmute::<u32, VslTag>(
99 (self.raw.logtag as u32) + u32::from(HDR_UNSET) + u32::from(HDR_METHOD),
100 ),
101 *self.raw.hd.add(HDR_FIRST as usize + idx),
102 );
103 }
104 continue;
105 }
106 if idx != idx_empty {
107 unsafe {
108 std::ptr::copy_nonoverlapping(
109 self.raw.hd.add(HDR_FIRST as usize + idx),
110 self.raw.hd.add(HDR_FIRST as usize + idx_empty),
111 1,
112 );
113 std::ptr::copy_nonoverlapping(
114 self.raw.hdf.add(HDR_FIRST as usize + idx),
115 self.raw.hdf.add(HDR_FIRST as usize + idx_empty),
116 1,
117 );
118 }
119 }
120 idx_empty += 1;
121 }
122 self.raw.nhd = HDR_FIRST + idx_empty as u16;
123 }
124
125 fn field(&self, idx: u16) -> Option<StrOrBytes<'_>> {
127 unsafe {
128 if idx >= self.raw.nhd {
129 None
130 } else {
131 self.raw
132 .hd
133 .offset(idx as isize)
134 .as_ref()
135 .unwrap()
136 .to_slice()
137 .map(StrOrBytes::from)
138 }
139 }
140 }
141
142 pub fn method(&self) -> Option<StrOrBytes<'_>> {
144 self.field(HDR_METHOD)
145 }
146
147 pub fn url(&self) -> Option<StrOrBytes<'_>> {
149 self.field(HDR_URL)
150 }
151
152 pub fn proto(&self) -> Option<StrOrBytes<'_>> {
157 self.field(HDR_PROTO)
158 }
159
160 pub fn set_proto(&mut self, value: &str) -> VclResult<()> {
162 self.raw.protover = match value {
163 "HTTP/0.9" => 9,
164 "HTTP/1.0" => 10,
165 "HTTP/1.1" => 11,
166 "HTTP/2.0" => 20,
167 _ => 0,
168 };
169 self.change_header(HDR_PROTO, value)
170 }
171
172 pub fn status(&self) -> Option<StrOrBytes<'_>> {
174 self.field(HDR_STATUS)
175 }
176
177 pub fn set_status(&mut self, status: u16) {
179 unsafe {
180 ffi::http_SetStatus(
181 self.raw,
182 status,
183 #[cfg(not(varnishsys_6))]
184 std::ptr::null(),
185 );
186 }
187 }
188
189 pub fn reason(&self) -> Option<StrOrBytes<'_>> {
191 self.field(HDR_REASON)
192 }
193
194 pub fn set_reason(&mut self, value: &str) -> VclResult<()> {
196 self.change_header(HDR_REASON, value)
197 }
198
199 pub fn header(&self, name: &str) -> Option<StrOrBytes<'_>> {
203 self.iter()
204 .find(|hdr| name.eq_ignore_ascii_case(hdr.0))
205 .map(|hdr| hdr.1)
206 }
207
208 pub fn iter(&self) -> HttpHeadersIter<'_> {
209 HttpHeadersIter {
210 http: self,
211 cursor: HDR_FIRST as isize,
212 }
213 }
214}
215
216impl<'a> IntoIterator for &'a HttpHeaders<'a> {
217 type Item = (&'a str, StrOrBytes<'a>);
218 type IntoIter = HttpHeadersIter<'a>;
219
220 fn into_iter(self) -> Self::IntoIter {
221 self.iter()
222 }
223}
224
225#[derive(Debug)]
226pub struct HttpHeadersIter<'a> {
227 http: &'a HttpHeaders<'a>,
228 cursor: isize,
229}
230
231impl<'a> Iterator for HttpHeadersIter<'a> {
232 type Item = (&'a str, StrOrBytes<'a>);
233
234 fn next(&mut self) -> Option<Self::Item> {
235 loop {
236 let nhd = self.http.raw.nhd;
237 if self.cursor >= nhd as isize {
238 return None;
239 }
240 let hd = unsafe { self.http.raw.hd.offset(self.cursor).as_ref().unwrap() };
241 self.cursor += 1;
242 if let Some(hdr) = hd.parse_header() {
243 return Some(hdr);
244 }
245 }
246 }
247}