proxy_wasm_experimental/
hostcalls.rs

1// Copyright 2020 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use crate::dispatcher;
16use crate::types::*;
17use std::ptr::{null, null_mut};
18use std::time::{Duration, SystemTime, UNIX_EPOCH};
19
20use crate::error::{HostCallError, HostResponseError, Result};
21
22/// Represents empty headers map.
23pub const NO_HEADERS: &[(&[u8], &[u8])] = &[];
24
25/// Represents empty body.
26pub const NO_BODY: Option<&[u8]> = None;
27
28/// Represents empty trailers map.
29pub const NO_TRAILERS: &[(&[u8], &[u8])] = &[];
30
31mod abi {
32    pub const PROXY_LOG: &str = "proxy_log";
33    pub const PROXY_GET_CURRENT_TIME_NANOSECONDS: &str = "proxy_get_current_time_nanoseconds";
34    pub const PROXY_SET_TICK_PERIOD_MILLISECONDS: &str = "proxy_set_tick_period_milliseconds";
35    pub const PROXY_GET_BUFFER_BYTES: &str = "proxy_get_buffer_bytes";
36    pub const PROXY_SET_BUFFER_BYTES: &str = "proxy_set_buffer_bytes";
37    pub const PROXY_GET_HEADER_MAP_PAIRS: &str = "proxy_get_header_map_pairs";
38    pub const PROXY_SET_HEADER_MAP_PAIRS: &str = "proxy_set_header_map_pairs";
39    pub const PROXY_GET_HEADER_MAP_VALUE: &str = "proxy_get_header_map_value";
40    pub const PROXY_REPLACE_HEADER_MAP_VALUE: &str = "proxy_replace_header_map_value";
41    pub const PROXY_REMOVE_HEADER_MAP_VALUE: &str = "proxy_remove_header_map_value";
42    pub const PROXY_ADD_HEADER_MAP_VALUE: &str = "proxy_add_header_map_value";
43    pub const PROXY_GET_PROPERTY: &str = "proxy_get_property";
44    pub const PROXY_SET_PROPERTY: &str = "proxy_set_property";
45    pub const PROXY_GET_SHARED_DATA: &str = "proxy_get_shared_data";
46    pub const PROXY_SET_SHARED_DATA: &str = "proxy_set_shared_data";
47    pub const PROXY_REGISTER_SHARED_QUEUE: &str = "proxy_register_shared_queue";
48    pub const PROXY_RESOLVE_SHARED_QUEUE: &str = "proxy_resolve_shared_queue";
49    pub const PROXY_DEQUEUE_SHARED_QUEUE: &str = "proxy_dequeue_shared_queue";
50    pub const PROXY_ENQUEUE_SHARED_QUEUE: &str = "proxy_enqueue_shared_queue";
51    pub const PROXY_CONTINUE_STREAM: &str = "proxy_continue_stream";
52    pub const PROXY_CLOSE_STREAM: &str = "proxy_close_stream";
53    pub const PROXY_SEND_LOCAL_RESPONSE: &str = "proxy_send_local_response";
54    pub const PROXY_HTTP_CALL: &str = "proxy_http_call";
55    pub const PROXY_SET_EFFECTIVE_CONTEXT: &str = "proxy_set_effective_context";
56    pub const PROXY_DONE: &str = "proxy_done";
57    pub const PROXY_DEFINE_METRIC: &str = "proxy_define_metric";
58    pub const PROXY_GET_METRIC: &str = "proxy_get_metric";
59    pub const PROXY_RECORD_METRIC: &str = "proxy_record_metric";
60    pub const PROXY_INCREMENT_METRIC: &str = "proxy_increment_metric";
61}
62
63extern "C" {
64    fn proxy_log(level: LogLevel, message_data: *const u8, message_size: usize) -> Status;
65}
66
67/// Logs a message at a given log level.
68pub fn log(level: LogLevel, message: &str) -> Result<()> {
69    unsafe {
70        match proxy_log(level, message.as_ptr(), message.len()) {
71            Status::Ok => Ok(()),
72            status => Err(HostCallError::new(abi::PROXY_LOG, status).into()),
73        }
74    }
75}
76
77extern "C" {
78    fn proxy_get_current_time_nanoseconds(return_time: *mut u64) -> Status;
79}
80
81/// Returns current system time.
82pub fn get_current_time() -> Result<SystemTime> {
83    let mut return_time: u64 = 0;
84    unsafe {
85        match proxy_get_current_time_nanoseconds(&mut return_time) {
86            Status::Ok => Ok(UNIX_EPOCH + Duration::from_nanos(return_time)),
87            status => {
88                Err(HostCallError::new(abi::PROXY_GET_CURRENT_TIME_NANOSECONDS, status).into())
89            }
90        }
91    }
92}
93
94extern "C" {
95    fn proxy_set_tick_period_milliseconds(period: u32) -> Status;
96}
97
98/// Sets the timer to a given period.
99pub fn set_tick_period(period: Duration) -> Result<()> {
100    unsafe {
101        match proxy_set_tick_period_milliseconds(period.as_millis() as u32) {
102            Status::Ok => Ok(()),
103            status => {
104                Err(HostCallError::new(abi::PROXY_SET_TICK_PERIOD_MILLISECONDS, status).into())
105            }
106        }
107    }
108}
109
110extern "C" {
111    fn proxy_get_buffer_bytes(
112        buffer_type: BufferType,
113        start: usize,
114        max_size: usize,
115        return_buffer_data: *mut *mut u8,
116        return_buffer_size: *mut usize,
117    ) -> Status;
118}
119
120/// Returns content from a given buffer.
121pub fn get_buffer(
122    buffer_type: BufferType,
123    start: usize,
124    max_size: usize,
125) -> Result<Option<ByteString>> {
126    let mut return_data: *mut u8 = null_mut();
127    let mut return_size: usize = 0;
128    unsafe {
129        match proxy_get_buffer_bytes(
130            buffer_type,
131            start,
132            max_size,
133            &mut return_data,
134            &mut return_size,
135        ) {
136            Status::Ok => {
137                if !return_data.is_null() {
138                    Ok(Vec::from_raw_parts(return_data, return_size, return_size))
139                        .map(ByteString::from)
140                        .map(Option::from)
141                } else {
142                    Ok(None)
143                }
144            }
145            Status::NotFound => Ok(None),
146            status => Err(HostCallError::new(abi::PROXY_GET_BUFFER_BYTES, status).into()),
147        }
148    }
149}
150
151extern "C" {
152    fn proxy_set_buffer_bytes(
153        buffer_type: BufferType,
154        start: usize,
155        size: usize,
156        buffer_data: *const u8,
157        buffer_size: usize,
158    ) -> Status;
159}
160
161/// Mutates content in a given buffer.
162///
163/// # Examples
164///
165/// ```no_run
166/// # use proxy_wasm_experimental as proxy_wasm;
167/// use proxy_wasm::hostcalls;
168/// use proxy_wasm::types::BufferType;
169///
170/// # fn action() -> proxy_wasm::error::Result<()> {
171/// hostcalls::set_buffer(BufferType::HttpRequestBody, 0, usize::MAX, "replacement text")?;
172/// # Ok(())
173/// # }
174pub fn set_buffer<B>(buffer_type: BufferType, start: usize, size: usize, value: B) -> Result<()>
175where
176    B: AsRef<[u8]>,
177{
178    unsafe {
179        match proxy_set_buffer_bytes(
180            buffer_type,
181            start,
182            size,
183            value.as_ref().as_ptr(),
184            value.as_ref().len(),
185        ) {
186            Status::Ok => Ok(()),
187            status => Err(HostCallError::new(abi::PROXY_SET_BUFFER_BYTES, status).into()),
188        }
189    }
190}
191
192extern "C" {
193    fn proxy_get_header_map_pairs(
194        map_type: MapType,
195        return_map_data: *mut *mut u8,
196        return_map_size: *mut usize,
197    ) -> Status;
198}
199
200/// Returns all key-value pairs from a given map.
201pub fn get_map(map_type: MapType) -> Result<Vec<(ByteString, ByteString)>> {
202    unsafe {
203        let mut return_data: *mut u8 = null_mut();
204        let mut return_size: usize = 0;
205        match proxy_get_header_map_pairs(map_type, &mut return_data, &mut return_size) {
206            Status::Ok => {
207                if !return_data.is_null() {
208                    let serialized_map = Vec::from_raw_parts(return_data, return_size, return_size);
209                    utils::deserialize_map(&serialized_map).map_err(|err| {
210                        HostResponseError::new(abi::PROXY_GET_HEADER_MAP_PAIRS, err).into()
211                    })
212                } else {
213                    Ok(Vec::new())
214                }
215            }
216            status => Err(HostCallError::new(abi::PROXY_GET_HEADER_MAP_PAIRS, status).into()),
217        }
218    }
219}
220
221extern "C" {
222    fn proxy_set_header_map_pairs(
223        map_type: MapType,
224        map_data: *const u8,
225        map_size: usize,
226    ) -> Status;
227}
228
229/// Sets all key-value pairs in a given map.
230///
231/// # Examples
232///
233/// ```no_run
234/// # use proxy_wasm_experimental as proxy_wasm;
235/// use proxy_wasm::hostcalls;
236/// use proxy_wasm::types::MapType;
237///
238/// # fn action() -> proxy_wasm::error::Result<()> {
239/// hostcalls::set_map(MapType::HttpRequestHeaders, &vec![
240///     (":method", "GET"),
241///     (":path", "/stuff"),
242/// ])?;
243/// # Ok(())
244/// # }
245/// ```
246pub fn set_map<K, V>(map_type: MapType, map: &[(K, V)]) -> Result<()>
247where
248    K: AsRef<[u8]>,
249    V: AsRef<[u8]>,
250{
251    let serialized_map = utils::serialize_map(map);
252    unsafe {
253        match proxy_set_header_map_pairs(map_type, serialized_map.as_ptr(), serialized_map.len()) {
254            Status::Ok => Ok(()),
255            status => Err(HostCallError::new(abi::PROXY_SET_HEADER_MAP_PAIRS, status).into()),
256        }
257    }
258}
259
260extern "C" {
261    fn proxy_get_header_map_value(
262        map_type: MapType,
263        key_data: *const u8,
264        key_size: usize,
265        return_value_data: *mut *mut u8,
266        return_value_size: *mut usize,
267    ) -> Status;
268}
269
270/// Returns value of a given key from a given map.
271///
272/// # Examples
273///
274/// ```no_run
275/// # use proxy_wasm_experimental as proxy_wasm;
276/// use proxy_wasm::hostcalls;
277/// use proxy_wasm::types::MapType;
278///
279/// # fn action() -> proxy_wasm::error::Result<()> {
280/// let value = hostcalls::get_map_value(MapType::HttpRequestHeaders, "authorization")?;
281/// # Ok(())
282/// # }
283/// ```
284pub fn get_map_value<K>(map_type: MapType, key: K) -> Result<Option<ByteString>>
285where
286    K: AsRef<[u8]>,
287{
288    let mut return_data: *mut u8 = null_mut();
289    let mut return_size: usize = 0;
290    unsafe {
291        match proxy_get_header_map_value(
292            map_type,
293            key.as_ref().as_ptr(),
294            key.as_ref().len(),
295            &mut return_data,
296            &mut return_size,
297        ) {
298            Status::Ok => {
299                if !return_data.is_null() {
300                    Ok(Vec::from_raw_parts(return_data, return_size, return_size))
301                        .map(ByteString::from)
302                        .map(Option::from)
303                } else {
304                    Ok(None)
305                }
306            }
307            status => Err(HostCallError::new(abi::PROXY_GET_HEADER_MAP_VALUE, status).into()),
308        }
309    }
310}
311
312extern "C" {
313    fn proxy_replace_header_map_value(
314        map_type: MapType,
315        key_data: *const u8,
316        key_size: usize,
317        value_data: *const u8,
318        value_size: usize,
319    ) -> Status;
320}
321
322extern "C" {
323    fn proxy_remove_header_map_value(
324        map_type: MapType,
325        key_data: *const u8,
326        key_size: usize,
327    ) -> Status;
328}
329
330/// Sets / replaces / removes value of given key from a given map.
331///
332/// # Examples
333///
334/// ```no_run
335/// # use proxy_wasm_experimental as proxy_wasm;
336/// use proxy_wasm::hostcalls;
337/// use proxy_wasm::types::MapType;
338///
339/// # fn action() -> proxy_wasm::error::Result<()> {
340/// hostcalls::set_map_value(MapType::HttpRequestHeaders, "authorization", Some("Bearer ..."))?;
341/// # Ok(())
342/// # }
343/// ```
344pub fn set_map_value<K, V>(map_type: MapType, key: K, value: Option<V>) -> Result<()>
345where
346    K: AsRef<[u8]>,
347    V: AsRef<[u8]>,
348{
349    unsafe {
350        if let Some(value) = value {
351            match proxy_replace_header_map_value(
352                map_type,
353                key.as_ref().as_ptr(),
354                key.as_ref().len(),
355                value.as_ref().as_ptr(),
356                value.as_ref().len(),
357            ) {
358                Status::Ok => Ok(()),
359                status => {
360                    Err(HostCallError::new(abi::PROXY_REPLACE_HEADER_MAP_VALUE, status).into())
361                }
362            }
363        } else {
364            match proxy_remove_header_map_value(map_type, key.as_ref().as_ptr(), key.as_ref().len())
365            {
366                Status::Ok => Ok(()),
367                status => {
368                    Err(HostCallError::new(abi::PROXY_REMOVE_HEADER_MAP_VALUE, status).into())
369                }
370            }
371        }
372    }
373}
374
375extern "C" {
376    fn proxy_add_header_map_value(
377        map_type: MapType,
378        key_data: *const u8,
379        key_size: usize,
380        value_data: *const u8,
381        value_size: usize,
382    ) -> Status;
383}
384
385/// Adds a key-value pair to a given map.
386///
387/// # Examples
388///
389/// ```no_run
390/// # use proxy_wasm_experimental as proxy_wasm;
391/// use proxy_wasm::hostcalls;
392/// use proxy_wasm::types::MapType;
393///
394/// # fn action() -> proxy_wasm::error::Result<()> {
395/// hostcalls::add_map_value(MapType::HttpRequestHeaders, "authorization", "Bearer ...")?;
396/// # Ok(())
397/// # }
398/// ```
399pub fn add_map_value<K, V>(map_type: MapType, key: K, value: V) -> Result<()>
400where
401    K: AsRef<[u8]>,
402    V: AsRef<[u8]>,
403{
404    unsafe {
405        match proxy_add_header_map_value(
406            map_type,
407            key.as_ref().as_ptr(),
408            key.as_ref().len(),
409            value.as_ref().as_ptr(),
410            value.as_ref().len(),
411        ) {
412            Status::Ok => Ok(()),
413            status => Err(HostCallError::new(abi::PROXY_ADD_HEADER_MAP_VALUE, status).into()),
414        }
415    }
416}
417
418extern "C" {
419    fn proxy_get_property(
420        path_data: *const u8,
421        path_size: usize,
422        return_value_data: *mut *mut u8,
423        return_value_size: *mut usize,
424    ) -> Status;
425}
426
427/// Returns value of a property in the current context.
428///
429/// # Examples
430///
431/// ```no_run
432/// # use proxy_wasm_experimental as proxy_wasm;
433/// use proxy_wasm::hostcalls;
434///
435/// # fn action() -> proxy_wasm::error::Result<()> {
436/// let value = hostcalls::get_property(&["request", "time"])?;
437/// # Ok(())
438/// # }
439/// ```
440pub fn get_property<P>(path: &[P]) -> Result<Option<ByteString>>
441where
442    P: AsRef<str>,
443{
444    let serialized_path = utils::serialize_property_path(path);
445    let mut return_data: *mut u8 = null_mut();
446    let mut return_size: usize = 0;
447    unsafe {
448        match proxy_get_property(
449            serialized_path.as_ptr(),
450            serialized_path.len(),
451            &mut return_data,
452            &mut return_size,
453        ) {
454            Status::Ok => {
455                if !return_data.is_null() {
456                    Ok(Vec::from_raw_parts(return_data, return_size, return_size))
457                        .map(ByteString::from)
458                        .map(Option::from)
459                } else {
460                    Ok(None)
461                }
462            }
463            Status::NotFound => Ok(None),
464            status => Err(HostCallError::new(abi::PROXY_GET_PROPERTY, status).into()),
465        }
466    }
467}
468
469extern "C" {
470    fn proxy_set_property(
471        path_data: *const u8,
472        path_size: usize,
473        value_data: *const u8,
474        value_size: usize,
475    ) -> Status;
476}
477
478/// Sets property to a given value in the current context.
479///
480/// # Examples
481///
482/// ```no_run
483/// # use proxy_wasm_experimental as proxy_wasm;
484/// use proxy_wasm::hostcalls;
485///
486/// # fn action() -> proxy_wasm::error::Result<()> {
487/// hostcalls::set_property(&["my_filter", "my_property"], Some("my value"))?;
488/// # Ok(())
489/// # }
490/// ```
491pub fn set_property<P, V>(path: &[P], value: Option<V>) -> Result<()>
492where
493    P: AsRef<str>,
494    V: AsRef<[u8]>,
495{
496    let serialized_path = utils::serialize_property_path(path);
497    let (value_ptr, value_len) = value.map_or((null(), 0), |value| {
498        (value.as_ref().as_ptr(), value.as_ref().len())
499    });
500    unsafe {
501        match proxy_set_property(
502            serialized_path.as_ptr(),
503            serialized_path.len(),
504            value_ptr,
505            value_len,
506        ) {
507            Status::Ok => Ok(()),
508            status => Err(HostCallError::new(abi::PROXY_SET_PROPERTY, status).into()),
509        }
510    }
511}
512
513extern "C" {
514    fn proxy_get_shared_data(
515        key_data: *const u8,
516        key_size: usize,
517        return_value_data: *mut *mut u8,
518        return_value_size: *mut usize,
519        return_cas: *mut u32,
520    ) -> Status;
521}
522
523/// Returns shared data by key.
524///
525/// # Examples
526///
527/// ```no_run
528/// # use proxy_wasm_experimental as proxy_wasm;
529/// use proxy_wasm::hostcalls;
530///
531/// # fn action() -> proxy_wasm::error::Result<()> {
532/// let (data, version) = hostcalls::get_shared_data("my_shared_key")?;
533/// # Ok(())
534/// # }
535/// ```
536pub fn get_shared_data<K>(key: K) -> Result<(Option<ByteString>, Option<u32>)>
537where
538    K: AsRef<str>,
539{
540    let mut return_data: *mut u8 = null_mut();
541    let mut return_size: usize = 0;
542    let mut return_cas: u32 = 0;
543    unsafe {
544        match proxy_get_shared_data(
545            key.as_ref().as_ptr(),
546            key.as_ref().len(),
547            &mut return_data,
548            &mut return_size,
549            &mut return_cas,
550        ) {
551            Status::Ok => {
552                let cas = match return_cas {
553                    0 => None,
554                    cas => Some(cas),
555                };
556                if !return_data.is_null() {
557                    Ok((
558                        Some(Vec::from_raw_parts(return_data, return_size, return_size))
559                            .map(ByteString::from),
560                        cas,
561                    ))
562                } else {
563                    Ok((None, cas))
564                }
565            }
566            Status::NotFound => Ok((None, None)),
567            status => Err(HostCallError::new(abi::PROXY_GET_SHARED_DATA, status).into()),
568        }
569    }
570}
571
572extern "C" {
573    fn proxy_set_shared_data(
574        key_data: *const u8,
575        key_size: usize,
576        value_data: *const u8,
577        value_size: usize,
578        cas: u32,
579    ) -> Status;
580}
581
582/// Sets shared data by key.
583///
584/// # Examples
585///
586/// ```no_run
587/// # use proxy_wasm_experimental as proxy_wasm;
588/// use proxy_wasm::hostcalls;
589///
590/// # fn action() -> proxy_wasm::error::Result<()> {
591/// hostcalls::set_shared_data("my_shared_key", Some("my value"), None)?;
592/// # Ok(())
593/// # }
594/// ```
595pub fn set_shared_data<K, V>(key: K, value: Option<V>, cas: Option<u32>) -> Result<()>
596where
597    K: AsRef<str>,
598    V: AsRef<[u8]>,
599{
600    let (value_ptr, value_len) = value.map_or((null(), 0), |value| {
601        (value.as_ref().as_ptr(), value.as_ref().len())
602    });
603    unsafe {
604        match proxy_set_shared_data(
605            key.as_ref().as_ptr(),
606            key.as_ref().len(),
607            value_ptr,
608            value_len,
609            cas.unwrap_or(0),
610        ) {
611            Status::Ok => Ok(()),
612            status => Err(HostCallError::new(abi::PROXY_SET_SHARED_DATA, status).into()),
613        }
614    }
615}
616
617extern "C" {
618    fn proxy_register_shared_queue(
619        name_data: *const u8,
620        name_size: usize,
621        return_id: *mut u32,
622    ) -> Status;
623}
624
625/// Registers a shared queue with a given name.
626pub fn register_shared_queue(name: &str) -> Result<u32> {
627    unsafe {
628        let mut return_id: u32 = 0;
629        match proxy_register_shared_queue(name.as_ptr(), name.len(), &mut return_id) {
630            Status::Ok => Ok(return_id),
631            status => Err(HostCallError::new(abi::PROXY_REGISTER_SHARED_QUEUE, status).into()),
632        }
633    }
634}
635
636extern "C" {
637    fn proxy_resolve_shared_queue(
638        vm_id_data: *const u8,
639        vm_id_size: usize,
640        name_data: *const u8,
641        name_size: usize,
642        return_id: *mut u32,
643    ) -> Status;
644}
645
646/// Looks up for an existing shared queue with a given name.
647pub fn resolve_shared_queue(vm_id: &str, name: &str) -> Result<Option<u32>> {
648    let mut return_id: u32 = 0;
649    unsafe {
650        match proxy_resolve_shared_queue(
651            vm_id.as_ptr(),
652            vm_id.len(),
653            name.as_ptr(),
654            name.len(),
655            &mut return_id,
656        ) {
657            Status::Ok => Ok(Some(return_id)),
658            Status::NotFound => Ok(None),
659            status => Err(HostCallError::new(abi::PROXY_RESOLVE_SHARED_QUEUE, status).into()),
660        }
661    }
662}
663
664extern "C" {
665    fn proxy_dequeue_shared_queue(
666        queue_id: u32,
667        return_value_data: *mut *mut u8,
668        return_value_size: *mut usize,
669    ) -> Status;
670}
671
672/// Returns data from the end of a given queue.
673pub fn dequeue_shared_queue(queue_id: u32) -> Result<Option<ByteString>> {
674    let mut return_data: *mut u8 = null_mut();
675    let mut return_size: usize = 0;
676    unsafe {
677        match proxy_dequeue_shared_queue(queue_id, &mut return_data, &mut return_size) {
678            Status::Ok => {
679                if !return_data.is_null() {
680                    Ok(Vec::from_raw_parts(return_data, return_size, return_size))
681                        .map(ByteString::from)
682                        .map(Option::from)
683                } else {
684                    Ok(None)
685                }
686            }
687            Status::Empty => Ok(None),
688            status => Err(HostCallError::new(abi::PROXY_DEQUEUE_SHARED_QUEUE, status).into()),
689        }
690    }
691}
692
693extern "C" {
694    fn proxy_enqueue_shared_queue(
695        queue_id: u32,
696        value_data: *const u8,
697        value_size: usize,
698    ) -> Status;
699}
700
701/// Adds a value to the front of a given queue.
702///
703/// # Examples
704///
705/// ```no_run
706/// # use proxy_wasm_experimental as proxy_wasm;
707/// use proxy_wasm::hostcalls;
708///
709/// # fn action() -> proxy_wasm::error::Result<()> {
710/// hostcalls::enqueue_shared_queue(1, Some("my value"))?;
711/// # Ok(())
712/// # }
713/// ```
714pub fn enqueue_shared_queue<V>(queue_id: u32, value: Option<V>) -> Result<()>
715where
716    V: AsRef<[u8]>,
717{
718    let (value_ptr, value_len) = value.map_or((null(), 0), |value| {
719        (value.as_ref().as_ptr(), value.as_ref().len())
720    });
721    unsafe {
722        match proxy_enqueue_shared_queue(queue_id, value_ptr, value_len) {
723            Status::Ok => Ok(()),
724            status => Err(HostCallError::new(abi::PROXY_ENQUEUE_SHARED_QUEUE, status).into()),
725        }
726    }
727}
728
729extern "C" {
730    fn proxy_continue_stream(stream: StreamType) -> Status;
731}
732
733/// Resumes processing of a given stream, i.e. HTTP request or HTTP response.
734pub fn continue_stream(stream_type: StreamType) -> Result<()> {
735    unsafe {
736        match proxy_continue_stream(stream_type) {
737            Status::Ok => Ok(()),
738            status => Err(HostCallError::new(abi::PROXY_CONTINUE_STREAM, status).into()),
739        }
740    }
741}
742
743extern "C" {
744    fn proxy_close_stream(stream: StreamType) -> Status;
745}
746
747/// Terminates processing of a given stream, i.e. HTTP request or HTTP response.
748pub fn close_stream(stream_type: StreamType) -> Result<()> {
749    unsafe {
750        match proxy_close_stream(stream_type) {
751            Status::Ok => Ok(()),
752            status => Err(HostCallError::new(abi::PROXY_CLOSE_STREAM, status).into()),
753        }
754    }
755}
756
757extern "C" {
758    fn proxy_send_local_response(
759        status_code: u32,
760        status_code_details_data: *const u8,
761        status_code_details_size: usize,
762        body_data: *const u8,
763        body_size: usize,
764        headers_data: *const u8,
765        headers_size: usize,
766        grpc_status: i32,
767    ) -> Status;
768}
769
770/// Sends HTTP response without forwarding request to the upstream.
771///
772/// # Examples
773///
774/// ```no_run
775/// # use proxy_wasm_experimental as proxy_wasm;
776/// use proxy_wasm::hostcalls;
777///
778/// # fn action() -> proxy_wasm::error::Result<()> {
779/// hostcalls::send_http_response(
780///     400,
781///     hostcalls::NO_HEADERS,
782///     hostcalls::NO_BODY,
783/// )?;
784/// # Ok(())
785/// # }
786/// ```
787pub fn send_http_response<K, V, B>(
788    status_code: u32,
789    headers: &[(K, V)],
790    body: Option<B>,
791) -> Result<()>
792where
793    K: AsRef<[u8]>,
794    V: AsRef<[u8]>,
795    B: AsRef<[u8]>,
796{
797    let serialized_headers = utils::serialize_map(headers);
798    let (body_ptr, body_len) = body.map_or((null(), 0), |body| {
799        (body.as_ref().as_ptr(), body.as_ref().len())
800    });
801    unsafe {
802        match proxy_send_local_response(
803            status_code,
804            null(),
805            0,
806            body_ptr,
807            body_len,
808            serialized_headers.as_ptr(),
809            serialized_headers.len(),
810            -1,
811        ) {
812            Status::Ok => Ok(()),
813            status => Err(HostCallError::new(abi::PROXY_SEND_LOCAL_RESPONSE, status).into()),
814        }
815    }
816}
817
818extern "C" {
819    fn proxy_http_call(
820        upstream_data: *const u8,
821        upstream_size: usize,
822        headers_data: *const u8,
823        headers_size: usize,
824        body_data: *const u8,
825        body_size: usize,
826        trailers_data: *const u8,
827        trailers_size: usize,
828        timeout: u32,
829        return_token: *mut u32,
830    ) -> Status;
831}
832
833/// Dispatches an HTTP call to a given upstream.
834///
835/// # Examples
836///
837/// ```no_run
838/// use std::time::Duration;
839/// # use proxy_wasm_experimental as proxy_wasm;
840/// use proxy_wasm::hostcalls;
841///
842/// # fn action() -> proxy_wasm::error::Result<()> {
843/// let request_handle = hostcalls::dispatch_http_call(
844///     "cluster_name",
845///     &vec![
846///         (":method", "POST"),
847///         (":path", "/stuff"),
848///     ],
849///     Some("hi there!"),
850///     hostcalls::NO_TRAILERS,
851///     Duration::from_secs(10),
852/// )?;
853/// # Ok(())
854/// # }
855/// ```
856pub fn dispatch_http_call<K1, V1, K2, V2, B>(
857    upstream: &str,
858    headers: &[(K1, V1)],
859    body: Option<B>,
860    trailers: &[(K2, V2)],
861    timeout: Duration,
862) -> Result<u32>
863where
864    K1: AsRef<[u8]>,
865    V1: AsRef<[u8]>,
866    K2: AsRef<[u8]>,
867    V2: AsRef<[u8]>,
868    B: AsRef<[u8]>,
869{
870    let serialized_headers = utils::serialize_map(headers);
871    let serialized_trailers = utils::serialize_map(trailers);
872    let (body_ptr, body_len) = body.map_or((null(), 0), |body| {
873        (body.as_ref().as_ptr(), body.as_ref().len())
874    });
875    let mut return_token: u32 = 0;
876    unsafe {
877        match proxy_http_call(
878            upstream.as_ptr(),
879            upstream.len(),
880            serialized_headers.as_ptr(),
881            serialized_headers.len(),
882            body_ptr,
883            body_len,
884            serialized_trailers.as_ptr(),
885            serialized_trailers.len(),
886            timeout.as_millis() as u32,
887            &mut return_token,
888        ) {
889            Status::Ok => {
890                dispatcher::register_callout(return_token);
891                Ok(return_token)
892            }
893            status => Err(HostCallError::new(abi::PROXY_HTTP_CALL, status).into()),
894        }
895    }
896}
897
898extern "C" {
899    fn proxy_set_effective_context(context_id: u32) -> Status;
900}
901
902/// Changes the effective context.
903pub fn set_effective_context(context_id: u32) -> Result<()> {
904    unsafe {
905        match proxy_set_effective_context(context_id) {
906            Status::Ok => Ok(()),
907            status => Err(HostCallError::new(abi::PROXY_SET_EFFECTIVE_CONTEXT, status).into()),
908        }
909    }
910}
911
912extern "C" {
913    fn proxy_done() -> Status;
914}
915
916/// Indicates to the host environment that Wasm VM side is done processing current context.
917pub fn done() -> Result<()> {
918    unsafe {
919        match proxy_done() {
920            Status::Ok => Ok(()),
921            status => Err(HostCallError::new(abi::PROXY_DONE, status).into()),
922        }
923    }
924}
925
926extern "C" {
927    fn proxy_define_metric(
928        metric_type: MetricType,
929        name_data: *const u8,
930        name_size: usize,
931        return_id: *mut u32,
932    ) -> Status;
933}
934
935pub fn define_metric(metric_type: MetricType, name: &str) -> Result<u32> {
936    let mut return_id: u32 = 0;
937    unsafe {
938        match proxy_define_metric(metric_type, name.as_ptr(), name.len(), &mut return_id) {
939            Status::Ok => Ok(return_id),
940            status => Err(HostCallError::new(abi::PROXY_DEFINE_METRIC, status).into()),
941        }
942    }
943}
944
945extern "C" {
946    fn proxy_get_metric(metric_id: u32, return_value: *mut u64) -> Status;
947}
948
949pub fn get_metric(metric_id: u32) -> Result<u64> {
950    let mut return_value: u64 = 0;
951    unsafe {
952        match proxy_get_metric(metric_id, &mut return_value) {
953            Status::Ok => Ok(return_value),
954            status => Err(HostCallError::new(abi::PROXY_GET_METRIC, status).into()),
955        }
956    }
957}
958
959extern "C" {
960    fn proxy_record_metric(metric_id: u32, value: u64) -> Status;
961}
962
963pub fn record_metric(metric_id: u32, value: u64) -> Result<()> {
964    unsafe {
965        match proxy_record_metric(metric_id, value) {
966            Status::Ok => Ok(()),
967            status => Err(HostCallError::new(abi::PROXY_RECORD_METRIC, status).into()),
968        }
969    }
970}
971
972extern "C" {
973    fn proxy_increment_metric(metric_id: u32, offset: i64) -> Status;
974}
975
976pub fn increment_metric(metric_id: u32, offset: i64) -> Result<()> {
977    unsafe {
978        match proxy_increment_metric(metric_id, offset) {
979            Status::Ok => Ok(()),
980            status => Err(HostCallError::new(abi::PROXY_INCREMENT_METRIC, status).into()),
981        }
982    }
983}
984
985mod utils {
986    use crate::error::Result;
987    use crate::types::ByteString;
988    use std::convert::TryFrom;
989
990    pub(super) fn serialize_property_path<P>(path: &[P]) -> Vec<u8>
991    where
992        P: AsRef<str>,
993    {
994        if path.is_empty() {
995            return Vec::new();
996        }
997        let mut size: usize = 0;
998        for part in path {
999            size += part.as_ref().len() + 1;
1000        }
1001        let mut bytes: Vec<u8> = Vec::with_capacity(size);
1002        for part in path {
1003            bytes.extend_from_slice(part.as_ref().as_bytes());
1004            bytes.push(0);
1005        }
1006        bytes.pop();
1007        bytes
1008    }
1009
1010    pub(super) fn serialize_map<K, V>(map: &[(K, V)]) -> Vec<u8>
1011    where
1012        K: AsRef<[u8]>,
1013        V: AsRef<[u8]>,
1014    {
1015        let mut size: usize = 4;
1016        for (name, value) in map {
1017            size += name.as_ref().len() + value.as_ref().len() + 10;
1018        }
1019        let mut bytes: Vec<u8> = Vec::with_capacity(size);
1020        bytes.extend_from_slice(&map.len().to_le_bytes());
1021        for (name, value) in map {
1022            bytes.extend_from_slice(&name.as_ref().len().to_le_bytes());
1023            bytes.extend_from_slice(&value.as_ref().len().to_le_bytes());
1024        }
1025        for (name, value) in map {
1026            bytes.extend_from_slice(name.as_ref());
1027            bytes.push(0);
1028            bytes.extend_from_slice(value.as_ref());
1029            bytes.push(0);
1030        }
1031        bytes
1032    }
1033
1034    pub(super) fn deserialize_map(bytes: &[u8]) -> Result<Vec<(ByteString, ByteString)>> {
1035        let mut map = Vec::new();
1036        if bytes.is_empty() {
1037            return Ok(map);
1038        }
1039        let size = u32::from_le_bytes(<[u8; 4]>::try_from(&bytes[0..4])?) as usize;
1040        let mut p = 4 + size * 8;
1041        for n in 0..size {
1042            let s = 4 + n * 8;
1043            let size = u32::from_le_bytes(<[u8; 4]>::try_from(&bytes[s..s + 4])?) as usize;
1044            let key = bytes[p..p + size].to_vec();
1045            p += size + 1;
1046            let size = u32::from_le_bytes(<[u8; 4]>::try_from(&bytes[s + 4..s + 8])?) as usize;
1047            let value = bytes[p..p + size].to_vec();
1048            p += size + 1;
1049            map.push((key.into(), value.into()));
1050        }
1051        Ok(map)
1052    }
1053}