1use aws_smithy_types::date_time::Format;
9use aws_smithy_types::primitive::Parse;
10use aws_smithy_types::DateTime;
11use http_02x::header::{HeaderMap, HeaderName, HeaderValue};
12use std::borrow::Cow;
13use std::error::Error;
14use std::fmt;
15use std::str::FromStr;
16
17#[derive(Debug)]
19pub struct ParseError {
20 message: Cow<'static, str>,
21 source: Option<Box<dyn Error + Send + Sync + 'static>>,
22}
23
24impl ParseError {
25 pub fn new(message: impl Into<Cow<'static, str>>) -> Self {
27 Self {
28 message: message.into(),
29 source: None,
30 }
31 }
32
33 pub fn with_source(self, source: impl Into<Box<dyn Error + Send + Sync + 'static>>) -> Self {
35 Self {
36 source: Some(source.into()),
37 ..self
38 }
39 }
40}
41
42impl fmt::Display for ParseError {
43 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44 write!(f, "output failed to parse in headers: {}", self.message)
45 }
46}
47
48impl Error for ParseError {
49 fn source(&self) -> Option<&(dyn Error + 'static)> {
50 self.source.as_ref().map(|err| err.as_ref() as _)
51 }
52}
53
54pub fn many_dates<'a>(
59 values: impl Iterator<Item = &'a str>,
60 format: Format,
61) -> Result<Vec<DateTime>, ParseError> {
62 let mut out = vec![];
63 for header in values {
64 let mut header = header;
65 while !header.is_empty() {
66 let (v, next) = DateTime::read(header, format, ',').map_err(|err| {
67 ParseError::new(format!("header could not be parsed as date: {}", err))
68 })?;
69 out.push(v);
70 header = next;
71 }
72 }
73 Ok(out)
74}
75
76pub fn headers_for_prefix<'a>(
79 header_names: impl Iterator<Item = &'a str>,
80 key: &'a str,
81) -> impl Iterator<Item = (&'a str, &'a str)> {
82 let lower_key = key.to_ascii_lowercase();
83 header_names
84 .filter(move |k| k.starts_with(&lower_key))
85 .map(move |k| (&k[key.len()..], k))
86}
87
88pub fn read_many_from_str<'a, T: FromStr>(
90 values: impl Iterator<Item = &'a str>,
91) -> Result<Vec<T>, ParseError>
92where
93 T::Err: Error + Send + Sync + 'static,
94{
95 read_many(values, |v: &str| {
96 v.parse().map_err(|err| {
97 ParseError::new("failed during `FromString` conversion").with_source(err)
98 })
99 })
100}
101
102pub fn read_many_primitive<'a, T: Parse>(
104 values: impl Iterator<Item = &'a str>,
105) -> Result<Vec<T>, ParseError> {
106 read_many(values, |v: &str| {
107 T::parse_smithy_primitive(v)
108 .map_err(|err| ParseError::new("failed reading a list of primitives").with_source(err))
109 })
110}
111
112fn read_many<'a, T>(
114 values: impl Iterator<Item = &'a str>,
115 f: impl Fn(&str) -> Result<T, ParseError>,
116) -> Result<Vec<T>, ParseError> {
117 let mut out = vec![];
118 for header in values {
119 let mut header = header.as_bytes();
120 while !header.is_empty() {
121 let (v, next) = read_one(header, &f)?;
122 out.push(v);
123 header = next;
124 }
125 }
126 Ok(out)
127}
128
129pub fn one_or_none<'a, T: FromStr>(
133 mut values: impl Iterator<Item = &'a str>,
134) -> Result<Option<T>, ParseError>
135where
136 T::Err: Error + Send + Sync + 'static,
137{
138 let first = match values.next() {
139 Some(v) => v,
140 None => return Ok(None),
141 };
142 match values.next() {
143 None => T::from_str(first.trim())
144 .map_err(|err| ParseError::new("failed to parse string").with_source(err))
145 .map(Some),
146 Some(_) => Err(ParseError::new(
147 "expected a single value but found multiple",
148 )),
149 }
150}
151
152pub fn set_request_header_if_absent<V>(
154 request: http_02x::request::Builder,
155 key: HeaderName,
156 value: V,
157) -> http_02x::request::Builder
158where
159 HeaderValue: TryFrom<V>,
160 <HeaderValue as TryFrom<V>>::Error: Into<http_02x::Error>,
161{
162 if !request
163 .headers_ref()
164 .map(|map| map.contains_key(&key))
165 .unwrap_or(false)
166 {
167 request.header(key, value)
168 } else {
169 request
170 }
171}
172
173pub fn set_response_header_if_absent<V>(
175 response: http_02x::response::Builder,
176 key: HeaderName,
177 value: V,
178) -> http_02x::response::Builder
179where
180 HeaderValue: TryFrom<V>,
181 <HeaderValue as TryFrom<V>>::Error: Into<http_02x::Error>,
182{
183 if !response
184 .headers_ref()
185 .map(|map| map.contains_key(&key))
186 .unwrap_or(false)
187 {
188 response.header(key, value)
189 } else {
190 response
191 }
192}
193
194mod parse_multi_header {
198 use super::ParseError;
199 use std::borrow::Cow;
200
201 fn trim(s: Cow<'_, str>) -> Cow<'_, str> {
202 match s {
203 Cow::Owned(s) => Cow::Owned(s.trim().into()),
204 Cow::Borrowed(s) => Cow::Borrowed(s.trim()),
205 }
206 }
207
208 fn replace<'a>(value: Cow<'a, str>, pattern: &str, replacement: &str) -> Cow<'a, str> {
209 if value.contains(pattern) {
210 Cow::Owned(value.replace(pattern, replacement))
211 } else {
212 value
213 }
214 }
215
216 pub(crate) fn read_value(input: &[u8]) -> Result<(Cow<'_, str>, &[u8]), ParseError> {
220 for (index, &byte) in input.iter().enumerate() {
221 let current_slice = &input[index..];
222 match byte {
223 b' ' | b'\t' => { }
224 b'"' => return read_quoted_value(¤t_slice[1..]),
225 _ => {
226 let (value, rest) = read_unquoted_value(current_slice)?;
227 return Ok((trim(value), rest));
228 }
229 }
230 }
231
232 Ok((Cow::Borrowed(""), &[]))
234 }
235
236 fn read_unquoted_value(input: &[u8]) -> Result<(Cow<'_, str>, &[u8]), ParseError> {
237 let next_delim = input.iter().position(|&b| b == b',').unwrap_or(input.len());
238 let (first, next) = input.split_at(next_delim);
239 let first = std::str::from_utf8(first)
240 .map_err(|_| ParseError::new("header was not valid utf-8"))?;
241 Ok((Cow::Borrowed(first), then_comma(next).unwrap()))
242 }
243
244 fn read_quoted_value(input: &[u8]) -> Result<(Cow<'_, str>, &[u8]), ParseError> {
247 for index in 0..input.len() {
248 match input[index] {
249 b'"' if index == 0 || input[index - 1] != b'\\' => {
250 let mut inner = Cow::Borrowed(
251 std::str::from_utf8(&input[0..index])
252 .map_err(|_| ParseError::new("header was not valid utf-8"))?,
253 );
254 inner = replace(inner, "\\\"", "\"");
255 inner = replace(inner, "\\\\", "\\");
256 let rest = then_comma(&input[(index + 1)..])?;
257 return Ok((inner, rest));
258 }
259 _ => {}
260 }
261 }
262 Err(ParseError::new(
263 "header value had quoted value without end quote",
264 ))
265 }
266
267 fn then_comma(s: &[u8]) -> Result<&[u8], ParseError> {
268 if s.is_empty() {
269 Ok(s)
270 } else if s.starts_with(b",") {
271 Ok(&s[1..])
272 } else {
273 Err(ParseError::new("expected delimiter `,`"))
274 }
275 }
276}
277
278fn read_one<'a, T>(
280 s: &'a [u8],
281 f: &impl Fn(&str) -> Result<T, ParseError>,
282) -> Result<(T, &'a [u8]), ParseError> {
283 let (value, rest) = parse_multi_header::read_value(s)?;
284 Ok((f(&value)?, rest))
285}
286
287pub fn quote_header_value<'a>(value: impl Into<Cow<'a, str>>) -> Cow<'a, str> {
289 let value = value.into();
290 if value.trim().len() != value.len()
291 || value.contains('"')
292 || value.contains(',')
293 || value.contains('(')
294 || value.contains(')')
295 {
296 Cow::Owned(format!(
297 "\"{}\"",
298 value.replace('\\', "\\\\").replace('"', "\\\"")
299 ))
300 } else {
301 value
302 }
303}
304
305pub fn append_merge_header_maps(
308 mut lhs: HeaderMap<HeaderValue>,
309 rhs: HeaderMap<HeaderValue>,
310) -> HeaderMap<HeaderValue> {
311 let mut last_header_name_seen = None;
312 for (header_name, header_value) in rhs.into_iter() {
313 match (&mut last_header_name_seen, header_name) {
318 (_, Some(header_name)) => {
319 lhs.append(header_name.clone(), header_value);
320 last_header_name_seen = Some(header_name);
321 }
322 (Some(header_name), None) => {
323 lhs.append(header_name.clone(), header_value);
324 }
325 (None, None) => unreachable!(),
326 };
327 }
328
329 lhs
330}
331
332#[cfg(test)]
333mod test {
334 use super::quote_header_value;
335 use crate::header::{
336 append_merge_header_maps, headers_for_prefix, many_dates, read_many_from_str,
337 read_many_primitive, set_request_header_if_absent, set_response_header_if_absent,
338 ParseError,
339 };
340 use aws_smithy_runtime_api::http::Request;
341 use aws_smithy_types::error::display::DisplayErrorContext;
342 use aws_smithy_types::{date_time::Format, DateTime};
343 use http_02x::header::{HeaderMap, HeaderName, HeaderValue};
344 use std::collections::HashMap;
345
346 #[test]
347 fn put_on_request_if_absent() {
348 let builder = http_02x::Request::builder().header("foo", "bar");
349 let builder = set_request_header_if_absent(builder, HeaderName::from_static("foo"), "baz");
350 let builder =
351 set_request_header_if_absent(builder, HeaderName::from_static("other"), "value");
352 let req = builder.body(()).expect("valid request");
353 assert_eq!(
354 req.headers().get_all("foo").iter().collect::<Vec<_>>(),
355 vec!["bar"]
356 );
357 assert_eq!(
358 req.headers().get_all("other").iter().collect::<Vec<_>>(),
359 vec!["value"]
360 );
361 }
362
363 #[test]
364 fn put_on_response_if_absent() {
365 let builder = http_02x::Response::builder().header("foo", "bar");
366 let builder = set_response_header_if_absent(builder, HeaderName::from_static("foo"), "baz");
367 let builder =
368 set_response_header_if_absent(builder, HeaderName::from_static("other"), "value");
369 let response = builder.body(()).expect("valid response");
370 assert_eq!(
371 response.headers().get_all("foo").iter().collect::<Vec<_>>(),
372 vec!["bar"]
373 );
374 assert_eq!(
375 response
376 .headers()
377 .get_all("other")
378 .iter()
379 .collect::<Vec<_>>(),
380 vec!["value"]
381 );
382 }
383
384 #[test]
385 fn parse_floats() {
386 let test_request = http_02x::Request::builder()
387 .header("X-Float-Multi", "0.0,Infinity,-Infinity,5555.5")
388 .header("X-Float-Error", "notafloat")
389 .body(())
390 .unwrap();
391 assert_eq!(
392 read_many_primitive::<f32>(
393 test_request
394 .headers()
395 .get_all("X-Float-Multi")
396 .iter()
397 .map(|v| v.to_str().unwrap())
398 )
399 .expect("valid"),
400 vec![0.0, f32::INFINITY, f32::NEG_INFINITY, 5555.5]
401 );
402 let message = format!(
403 "{}",
404 DisplayErrorContext(
405 read_many_primitive::<f32>(
406 test_request
407 .headers()
408 .get_all("X-Float-Error")
409 .iter()
410 .map(|v| v.to_str().unwrap())
411 )
412 .expect_err("invalid")
413 )
414 );
415 let expected = "output failed to parse in headers: failed reading a list of primitives: failed to parse input as f32";
416 assert!(
417 message.starts_with(expected),
418 "expected '{message}' to start with '{expected}'"
419 );
420 }
421
422 #[test]
423 fn test_many_dates() {
424 let test_request = http_02x::Request::builder()
425 .header("Empty", "")
426 .header("SingleHttpDate", "Wed, 21 Oct 2015 07:28:00 GMT")
427 .header(
428 "MultipleHttpDates",
429 "Wed, 21 Oct 2015 07:28:00 GMT,Thu, 22 Oct 2015 07:28:00 GMT",
430 )
431 .header("SingleEpochSeconds", "1234.5678")
432 .header("MultipleEpochSeconds", "1234.5678,9012.3456")
433 .body(())
434 .unwrap();
435 let read = |name: &str, format: Format| {
436 many_dates(
437 test_request
438 .headers()
439 .get_all(name)
440 .iter()
441 .map(|v| v.to_str().unwrap()),
442 format,
443 )
444 };
445 let read_valid = |name: &str, format: Format| read(name, format).expect("valid");
446 assert_eq!(
447 read_valid("Empty", Format::DateTime),
448 Vec::<DateTime>::new()
449 );
450 assert_eq!(
451 read_valid("SingleHttpDate", Format::HttpDate),
452 vec![DateTime::from_secs_and_nanos(1445412480, 0)]
453 );
454 assert_eq!(
455 read_valid("MultipleHttpDates", Format::HttpDate),
456 vec![
457 DateTime::from_secs_and_nanos(1445412480, 0),
458 DateTime::from_secs_and_nanos(1445498880, 0)
459 ]
460 );
461 assert_eq!(
462 read_valid("SingleEpochSeconds", Format::EpochSeconds),
463 vec![DateTime::from_secs_and_nanos(1234, 567_800_000)]
464 );
465 assert_eq!(
466 read_valid("MultipleEpochSeconds", Format::EpochSeconds),
467 vec![
468 DateTime::from_secs_and_nanos(1234, 567_800_000),
469 DateTime::from_secs_and_nanos(9012, 345_600_000)
470 ]
471 );
472 }
473
474 #[test]
475 fn read_many_strings() {
476 let test_request = http_02x::Request::builder()
477 .header("Empty", "")
478 .header("Foo", " foo")
479 .header("FooTrailing", "foo ")
480 .header("FooInQuotes", "\" foo \"")
481 .header("CommaInQuotes", "\"foo,bar\",baz")
482 .header("CommaInQuotesTrailing", "\"foo,bar\",baz ")
483 .header("QuoteInQuotes", "\"foo\\\",bar\",\"\\\"asdf\\\"\",baz")
484 .header(
485 "QuoteInQuotesWithSpaces",
486 "\"foo\\\",bar\", \"\\\"asdf\\\"\", baz",
487 )
488 .header("JunkFollowingQuotes", "\"\\\"asdf\\\"\"baz")
489 .header("EmptyQuotes", "\"\",baz")
490 .header("EscapedSlashesInQuotes", "foo, \"(foo\\\\bar)\"")
491 .body(())
492 .unwrap();
493 let read = |name: &str| {
494 read_many_from_str::<String>(
495 test_request
496 .headers()
497 .get_all(name)
498 .iter()
499 .map(|v| v.to_str().unwrap()),
500 )
501 };
502 let read_valid = |name: &str| read(name).expect("valid");
503 assert_eq!(read_valid("Empty"), Vec::<String>::new());
504 assert_eq!(read_valid("Foo"), vec!["foo"]);
505 assert_eq!(read_valid("FooTrailing"), vec!["foo"]);
506 assert_eq!(read_valid("FooInQuotes"), vec![" foo "]);
507 assert_eq!(read_valid("CommaInQuotes"), vec!["foo,bar", "baz"]);
508 assert_eq!(read_valid("CommaInQuotesTrailing"), vec!["foo,bar", "baz"]);
509 assert_eq!(
510 read_valid("QuoteInQuotes"),
511 vec!["foo\",bar", "\"asdf\"", "baz"]
512 );
513 assert_eq!(
514 read_valid("QuoteInQuotesWithSpaces"),
515 vec!["foo\",bar", "\"asdf\"", "baz"]
516 );
517 assert!(read("JunkFollowingQuotes").is_err());
518 assert_eq!(read_valid("EmptyQuotes"), vec!["", "baz"]);
519 assert_eq!(
520 read_valid("EscapedSlashesInQuotes"),
521 vec!["foo", "(foo\\bar)"]
522 );
523 }
524
525 #[test]
526 fn read_many_bools() {
527 let test_request = http_02x::Request::builder()
528 .header("X-Bool-Multi", "true,false")
529 .header("X-Bool-Multi", "true")
530 .header("X-Bool", "true")
531 .header("X-Bool-Invalid", "truth,falsy")
532 .header("X-Bool-Single", "true,false,true,true")
533 .header("X-Bool-Quoted", "true,\"false\",true,true")
534 .body(())
535 .unwrap();
536 assert_eq!(
537 read_many_primitive::<bool>(
538 test_request
539 .headers()
540 .get_all("X-Bool-Multi")
541 .iter()
542 .map(|v| v.to_str().unwrap())
543 )
544 .expect("valid"),
545 vec![true, false, true]
546 );
547
548 assert_eq!(
549 read_many_primitive::<bool>(
550 test_request
551 .headers()
552 .get_all("X-Bool")
553 .iter()
554 .map(|v| v.to_str().unwrap())
555 )
556 .unwrap(),
557 vec![true]
558 );
559 assert_eq!(
560 read_many_primitive::<bool>(
561 test_request
562 .headers()
563 .get_all("X-Bool-Single")
564 .iter()
565 .map(|v| v.to_str().unwrap())
566 )
567 .unwrap(),
568 vec![true, false, true, true]
569 );
570 assert_eq!(
571 read_many_primitive::<bool>(
572 test_request
573 .headers()
574 .get_all("X-Bool-Quoted")
575 .iter()
576 .map(|v| v.to_str().unwrap())
577 )
578 .unwrap(),
579 vec![true, false, true, true]
580 );
581 read_many_primitive::<bool>(
582 test_request
583 .headers()
584 .get_all("X-Bool-Invalid")
585 .iter()
586 .map(|v| v.to_str().unwrap()),
587 )
588 .expect_err("invalid");
589 }
590
591 #[test]
592 fn check_read_many_i16() {
593 let test_request = http_02x::Request::builder()
594 .header("X-Multi", "123,456")
595 .header("X-Multi", "789")
596 .header("X-Num", "777")
597 .header("X-Num-Invalid", "12ef3")
598 .header("X-Num-Single", "1,2,3,-4,5")
599 .header("X-Num-Quoted", "1, \"2\",3,\"-4\",5")
600 .body(())
601 .unwrap();
602 assert_eq!(
603 read_many_primitive::<i16>(
604 test_request
605 .headers()
606 .get_all("X-Multi")
607 .iter()
608 .map(|v| v.to_str().unwrap())
609 )
610 .expect("valid"),
611 vec![123, 456, 789]
612 );
613
614 assert_eq!(
615 read_many_primitive::<i16>(
616 test_request
617 .headers()
618 .get_all("X-Num")
619 .iter()
620 .map(|v| v.to_str().unwrap())
621 )
622 .unwrap(),
623 vec![777]
624 );
625 assert_eq!(
626 read_many_primitive::<i16>(
627 test_request
628 .headers()
629 .get_all("X-Num-Single")
630 .iter()
631 .map(|v| v.to_str().unwrap())
632 )
633 .unwrap(),
634 vec![1, 2, 3, -4, 5]
635 );
636 assert_eq!(
637 read_many_primitive::<i16>(
638 test_request
639 .headers()
640 .get_all("X-Num-Quoted")
641 .iter()
642 .map(|v| v.to_str().unwrap())
643 )
644 .unwrap(),
645 vec![1, 2, 3, -4, 5]
646 );
647 read_many_primitive::<i16>(
648 test_request
649 .headers()
650 .get_all("X-Num-Invalid")
651 .iter()
652 .map(|v| v.to_str().unwrap()),
653 )
654 .expect_err("invalid");
655 }
656
657 #[test]
658 fn test_prefix_headers() {
659 let test_request = Request::try_from(
660 http_02x::Request::builder()
661 .header("X-Prefix-A", "123,456")
662 .header("X-Prefix-B", "789")
663 .header("X-Prefix-C", "777")
664 .header("X-Prefix-C", "777")
665 .body(())
666 .unwrap(),
667 )
668 .unwrap();
669 let resp: Result<HashMap<String, Vec<i16>>, ParseError> =
670 headers_for_prefix(test_request.headers().iter().map(|h| h.0), "X-Prefix-")
671 .map(|(key, header_name)| {
672 let values = test_request.headers().get_all(header_name);
673 read_many_primitive(values).map(|v| (key.to_string(), v))
674 })
675 .collect();
676 let resp = resp.expect("valid");
677 assert_eq!(resp.get("a"), Some(&vec![123_i16, 456_i16]));
678 }
679
680 #[test]
681 fn test_quote_header_value() {
682 assert_eq!("", "e_header_value(""));
683 assert_eq!("foo", "e_header_value("foo"));
684 assert_eq!("\" foo\"", "e_header_value(" foo"));
685 assert_eq!("foo bar", "e_header_value("foo bar"));
686 assert_eq!("\"foo,bar\"", "e_header_value("foo,bar"));
687 assert_eq!("\",\"", "e_header_value(","));
688 assert_eq!("\"\\\"foo\\\"\"", "e_header_value("\"foo\""));
689 assert_eq!("\"\\\"f\\\\oo\\\"\"", "e_header_value("\"f\\oo\""));
690 assert_eq!("\"(\"", "e_header_value("("));
691 assert_eq!("\")\"", "e_header_value(")"));
692 }
693
694 #[test]
695 fn test_append_merge_header_maps_with_shared_key() {
696 let header_name = HeaderName::from_static("some_key");
697 let left_header_value = HeaderValue::from_static("lhs value");
698 let right_header_value = HeaderValue::from_static("rhs value");
699
700 let mut left_hand_side_headers = HeaderMap::new();
701 left_hand_side_headers.insert(header_name.clone(), left_header_value.clone());
702
703 let mut right_hand_side_headers = HeaderMap::new();
704 right_hand_side_headers.insert(header_name.clone(), right_header_value.clone());
705
706 let merged_header_map =
707 append_merge_header_maps(left_hand_side_headers, right_hand_side_headers);
708 let actual_merged_values: Vec<_> =
709 merged_header_map.get_all(header_name).into_iter().collect();
710
711 let expected_merged_values = vec![left_header_value, right_header_value];
712
713 assert_eq!(actual_merged_values, expected_merged_values);
714 }
715
716 #[test]
717 fn test_append_merge_header_maps_with_multiple_values_in_left_hand_map() {
718 let header_name = HeaderName::from_static("some_key");
719 let left_header_value_1 = HeaderValue::from_static("lhs value 1");
720 let left_header_value_2 = HeaderValue::from_static("lhs_value 2");
721 let right_header_value = HeaderValue::from_static("rhs value");
722
723 let mut left_hand_side_headers = HeaderMap::new();
724 left_hand_side_headers.insert(header_name.clone(), left_header_value_1.clone());
725 left_hand_side_headers.append(header_name.clone(), left_header_value_2.clone());
726
727 let mut right_hand_side_headers = HeaderMap::new();
728 right_hand_side_headers.insert(header_name.clone(), right_header_value.clone());
729
730 let merged_header_map =
731 append_merge_header_maps(left_hand_side_headers, right_hand_side_headers);
732 let actual_merged_values: Vec<_> =
733 merged_header_map.get_all(header_name).into_iter().collect();
734
735 let expected_merged_values =
736 vec![left_header_value_1, left_header_value_2, right_header_value];
737
738 assert_eq!(actual_merged_values, expected_merged_values);
739 }
740
741 #[test]
742 fn test_append_merge_header_maps_with_empty_left_hand_map() {
743 let header_name = HeaderName::from_static("some_key");
744 let right_header_value_1 = HeaderValue::from_static("rhs value 1");
745 let right_header_value_2 = HeaderValue::from_static("rhs_value 2");
746
747 let left_hand_side_headers = HeaderMap::new();
748
749 let mut right_hand_side_headers = HeaderMap::new();
750 right_hand_side_headers.insert(header_name.clone(), right_header_value_1.clone());
751 right_hand_side_headers.append(header_name.clone(), right_header_value_2.clone());
752
753 let merged_header_map =
754 append_merge_header_maps(left_hand_side_headers, right_hand_side_headers);
755 let actual_merged_values: Vec<_> =
756 merged_header_map.get_all(header_name).into_iter().collect();
757
758 let expected_merged_values = vec![right_header_value_1, right_header_value_2];
759
760 assert_eq!(actual_merged_values, expected_merged_values);
761 }
762}