1pub mod address;
31pub mod enumerated;
32pub mod float32;
33pub mod float64;
34pub mod group;
35pub mod identity;
36pub mod integer32;
37pub mod integer64;
38pub mod ipv4;
39pub mod ipv6;
40pub mod octetstring;
41pub mod time;
42pub mod unsigned32;
43pub mod unsigned64;
44pub mod uri;
45pub mod utf8string;
46
47use crate::dictionary::Dictionary;
48use crate::error::{Error, Result};
49use core::fmt;
50use std::io::Read;
51use std::io::Seek;
52use std::io::SeekFrom;
53use std::io::Write;
54
55pub use crate::avp::address::Address;
56pub use crate::avp::enumerated::Enumerated;
57pub use crate::avp::float32::Float32;
58pub use crate::avp::float64::Float64;
59pub use crate::avp::group::Grouped;
60pub use crate::avp::identity::Identity;
61pub use crate::avp::integer32::Integer32;
62pub use crate::avp::integer64::Integer64;
63pub use crate::avp::ipv4::IPv4;
64pub use crate::avp::ipv6::IPv6;
65pub use crate::avp::octetstring::OctetString;
66pub use crate::avp::time::Time;
67pub use crate::avp::unsigned32::Unsigned32;
68pub use crate::avp::unsigned64::Unsigned64;
69pub use crate::avp::uri::DiameterURI;
70pub use crate::avp::utf8string::UTF8String;
71pub use std::sync::Arc;
72
73pub mod flags {
74 pub const V: u8 = 0x80;
75 pub const M: u8 = 0x40;
76 pub const P: u8 = 0x20;
77}
78
79#[derive(Debug, Clone)]
80pub struct Avp {
81 header: AvpHeader,
82 value: AvpValue,
83 padding: u8,
84 dict: Arc<Dictionary>,
85}
86
87#[derive(Debug, Clone)]
88pub struct AvpHeader {
89 code: u32,
90 flags: AvpFlags,
91 length: u32,
92 vendor_id: Option<u32>,
93}
94
95#[derive(Debug, Clone)]
96pub struct AvpFlags {
97 pub vendor: bool,
98 pub mandatory: bool,
99 pub private: bool,
100}
101
102#[derive(Debug, Clone, Copy, PartialEq)]
103pub enum AvpType {
104 Unknown,
105 Address,
106 AddressIPv4,
107 AddressIPv6,
108 Identity,
109 DiameterURI,
110 Enumerated,
111 Float32,
112 Float64,
113 Grouped,
114 Integer32,
115 Integer64,
116 OctetString,
117 Time,
118 Unsigned32,
119 Unsigned64,
120 UTF8String,
121}
122
123#[derive(Debug, Clone)]
124pub enum AvpValue {
125 Address(Address),
126 AddressIPv4(IPv4),
127 AddressIPv6(IPv6),
128 Identity(Identity),
129 DiameterURI(DiameterURI),
130 Enumerated(Enumerated),
131 Float32(Float32),
132 Float64(Float64),
133 Grouped(Grouped),
134 Integer32(Integer32),
135 Integer64(Integer64),
136 OctetString(OctetString),
137 Time(Time),
138 Unsigned32(Unsigned32),
139 Unsigned64(Unsigned64),
140 UTF8String(UTF8String),
141}
142
143impl fmt::Display for AvpValue {
144 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
145 self.fmt(f, 0)
146 }
147}
148
149impl AvpValue {
150 pub fn length(&self) -> u32 {
151 match self {
152 AvpValue::Address(avp) => avp.length(),
153 AvpValue::AddressIPv4(avp) => avp.length(),
154 AvpValue::AddressIPv6(avp) => avp.length(),
155 AvpValue::Float32(avp) => avp.length(),
156 AvpValue::Float64(avp) => avp.length(),
157 AvpValue::Enumerated(avp) => avp.length(),
158 AvpValue::Integer32(avp) => avp.length(),
159 AvpValue::Integer64(avp) => avp.length(),
160 AvpValue::Unsigned32(avp) => avp.length(),
161 AvpValue::Unsigned64(avp) => avp.length(),
162 AvpValue::UTF8String(avp) => avp.length(),
163 AvpValue::OctetString(avp) => avp.length(),
164 AvpValue::Identity(avp) => avp.length(),
165 AvpValue::DiameterURI(avp) => avp.length(),
166 AvpValue::Time(avp) => avp.length(),
167 AvpValue::Grouped(avp) => avp.length(),
168 }
169 }
170
171 pub fn get_type_name(&self) -> &'static str {
172 match self {
173 AvpValue::Address(_) => "Address",
174 AvpValue::AddressIPv4(_) => "AddressIPv4",
175 AvpValue::AddressIPv6(_) => "AddressIPv6",
176 AvpValue::Float32(_) => "Float32",
177 AvpValue::Float64(_) => "Float64",
178 AvpValue::Enumerated(_) => "Enumerated",
179 AvpValue::Integer32(_) => "Integer32",
180 AvpValue::Integer64(_) => "Integer64",
181 AvpValue::Unsigned32(_) => "Unsigned32",
182 AvpValue::Unsigned64(_) => "Unsigned64",
183 AvpValue::UTF8String(_) => "UTF8String",
184 AvpValue::OctetString(_) => "OctetString",
185 AvpValue::Identity(_) => "Identity",
186 AvpValue::DiameterURI(_) => "DiameterURI",
187 AvpValue::Time(_) => "Time",
188 AvpValue::Grouped(_) => "Grouped",
189 }
190 }
191
192 fn fmt(&self, f: &mut fmt::Formatter, depth: usize) -> fmt::Result {
193 match self {
194 AvpValue::Address(avp) => write!(f, "{}", avp),
195 AvpValue::AddressIPv4(avp) => write!(f, "{}", avp),
196 AvpValue::AddressIPv6(avp) => write!(f, "{}", avp),
197 AvpValue::Float32(avp) => write!(f, "{}", avp),
198 AvpValue::Float64(avp) => write!(f, "{}", avp),
199 AvpValue::Enumerated(avp) => write!(f, "{}", avp),
200 AvpValue::Integer32(avp) => write!(f, "{}", avp),
201 AvpValue::Integer64(avp) => write!(f, "{}", avp),
202 AvpValue::Unsigned32(avp) => write!(f, "{}", avp),
203 AvpValue::Unsigned64(avp) => write!(f, "{}", avp),
204 AvpValue::UTF8String(avp) => write!(f, "{}", avp),
205 AvpValue::OctetString(avp) => write!(f, "{}", avp),
206 AvpValue::Identity(avp) => write!(f, "{}", avp),
207 AvpValue::DiameterURI(avp) => write!(f, "{}", avp),
208 AvpValue::Time(avp) => write!(f, "{}", avp),
209 AvpValue::Grouped(avp) => avp.fmt(f, depth),
210 }
211 }
212}
213
214impl From<Identity> for AvpValue {
215 fn from(identity: Identity) -> Self {
216 AvpValue::Identity(identity)
217 }
218}
219
220impl From<DiameterURI> for AvpValue {
221 fn from(uri: DiameterURI) -> Self {
222 AvpValue::DiameterURI(uri)
223 }
224}
225
226impl From<Enumerated> for AvpValue {
227 fn from(enumerated: Enumerated) -> Self {
228 AvpValue::Enumerated(enumerated)
229 }
230}
231
232impl From<Float32> for AvpValue {
233 fn from(float32: Float32) -> Self {
234 AvpValue::Float32(float32)
235 }
236}
237
238impl From<Float64> for AvpValue {
239 fn from(float64: Float64) -> Self {
240 AvpValue::Float64(float64)
241 }
242}
243
244impl From<Integer32> for AvpValue {
245 fn from(integer32: Integer32) -> Self {
246 AvpValue::Integer32(integer32)
247 }
248}
249
250impl From<Integer64> for AvpValue {
251 fn from(integer64: Integer64) -> Self {
252 AvpValue::Integer64(integer64)
253 }
254}
255
256impl From<Address> for AvpValue {
257 fn from(avp: Address) -> Self {
258 AvpValue::Address(avp)
259 }
260}
261
262impl From<IPv4> for AvpValue {
263 fn from(ipv4: IPv4) -> Self {
264 AvpValue::AddressIPv4(ipv4)
265 }
266}
267
268impl From<IPv6> for AvpValue {
269 fn from(ipv6: IPv6) -> Self {
270 AvpValue::AddressIPv6(ipv6)
271 }
272}
273
274impl From<OctetString> for AvpValue {
275 fn from(octetstring: OctetString) -> Self {
276 AvpValue::OctetString(octetstring)
277 }
278}
279
280impl From<Time> for AvpValue {
281 fn from(time: Time) -> Self {
282 AvpValue::Time(time)
283 }
284}
285
286impl From<Unsigned32> for AvpValue {
287 fn from(unsigned32: Unsigned32) -> Self {
288 AvpValue::Unsigned32(unsigned32)
289 }
290}
291
292impl From<Unsigned64> for AvpValue {
293 fn from(unsigned64: Unsigned64) -> Self {
294 AvpValue::Unsigned64(unsigned64)
295 }
296}
297
298impl From<UTF8String> for AvpValue {
299 fn from(utf8string: UTF8String) -> Self {
300 AvpValue::UTF8String(utf8string)
301 }
302}
303
304impl From<Grouped> for AvpValue {
305 fn from(group: Grouped) -> Self {
306 AvpValue::Grouped(group)
307 }
308}
309
310impl AvpHeader {
311 pub fn decode_from<R: Read>(reader: &mut R) -> Result<AvpHeader> {
312 let mut b = [0; 8];
313 reader.read_exact(&mut b)?;
314
315 let code = u32::from_be_bytes([b[0], b[1], b[2], b[3]]);
316
317 let flags = AvpFlags {
318 vendor: (b[4] & flags::V) != 0,
319 mandatory: (b[4] & flags::M) != 0,
320 private: (b[4] & flags::P) != 0,
321 };
322
323 let length = u32::from_be_bytes([0, b[5], b[6], b[7]]);
324
325 let vendor_id = if flags.vendor {
326 let mut b = [0; 4];
327 reader.read_exact(&mut b)?;
328 Some(u32::from_be_bytes([b[0], b[1], b[2], b[3]]))
329 } else {
330 None
331 };
332
333 Ok(AvpHeader {
334 code,
335 flags,
336 length,
337 vendor_id,
338 })
339 }
340
341 pub fn encode_to<W: Write>(&self, writer: &mut W) -> Result<()> {
342 writer.write_all(&self.code.to_be_bytes())?;
344
345 let mut flags: u8 = 0;
347 if self.flags.vendor {
348 flags |= flags::V;
349 }
350 if self.flags.mandatory {
351 flags |= flags::M;
352 }
353 if self.flags.private {
354 flags |= flags::P;
355 }
356 writer.write_all(&[flags])?;
357
358 let length_bytes = &self.length.to_be_bytes()[1..4];
360 writer.write_all(length_bytes)?;
361
362 if let Some(vendor_id) = self.vendor_id {
364 writer.write_all(&vendor_id.to_be_bytes())?;
365 }
366
367 Ok(())
368 }
369}
370
371impl Avp {
372 pub fn new(
373 code: u32,
374 vendor_id: Option<u32>,
375 flags: u8,
376 value: AvpValue,
377 dict: Arc<Dictionary>,
378 ) -> Avp {
379 let header_length = if vendor_id.is_some() { 12 } else { 8 };
380 let padding = Avp::pad_to_32_bits(value.length());
381 let header = AvpHeader {
382 code,
383 flags: AvpFlags {
384 vendor: if vendor_id.is_some() { true } else { false },
385 mandatory: (flags & flags::M) != 0,
386 private: (flags & flags::P) != 0,
387 },
388 length: header_length + value.length(),
389 vendor_id,
390 };
391 return Avp {
392 header,
393 value,
394 padding,
395 dict,
396 };
397 }
398
399 pub fn from_name(avp_name: &str, value: AvpValue, dict: Arc<Dictionary>) -> Result<Avp> {
400 let avp_def = dict
401 .get_avp_by_name(avp_name)
402 .ok_or(Error::UnknownAvpName(avp_name.to_string()))?;
403
404 let flags = if avp_def.m_flag { flags::M } else { 0 };
405 Ok(Avp::new(
406 avp_def.code,
407 avp_def.vendor_id,
408 flags,
409 value,
410 dict,
411 ))
412 }
413
414 pub fn get_code(&self) -> u32 {
415 self.header.code
416 }
417
418 pub fn get_flags(&self) -> &AvpFlags {
419 &self.header.flags
420 }
421
422 pub fn get_vendor_id(&self) -> Option<u32> {
423 self.header.vendor_id
424 }
425
426 pub fn get_length(&self) -> u32 {
427 self.header.length
428 }
429
430 pub fn get_padding(&self) -> u8 {
431 self.padding
432 }
433
434 pub fn get_value(&self) -> &AvpValue {
435 &self.value
436 }
437
438 pub fn decode_from<R: Read + Seek>(reader: &mut R, dict: Arc<Dictionary>) -> Result<Avp> {
439 let header = AvpHeader::decode_from(reader)?;
440
441 let header_length = if header.flags.vendor { 12 } else { 8 };
442 let value_length = header.length - header_length;
443
444 let avp_type = dict
445 .get_avp_type(header.code, header.vendor_id)
446 .unwrap_or(&AvpType::Unknown);
447
448 let value = match avp_type {
449 AvpType::Address => {
450 AvpValue::Address(Address::decode_from(reader, value_length as usize)?)
451 }
452 AvpType::AddressIPv4 => AvpValue::AddressIPv4(IPv4::decode_from(reader)?),
453 AvpType::AddressIPv6 => AvpValue::AddressIPv6(IPv6::decode_from(reader)?),
454 AvpType::Float32 => AvpValue::Float32(Float32::decode_from(reader)?),
455 AvpType::Float64 => AvpValue::Float64(Float64::decode_from(reader)?),
456 AvpType::Enumerated => AvpValue::Enumerated(Enumerated::decode_from(reader)?),
457 AvpType::Integer32 => AvpValue::Integer32(Integer32::decode_from(reader)?),
458 AvpType::Integer64 => AvpValue::Integer64(Integer64::decode_from(reader)?),
459 AvpType::Unsigned32 => AvpValue::Unsigned32(Unsigned32::decode_from(reader)?),
460 AvpType::Unsigned64 => AvpValue::Unsigned64(Unsigned64::decode_from(reader)?),
461 AvpType::UTF8String => {
462 AvpValue::UTF8String(UTF8String::decode_from(reader, value_length as usize)?)
463 }
464 AvpType::OctetString => {
465 AvpValue::OctetString(OctetString::decode_from(reader, value_length as usize)?)
466 }
467 AvpType::Identity => {
468 AvpValue::Identity(Identity::decode_from(reader, value_length as usize)?)
469 }
470 AvpType::DiameterURI => {
471 AvpValue::DiameterURI(DiameterURI::decode_from(reader, value_length as usize)?)
472 }
473 AvpType::Time => AvpValue::Time(Time::decode_from(reader)?),
474 AvpType::Grouped => AvpValue::Grouped(Grouped::decode_from(
475 reader,
476 value_length as usize,
477 Arc::clone(&dict),
478 )?),
479 AvpType::Unknown => return Err(Error::UnknownAvpCode(header.code)),
480 };
481
482 let padding = Avp::pad_to_32_bits(value_length);
484 if padding > 0 {
485 reader.seek(SeekFrom::Current(padding as i64))?;
486 }
487
488 return Ok(Avp {
489 header,
490 value,
491 padding,
492 dict,
493 });
494 }
495
496 pub fn encode_to<W: Write>(&self, writer: &mut W) -> Result<()> {
497 self.header.encode_to(writer)?;
498
499 let _ = match &self.value {
500 AvpValue::Address(avp) => avp.encode_to(writer),
501 AvpValue::AddressIPv4(avp) => avp.encode_to(writer),
502 AvpValue::AddressIPv6(avp) => avp.encode_to(writer),
503 AvpValue::Float32(avp) => avp.encode_to(writer),
504 AvpValue::Float64(avp) => avp.encode_to(writer),
505 AvpValue::Enumerated(avp) => avp.encode_to(writer),
506 AvpValue::Integer32(avp) => avp.encode_to(writer),
507 AvpValue::Integer64(avp) => avp.encode_to(writer),
508 AvpValue::Unsigned32(avp) => avp.encode_to(writer),
509 AvpValue::Unsigned64(avp) => avp.encode_to(writer),
510 AvpValue::UTF8String(avp) => avp.encode_to(writer),
511 AvpValue::OctetString(avp) => avp.encode_to(writer),
512 AvpValue::Identity(avp) => avp.encode_to(writer),
513 AvpValue::DiameterURI(avp) => avp.encode_to(writer),
514 AvpValue::Time(avp) => avp.encode_to(writer),
515 AvpValue::Grouped(avp) => avp.encode_to(writer),
516 };
517
518 for _ in 0..self.padding {
520 writer.write_all(&[0])?;
521 }
522
523 Ok(())
524 }
525
526 fn pad_to_32_bits(length: u32) -> u8 {
527 ((4 - (length & 0b11)) % 4) as u8
528 }
529
530 pub fn get_address(&self) -> Option<&Address> {
531 match &self.value {
532 AvpValue::Address(avp) => Some(avp),
533 _ => None,
534 }
535 }
536
537 pub fn get_address_ipv4(&self) -> Option<&IPv4> {
538 match &self.value {
539 AvpValue::AddressIPv4(avp) => Some(avp),
540 _ => None,
541 }
542 }
543
544 pub fn get_address_ipv6(&self) -> Option<&IPv6> {
545 match &self.value {
546 AvpValue::AddressIPv6(avp) => Some(avp),
547 _ => None,
548 }
549 }
550
551 pub fn get_identity(&self) -> Option<&Identity> {
552 match &self.value {
553 AvpValue::Identity(avp) => Some(avp),
554 _ => None,
555 }
556 }
557
558 pub fn get_diameter_uri(&self) -> Option<&DiameterURI> {
559 match &self.value {
560 AvpValue::DiameterURI(avp) => Some(avp),
561 _ => None,
562 }
563 }
564
565 pub fn get_enumerated(&self) -> Option<&Enumerated> {
566 match &self.value {
567 AvpValue::Enumerated(avp) => Some(avp),
568 _ => None,
569 }
570 }
571
572 pub fn get_integer32(&self) -> Option<i32> {
573 match &self.value {
574 AvpValue::Integer32(avp) => Some(avp.value()),
575 _ => None,
576 }
577 }
578
579 pub fn get_integer64(&self) -> Option<i64> {
580 match &self.value {
581 AvpValue::Integer64(avp) => Some(avp.value()),
582 _ => None,
583 }
584 }
585
586 pub fn get_unsigned32(&self) -> Option<u32> {
587 match &self.value {
588 AvpValue::Unsigned32(avp) => Some(avp.value()),
589 _ => None,
590 }
591 }
592
593 pub fn get_unsigned64(&self) -> Option<u64> {
594 match &self.value {
595 AvpValue::Unsigned64(avp) => Some(avp.value()),
596 _ => None,
597 }
598 }
599
600 pub fn get_utf8string(&self) -> Option<&UTF8String> {
601 match &self.value {
602 AvpValue::UTF8String(avp) => Some(avp),
603 _ => None,
604 }
605 }
606
607 pub fn get_octetstring(&self) -> Option<&OctetString> {
608 match &self.value {
609 AvpValue::OctetString(avp) => Some(avp),
610 _ => None,
611 }
612 }
613
614 pub fn get_time(&self) -> Option<&Time> {
615 match &self.value {
616 AvpValue::Time(avp) => Some(avp),
617 _ => None,
618 }
619 }
620
621 pub fn get_float32(&self) -> Option<f32> {
622 match &self.value {
623 AvpValue::Float32(avp) => Some(avp.value()),
624 _ => None,
625 }
626 }
627
628 pub fn get_float64(&self) -> Option<f64> {
629 match &self.value {
630 AvpValue::Float64(avp) => Some(avp.value()),
631 _ => None,
632 }
633 }
634
635 pub fn get_grouped(&self) -> Option<&Grouped> {
636 match &self.value {
637 AvpValue::Grouped(avp) => Some(avp),
638 _ => None,
639 }
640 }
641
642 pub fn fmt(&self, f: &mut fmt::Formatter<'_>, depth: usize) -> fmt::Result {
643 let indent = " ".repeat(depth.max(0));
644
645 let avp_name = self
646 .dict
647 .get_avp_name(self.get_code() as u32, self.get_vendor_id())
648 .unwrap_or("Unknown");
649
650 let avp_name = format!("{}{}", indent, avp_name);
651
652 let vendor_id = match self.get_vendor_id() {
653 Some(v) => v.to_string(),
654 None => "".to_string(),
655 };
656
657 write!(
658 f,
659 " {:<40} {:>8} {:>5} {} {} {} {:<16} ",
660 avp_name,
661 vendor_id,
662 self.get_code(),
663 get_bool_unicode(self.get_flags().vendor),
664 get_bool_unicode(self.get_flags().mandatory),
665 get_bool_unicode(self.get_flags().private),
666 self.get_value().get_type_name(),
667 )?;
668
669 self.get_value().fmt(f, depth)
670 }
671}
672
673fn get_bool_unicode(v: bool) -> &'static str {
674 if v {
675 "✓"
676 } else {
677 "✗"
678 }
679}
680
681impl fmt::Display for Avp {
682 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
683 self.fmt(f, 0)
684 }
685}
686
687#[macro_export]
688macro_rules! avp {
689 ($code:expr, $vendor_id:expr, $flags:expr, $value:expr, $dict:expr $(,)?) => {
690 Avp::new($code, $vendor_id, $flags, $value.into(), $dict)
691 };
692 ($name:expr, $value:expr, $dict:expr $(,)?) => {
693 Avp::from_name($name, $value.into(), $dict)
694 };
695}
696
697#[cfg(test)]
698mod tests {
699 use crate::dictionary;
700 use flags::M;
701
702 use super::*;
703 use std::io::Cursor;
704
705 #[test]
706 fn test_decode_encode_header() {
707 let data = [
708 0x00, 0x00, 0x00, 0x64, 0x40, 0x00, 0x00, 0x0C, ];
711
712 let mut cursor = Cursor::new(&data);
713 let header = AvpHeader::decode_from(&mut cursor).unwrap();
714
715 assert_eq!(header.code, 100);
716 assert_eq!(header.length, 12);
717 assert_eq!(header.flags.vendor, false);
718 assert_eq!(header.flags.mandatory, true);
719 assert_eq!(header.flags.private, false);
720 assert_eq!(header.vendor_id, None);
721
722 let mut encoded = Vec::new();
723 header.encode_to(&mut encoded).unwrap();
724 assert_eq!(encoded, data);
725 }
726
727 #[test]
728 fn test_decode_encode_header_with_vendor() {
729 let data = [
730 0x00, 0x00, 0x00, 0x64, 0x80, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0xC8, ];
734
735 let mut cursor = Cursor::new(&data);
736 let header = AvpHeader::decode_from(&mut cursor).unwrap();
737
738 assert_eq!(header.code, 100);
739 assert_eq!(header.length, 12);
740 assert_eq!(header.flags.vendor, true);
741 assert_eq!(header.flags.mandatory, false);
742 assert_eq!(header.flags.private, false);
743 assert_eq!(header.vendor_id, Some(200));
744
745 let mut encoded = Vec::new();
746 header.encode_to(&mut encoded).unwrap();
747 assert_eq!(encoded, data);
748 }
749
750 #[test]
751 fn test_avp_macro() {
752 let dict = Dictionary::new(&[&dictionary::DEFAULT_DICT_XML]);
753 let dict = Arc::new(dict);
754
755 let avp = avp!(
756 264,
757 None,
758 M,
759 Identity::new("host.example.com"),
760 Arc::clone(&dict)
761 );
762 assert_eq!(avp.get_code(), 264);
763 assert_eq!(avp.get_flags().mandatory, true);
764 assert_eq!(avp.get_flags().private, false);
765 assert_eq!(avp.get_flags().vendor, false);
766 assert_eq!(avp.get_vendor_id(), None);
767 assert_eq!(avp.get_identity().unwrap().value(), "host.example.com");
768
769 let avp = avp!(
770 "Session-Id",
771 UTF8String::new("session-id"),
772 Arc::clone(&dict)
773 )
774 .unwrap();
775 assert_eq!(avp.get_code(), 263);
776 assert_eq!(avp.get_flags().mandatory, true);
777 assert_eq!(avp.get_flags().private, false);
778 assert_eq!(avp.get_flags().vendor, false);
779 assert_eq!(avp.get_vendor_id(), None);
780 assert_eq!(avp.get_utf8string().unwrap().value(), "session-id");
781 }
782}