1 use bytes::Bytes;
2 use libc::{c_int, size_t};
3 use std::ffi::c_void;
4 
5 use super::body::{hyper_body, hyper_buf};
6 use super::error::hyper_code;
7 use super::task::{hyper_task_return_type, AsTaskType};
8 use super::{UserDataPointer, HYPER_ITER_CONTINUE};
9 use crate::ext::{HeaderCaseMap, OriginalHeaderOrder, ReasonPhrase};
10 use crate::header::{HeaderName, HeaderValue};
11 use crate::{Body, HeaderMap, Method, Request, Response, Uri};
12 
13 /// An HTTP request.
14 pub struct hyper_request(pub(super) Request<Body>);
15 
16 /// An HTTP response.
17 pub struct hyper_response(pub(super) Response<Body>);
18 
19 /// An HTTP header map.
20 ///
21 /// These can be part of a request or response.
22 pub struct hyper_headers {
23     pub(super) headers: HeaderMap,
24     orig_casing: HeaderCaseMap,
25     orig_order: OriginalHeaderOrder,
26 }
27 
28 pub(crate) struct RawHeaders(pub(crate) hyper_buf);
29 
30 pub(crate) struct OnInformational {
31     func: hyper_request_on_informational_callback,
32     data: UserDataPointer,
33 }
34 
35 type hyper_request_on_informational_callback = extern "C" fn(*mut c_void, *mut hyper_response);
36 
37 // ===== impl hyper_request =====
38 
39 ffi_fn! {
40     /// Construct a new HTTP request.
41     fn hyper_request_new() -> *mut hyper_request {
42         Box::into_raw(Box::new(hyper_request(Request::new(Body::empty()))))
43     } ?= std::ptr::null_mut()
44 }
45 
46 ffi_fn! {
47     /// Free an HTTP request if not going to send it on a client.
48     fn hyper_request_free(req: *mut hyper_request) {
49         drop(non_null!(Box::from_raw(req) ?= ()));
50     }
51 }
52 
53 ffi_fn! {
54     /// Set the HTTP Method of the request.
55     fn hyper_request_set_method(req: *mut hyper_request, method: *const u8, method_len: size_t) -> hyper_code {
56         let bytes = unsafe {
57             std::slice::from_raw_parts(method, method_len as usize)
58         };
59         let req = non_null!(&mut *req ?= hyper_code::HYPERE_INVALID_ARG);
60         match Method::from_bytes(bytes) {
61             Ok(m) => {
62                 *req.0.method_mut() = m;
63                 hyper_code::HYPERE_OK
64             },
65             Err(_) => {
66                 hyper_code::HYPERE_INVALID_ARG
67             }
68         }
69     }
70 }
71 
72 ffi_fn! {
73     /// Set the URI of the request.
74     ///
75     /// The request's URI is best described as the `request-target` from the RFCs. So in HTTP/1,
76     /// whatever is set will get sent as-is in the first line (GET $uri HTTP/1.1). It
77     /// supports the 4 defined variants, origin-form, absolute-form, authority-form, and
78     /// asterisk-form.
79     ///
80     /// The underlying type was built to efficiently support HTTP/2 where the request-target is
81     /// split over :scheme, :authority, and :path. As such, each part can be set explicitly, or the
82     /// type can parse a single contiguous string and if a scheme is found, that slot is "set". If
83     /// the string just starts with a path, only the path portion is set. All pseudo headers that
84     /// have been parsed/set are sent when the connection type is HTTP/2.
85     ///
86     /// To set each slot explicitly, use `hyper_request_set_uri_parts`.
87     fn hyper_request_set_uri(req: *mut hyper_request, uri: *const u8, uri_len: size_t) -> hyper_code {
88         let bytes = unsafe {
89             std::slice::from_raw_parts(uri, uri_len as usize)
90         };
91         let req = non_null!(&mut *req ?= hyper_code::HYPERE_INVALID_ARG);
92         match Uri::from_maybe_shared(bytes) {
93             Ok(u) => {
94                 *req.0.uri_mut() = u;
95                 hyper_code::HYPERE_OK
96             },
97             Err(_) => {
98                 hyper_code::HYPERE_INVALID_ARG
99             }
100         }
101     }
102 }
103 
104 ffi_fn! {
105     /// Set the URI of the request with separate scheme, authority, and
106     /// path/query strings.
107     ///
108     /// Each of `scheme`, `authority`, and `path_and_query` should either be
109     /// null, to skip providing a component, or point to a UTF-8 encoded
110     /// string. If any string pointer argument is non-null, its corresponding
111     /// `len` parameter must be set to the string's length.
112     fn hyper_request_set_uri_parts(
113         req: *mut hyper_request,
114         scheme: *const u8,
115         scheme_len: size_t,
116         authority: *const u8,
117         authority_len: size_t,
118         path_and_query: *const u8,
119         path_and_query_len: size_t
120     ) -> hyper_code {
121         let mut builder = Uri::builder();
122         if !scheme.is_null() {
123             let scheme_bytes = unsafe {
124                 std::slice::from_raw_parts(scheme, scheme_len as usize)
125             };
126             builder = builder.scheme(scheme_bytes);
127         }
128         if !authority.is_null() {
129             let authority_bytes = unsafe {
130                 std::slice::from_raw_parts(authority, authority_len as usize)
131             };
132             builder = builder.authority(authority_bytes);
133         }
134         if !path_and_query.is_null() {
135             let path_and_query_bytes = unsafe {
136                 std::slice::from_raw_parts(path_and_query, path_and_query_len as usize)
137             };
138             builder = builder.path_and_query(path_and_query_bytes);
139         }
140         match builder.build() {
141             Ok(u) => {
142                 *unsafe { &mut *req }.0.uri_mut() = u;
143                 hyper_code::HYPERE_OK
144             },
145             Err(_) => {
146                 hyper_code::HYPERE_INVALID_ARG
147             }
148         }
149     }
150 }
151 
152 ffi_fn! {
153     /// Set the preferred HTTP version of the request.
154     ///
155     /// The version value should be one of the `HYPER_HTTP_VERSION_` constants.
156     ///
157     /// Note that this won't change the major HTTP version of the connection,
158     /// since that is determined at the handshake step.
159     fn hyper_request_set_version(req: *mut hyper_request, version: c_int) -> hyper_code {
160         use http::Version;
161 
162         let req = non_null!(&mut *req ?= hyper_code::HYPERE_INVALID_ARG);
163         *req.0.version_mut() = match version {
164             super::HYPER_HTTP_VERSION_NONE => Version::HTTP_11,
165             super::HYPER_HTTP_VERSION_1_0 => Version::HTTP_10,
166             super::HYPER_HTTP_VERSION_1_1 => Version::HTTP_11,
167             super::HYPER_HTTP_VERSION_2 => Version::HTTP_2,
168             _ => {
169                 // We don't know this version
170                 return hyper_code::HYPERE_INVALID_ARG;
171             }
172         };
173         hyper_code::HYPERE_OK
174     }
175 }
176 
177 ffi_fn! {
178     /// Gets a reference to the HTTP headers of this request
179     ///
180     /// This is not an owned reference, so it should not be accessed after the
181     /// `hyper_request` has been consumed.
182     fn hyper_request_headers(req: *mut hyper_request) -> *mut hyper_headers {
183         hyper_headers::get_or_default(unsafe { &mut *req }.0.extensions_mut())
184     } ?= std::ptr::null_mut()
185 }
186 
187 ffi_fn! {
188     /// Set the body of the request.
189     ///
190     /// The default is an empty body.
191     ///
192     /// This takes ownership of the `hyper_body *`, you must not use it or
193     /// free it after setting it on the request.
194     fn hyper_request_set_body(req: *mut hyper_request, body: *mut hyper_body) -> hyper_code {
195         let body = non_null!(Box::from_raw(body) ?= hyper_code::HYPERE_INVALID_ARG);
196         let req = non_null!(&mut *req ?= hyper_code::HYPERE_INVALID_ARG);
197         *req.0.body_mut() = body.0;
198         hyper_code::HYPERE_OK
199     }
200 }
201 
202 ffi_fn! {
203     /// Set an informational (1xx) response callback.
204     ///
205     /// The callback is called each time hyper receives an informational (1xx)
206     /// response for this request.
207     ///
208     /// The third argument is an opaque user data pointer, which is passed to
209     /// the callback each time.
210     ///
211     /// The callback is passed the `void *` data pointer, and a
212     /// `hyper_response *` which can be inspected as any other response. The
213     /// body of the response will always be empty.
214     ///
215     /// NOTE: The `hyper_response *` is just borrowed data, and will not
216     /// be valid after the callback finishes. You must copy any data you wish
217     /// to persist.
218     fn hyper_request_on_informational(req: *mut hyper_request, callback: hyper_request_on_informational_callback, data: *mut c_void) -> hyper_code {
219         let ext = OnInformational {
220             func: callback,
221             data: UserDataPointer(data),
222         };
223         let req = non_null!(&mut *req ?= hyper_code::HYPERE_INVALID_ARG);
224         req.0.extensions_mut().insert(ext);
225         hyper_code::HYPERE_OK
226     }
227 }
228 
229 impl hyper_request {
finalize_request(&mut self)230     pub(super) fn finalize_request(&mut self) {
231         if let Some(headers) = self.0.extensions_mut().remove::<hyper_headers>() {
232             *self.0.headers_mut() = headers.headers;
233             self.0.extensions_mut().insert(headers.orig_casing);
234             self.0.extensions_mut().insert(headers.orig_order);
235         }
236     }
237 }
238 
239 // ===== impl hyper_response =====
240 
241 ffi_fn! {
242     /// Free an HTTP response after using it.
243     fn hyper_response_free(resp: *mut hyper_response) {
244         drop(non_null!(Box::from_raw(resp) ?= ()));
245     }
246 }
247 
248 ffi_fn! {
249     /// Get the HTTP-Status code of this response.
250     ///
251     /// It will always be within the range of 100-599.
252     fn hyper_response_status(resp: *const hyper_response) -> u16 {
253         non_null!(&*resp ?= 0).0.status().as_u16()
254     }
255 }
256 
257 ffi_fn! {
258     /// Get a pointer to the reason-phrase of this response.
259     ///
260     /// This buffer is not null-terminated.
261     ///
262     /// This buffer is owned by the response, and should not be used after
263     /// the response has been freed.
264     ///
265     /// Use `hyper_response_reason_phrase_len()` to get the length of this
266     /// buffer.
267     fn hyper_response_reason_phrase(resp: *const hyper_response) -> *const u8 {
268         non_null!(&*resp ?= std::ptr::null()).reason_phrase().as_ptr()
269     } ?= std::ptr::null()
270 }
271 
272 ffi_fn! {
273     /// Get the length of the reason-phrase of this response.
274     ///
275     /// Use `hyper_response_reason_phrase()` to get the buffer pointer.
276     fn hyper_response_reason_phrase_len(resp: *const hyper_response) -> size_t {
277         non_null!(&*resp ?= 0).reason_phrase().len()
278     }
279 }
280 
281 ffi_fn! {
282     /// Get a reference to the full raw headers of this response.
283     ///
284     /// You must have enabled `hyper_clientconn_options_headers_raw()`, or this
285     /// will return NULL.
286     ///
287     /// The returned `hyper_buf *` is just a reference, owned by the response.
288     /// You need to make a copy if you wish to use it after freeing the
289     /// response.
290     ///
291     /// The buffer is not null-terminated, see the `hyper_buf` functions for
292     /// getting the bytes and length.
293     fn hyper_response_headers_raw(resp: *const hyper_response) -> *const hyper_buf {
294         let resp = non_null!(&*resp ?= std::ptr::null());
295         match resp.0.extensions().get::<RawHeaders>() {
296             Some(raw) => &raw.0,
297             None => std::ptr::null(),
298         }
299     } ?= std::ptr::null()
300 }
301 
302 ffi_fn! {
303     /// Get the HTTP version used by this response.
304     ///
305     /// The returned value could be:
306     ///
307     /// - `HYPER_HTTP_VERSION_1_0`
308     /// - `HYPER_HTTP_VERSION_1_1`
309     /// - `HYPER_HTTP_VERSION_2`
310     /// - `HYPER_HTTP_VERSION_NONE` if newer (or older).
311     fn hyper_response_version(resp: *const hyper_response) -> c_int {
312         use http::Version;
313 
314         match non_null!(&*resp ?= 0).0.version() {
315             Version::HTTP_10 => super::HYPER_HTTP_VERSION_1_0,
316             Version::HTTP_11 => super::HYPER_HTTP_VERSION_1_1,
317             Version::HTTP_2 => super::HYPER_HTTP_VERSION_2,
318             _ => super::HYPER_HTTP_VERSION_NONE,
319         }
320     }
321 }
322 
323 ffi_fn! {
324     /// Gets a reference to the HTTP headers of this response.
325     ///
326     /// This is not an owned reference, so it should not be accessed after the
327     /// `hyper_response` has been freed.
328     fn hyper_response_headers(resp: *mut hyper_response) -> *mut hyper_headers {
329         hyper_headers::get_or_default(unsafe { &mut *resp }.0.extensions_mut())
330     } ?= std::ptr::null_mut()
331 }
332 
333 ffi_fn! {
334     /// Take ownership of the body of this response.
335     ///
336     /// It is safe to free the response even after taking ownership of its body.
337     fn hyper_response_body(resp: *mut hyper_response) -> *mut hyper_body {
338         let body = std::mem::take(non_null!(&mut *resp ?= std::ptr::null_mut()).0.body_mut());
339         Box::into_raw(Box::new(hyper_body(body)))
340     } ?= std::ptr::null_mut()
341 }
342 
343 impl hyper_response {
wrap(mut resp: Response<Body>) -> hyper_response344     pub(super) fn wrap(mut resp: Response<Body>) -> hyper_response {
345         let headers = std::mem::take(resp.headers_mut());
346         let orig_casing = resp
347             .extensions_mut()
348             .remove::<HeaderCaseMap>()
349             .unwrap_or_else(HeaderCaseMap::default);
350         let orig_order = resp
351             .extensions_mut()
352             .remove::<OriginalHeaderOrder>()
353             .unwrap_or_else(OriginalHeaderOrder::default);
354         resp.extensions_mut().insert(hyper_headers {
355             headers,
356             orig_casing,
357             orig_order,
358         });
359 
360         hyper_response(resp)
361     }
362 
reason_phrase(&self) -> &[u8]363     fn reason_phrase(&self) -> &[u8] {
364         if let Some(reason) = self.0.extensions().get::<ReasonPhrase>() {
365             return reason.as_bytes();
366         }
367 
368         if let Some(reason) = self.0.status().canonical_reason() {
369             return reason.as_bytes();
370         }
371 
372         &[]
373     }
374 }
375 
376 unsafe impl AsTaskType for hyper_response {
as_task_type(&self) -> hyper_task_return_type377     fn as_task_type(&self) -> hyper_task_return_type {
378         hyper_task_return_type::HYPER_TASK_RESPONSE
379     }
380 }
381 
382 // ===== impl Headers =====
383 
384 type hyper_headers_foreach_callback =
385     extern "C" fn(*mut c_void, *const u8, size_t, *const u8, size_t) -> c_int;
386 
387 impl hyper_headers {
get_or_default(ext: &mut http::Extensions) -> &mut hyper_headers388     pub(super) fn get_or_default(ext: &mut http::Extensions) -> &mut hyper_headers {
389         if let None = ext.get_mut::<hyper_headers>() {
390             ext.insert(hyper_headers::default());
391         }
392 
393         ext.get_mut::<hyper_headers>().unwrap()
394     }
395 }
396 
397 ffi_fn! {
398     /// Iterates the headers passing each name and value pair to the callback.
399     ///
400     /// The `userdata` pointer is also passed to the callback.
401     ///
402     /// The callback should return `HYPER_ITER_CONTINUE` to keep iterating, or
403     /// `HYPER_ITER_BREAK` to stop.
404     fn hyper_headers_foreach(headers: *const hyper_headers, func: hyper_headers_foreach_callback, userdata: *mut c_void) {
405         let headers = non_null!(&*headers ?= ());
406         // For each header name/value pair, there may be a value in the casemap
407         // that corresponds to the HeaderValue. So, we iterator all the keys,
408         // and for each one, try to pair the originally cased name with the value.
409         //
410         // TODO: consider adding http::HeaderMap::entries() iterator
411         let mut ordered_iter =  headers.orig_order.get_in_order().peekable();
412         if ordered_iter.peek().is_some() {
413             for (name, idx) in ordered_iter {
414                 let (name_ptr, name_len) = if let Some(orig_name) = headers.orig_casing.get_all(name).nth(*idx) {
415                     (orig_name.as_ref().as_ptr(), orig_name.as_ref().len())
416                 } else {
417                     (
418                     name.as_str().as_bytes().as_ptr(),
419                     name.as_str().as_bytes().len(),
420                     )
421                 };
422 
423                 let val_ptr;
424                 let val_len;
425                 if let Some(value) = headers.headers.get_all(name).iter().nth(*idx) {
426                     val_ptr = value.as_bytes().as_ptr();
427                     val_len = value.as_bytes().len();
428                 } else {
429                     // Stop iterating, something has gone wrong.
430                     return;
431                 }
432 
433                 if HYPER_ITER_CONTINUE != func(userdata, name_ptr, name_len, val_ptr, val_len) {
434                     return;
435                 }
436             }
437         } else {
438             for name in headers.headers.keys() {
439                 let mut names = headers.orig_casing.get_all(name);
440 
441                 for value in headers.headers.get_all(name) {
442                     let (name_ptr, name_len) = if let Some(orig_name) = names.next() {
443                         (orig_name.as_ref().as_ptr(), orig_name.as_ref().len())
444                     } else {
445                         (
446                             name.as_str().as_bytes().as_ptr(),
447                             name.as_str().as_bytes().len(),
448                         )
449                     };
450 
451                     let val_ptr = value.as_bytes().as_ptr();
452                     let val_len = value.as_bytes().len();
453 
454                     if HYPER_ITER_CONTINUE != func(userdata, name_ptr, name_len, val_ptr, val_len) {
455                         return;
456                     }
457                 }
458             }
459         }
460     }
461 }
462 
463 ffi_fn! {
464     /// Sets the header with the provided name to the provided value.
465     ///
466     /// This overwrites any previous value set for the header.
467     fn hyper_headers_set(headers: *mut hyper_headers, name: *const u8, name_len: size_t, value: *const u8, value_len: size_t) -> hyper_code {
468         let headers = non_null!(&mut *headers ?= hyper_code::HYPERE_INVALID_ARG);
469         match unsafe { raw_name_value(name, name_len, value, value_len) } {
470             Ok((name, value, orig_name)) => {
471                 headers.headers.insert(&name, value);
472                 headers.orig_casing.insert(name.clone(), orig_name.clone());
473                 headers.orig_order.insert(name);
474                 hyper_code::HYPERE_OK
475             }
476             Err(code) => code,
477         }
478     }
479 }
480 
481 ffi_fn! {
482     /// Adds the provided value to the list of the provided name.
483     ///
484     /// If there were already existing values for the name, this will append the
485     /// new value to the internal list.
486     fn hyper_headers_add(headers: *mut hyper_headers, name: *const u8, name_len: size_t, value: *const u8, value_len: size_t) -> hyper_code {
487         let headers = non_null!(&mut *headers ?= hyper_code::HYPERE_INVALID_ARG);
488 
489         match unsafe { raw_name_value(name, name_len, value, value_len) } {
490             Ok((name, value, orig_name)) => {
491                 headers.headers.append(&name, value);
492                 headers.orig_casing.append(&name, orig_name.clone());
493                 headers.orig_order.append(name);
494                 hyper_code::HYPERE_OK
495             }
496             Err(code) => code,
497         }
498     }
499 }
500 
501 impl Default for hyper_headers {
default() -> Self502     fn default() -> Self {
503         Self {
504             headers: Default::default(),
505             orig_casing: HeaderCaseMap::default(),
506             orig_order: OriginalHeaderOrder::default(),
507         }
508     }
509 }
510 
raw_name_value( name: *const u8, name_len: size_t, value: *const u8, value_len: size_t, ) -> Result<(HeaderName, HeaderValue, Bytes), hyper_code>511 unsafe fn raw_name_value(
512     name: *const u8,
513     name_len: size_t,
514     value: *const u8,
515     value_len: size_t,
516 ) -> Result<(HeaderName, HeaderValue, Bytes), hyper_code> {
517     let name = std::slice::from_raw_parts(name, name_len);
518     let orig_name = Bytes::copy_from_slice(name);
519     let name = match HeaderName::from_bytes(name) {
520         Ok(name) => name,
521         Err(_) => return Err(hyper_code::HYPERE_INVALID_ARG),
522     };
523     let value = std::slice::from_raw_parts(value, value_len);
524     let value = match HeaderValue::from_bytes(value) {
525         Ok(val) => val,
526         Err(_) => return Err(hyper_code::HYPERE_INVALID_ARG),
527     };
528 
529     Ok((name, value, orig_name))
530 }
531 
532 // ===== impl OnInformational =====
533 
534 impl OnInformational {
call(&mut self, resp: Response<Body>)535     pub(crate) fn call(&mut self, resp: Response<Body>) {
536         let mut resp = hyper_response::wrap(resp);
537         (self.func)(self.data.0, &mut resp);
538     }
539 }
540 
541 #[cfg(test)]
542 mod tests {
543     use super::*;
544 
545     #[test]
test_headers_foreach_cases_preserved()546     fn test_headers_foreach_cases_preserved() {
547         let mut headers = hyper_headers::default();
548 
549         let name1 = b"Set-CookiE";
550         let value1 = b"a=b";
551         hyper_headers_add(
552             &mut headers,
553             name1.as_ptr(),
554             name1.len(),
555             value1.as_ptr(),
556             value1.len(),
557         );
558 
559         let name2 = b"SET-COOKIE";
560         let value2 = b"c=d";
561         hyper_headers_add(
562             &mut headers,
563             name2.as_ptr(),
564             name2.len(),
565             value2.as_ptr(),
566             value2.len(),
567         );
568 
569         let mut vec = Vec::<u8>::new();
570         hyper_headers_foreach(&headers, concat, &mut vec as *mut _ as *mut c_void);
571 
572         assert_eq!(vec, b"Set-CookiE: a=b\r\nSET-COOKIE: c=d\r\n");
573 
574         extern "C" fn concat(
575             vec: *mut c_void,
576             name: *const u8,
577             name_len: usize,
578             value: *const u8,
579             value_len: usize,
580         ) -> c_int {
581             unsafe {
582                 let vec = &mut *(vec as *mut Vec<u8>);
583                 let name = std::slice::from_raw_parts(name, name_len);
584                 let value = std::slice::from_raw_parts(value, value_len);
585                 vec.extend(name);
586                 vec.extend(b": ");
587                 vec.extend(value);
588                 vec.extend(b"\r\n");
589             }
590             HYPER_ITER_CONTINUE
591         }
592     }
593 
594     #[cfg(all(feature = "http1", feature = "ffi"))]
595     #[test]
test_headers_foreach_order_preserved()596     fn test_headers_foreach_order_preserved() {
597         let mut headers = hyper_headers::default();
598 
599         let name1 = b"Set-CookiE";
600         let value1 = b"a=b";
601         hyper_headers_add(
602             &mut headers,
603             name1.as_ptr(),
604             name1.len(),
605             value1.as_ptr(),
606             value1.len(),
607         );
608 
609         let name2 = b"Content-Encoding";
610         let value2 = b"gzip";
611         hyper_headers_add(
612             &mut headers,
613             name2.as_ptr(),
614             name2.len(),
615             value2.as_ptr(),
616             value2.len(),
617         );
618 
619         let name3 = b"SET-COOKIE";
620         let value3 = b"c=d";
621         hyper_headers_add(
622             &mut headers,
623             name3.as_ptr(),
624             name3.len(),
625             value3.as_ptr(),
626             value3.len(),
627         );
628 
629         let mut vec = Vec::<u8>::new();
630         hyper_headers_foreach(&headers, concat, &mut vec as *mut _ as *mut c_void);
631 
632         println!("{}", std::str::from_utf8(&vec).unwrap());
633         assert_eq!(
634             vec,
635             b"Set-CookiE: a=b\r\nContent-Encoding: gzip\r\nSET-COOKIE: c=d\r\n"
636         );
637 
638         extern "C" fn concat(
639             vec: *mut c_void,
640             name: *const u8,
641             name_len: usize,
642             value: *const u8,
643             value_len: usize,
644         ) -> c_int {
645             unsafe {
646                 let vec = &mut *(vec as *mut Vec<u8>);
647                 let name = std::slice::from_raw_parts(name, name_len);
648                 let value = std::slice::from_raw_parts(value, value_len);
649                 vec.extend(name);
650                 vec.extend(b": ");
651                 vec.extend(value);
652                 vec.extend(b"\r\n");
653             }
654             HYPER_ITER_CONTINUE
655         }
656     }
657 }
658