]> git.proxmox.com Git - mirror_edk2.git/commitdiff
NetworkPkg: HttpDxe response/cancel issue fix
authorJiaxin Wu <jiaxin.wu@intel.com>
Tue, 31 May 2016 14:17:27 +0000 (22:17 +0800)
committerJiaxin Wu <jiaxin.wu@intel.com>
Mon, 13 Jun 2016 03:51:47 +0000 (11:51 +0800)
Remove timeout check for http body message receive. It should be handled
in HttpBootDxe driver for http response unblocking implementation. After
timeout removed, the Wrap date should not be freed immediately. Only the
TCP CompletionToken in Wrap date is canceled or signaled, the Wrap date
could be freed.
In addition, Http cancel token is also incorrect. Tcp Cancel should be called
to cancel TCP CompletionToken in Wrap date before close it directly. Otherwise,
some exception behavior may happened.
This patch also refine the coding style for HttpDxe driver.

Cc: Fu Siyuan <siyuan.fu@intel.com>
Cc: Ye Ting <ting.ye@intel.com>
Cc: Zhang Lubo <lubo.zhang@intel.com>
Cc: Hegde Nagaraj P <nagaraj-p.hegde@hpe.com>
Cc: Gary Lin <glin@suse.com>
Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Jiaxin Wu <jiaxin.wu@intel.com>
Reviewed-by: Gary Lin <glin@suse.com>
Reviewed-by: Hegde Nagaraj P <nagaraj-p.hegde@hpe.com>
Reviewed-by: Ye Ting <ting.ye@intel.com>
Tested-by: Gary Lin <glin@suse.com>
Tested-by: Hegde Nagaraj P <nagaraj-p.hegde@hpe.com>
NetworkPkg/HttpDxe/HttpImpl.c
NetworkPkg/HttpDxe/HttpProto.c
NetworkPkg/HttpDxe/HttpProto.h

index 12f22dbbb14be991df57da3d16cce03a64d5d260..ad194dbe0e5556da82d703477302082d690ff3c6 100644 (file)
@@ -462,7 +462,6 @@ EfiHttpRequest (
       }\r
     }\r
 \r
       }\r
     }\r
 \r
-\r
     //\r
     // Save the RemotePort and RemoteHost.\r
     //\r
     //\r
     // Save the RemotePort and RemoteHost.\r
     //\r
@@ -578,13 +577,13 @@ EfiHttpRequest (
   return EFI_SUCCESS;\r
 \r
 Error5:\r
   return EFI_SUCCESS;\r
 \r
 Error5:\r
-    //\r
-    // We would have inserted a TxToken only if Request structure is not NULL.\r
-    // Hence check before we do a remove in this error case.\r
-    //\r
-    if (Request != NULL) {\r
-      NetMapRemoveTail (&HttpInstance->TxTokens, NULL);\r
-    }\r
+  //\r
+  // We would have inserted a TxToken only if Request structure is not NULL.\r
+  // Hence check before we do a remove in this error case.\r
+  //\r
+  if (Request != NULL) {\r
+    NetMapRemoveTail (&HttpInstance->TxTokens, NULL);\r
+  }\r
 \r
 Error4:\r
   if (RequestMsg != NULL) {\r
 \r
 Error4:\r
   if (RequestMsg != NULL) {\r
@@ -606,7 +605,6 @@ Error2:
   }\r
 \r
 Error1:\r
   }\r
 \r
 Error1:\r
-\r
   if (HostName != NULL) {\r
     FreePool (HostName);\r
   }\r
   if (HostName != NULL) {\r
     FreePool (HostName);\r
   }\r
@@ -640,7 +638,6 @@ HttpCancelTokens (
   IN VOID                   *Context\r
   )\r
 {\r
   IN VOID                   *Context\r
   )\r
 {\r
-\r
   EFI_HTTP_TOKEN            *Token;\r
   HTTP_TOKEN_WRAP           *Wrap;\r
   HTTP_PROTOCOL             *HttpInstance;\r
   EFI_HTTP_TOKEN            *Token;\r
   HTTP_TOKEN_WRAP           *Wrap;\r
   HTTP_PROTOCOL             *HttpInstance;\r
@@ -658,42 +655,33 @@ HttpCancelTokens (
   Wrap = (HTTP_TOKEN_WRAP *) Item->Value;\r
   ASSERT (Wrap != NULL);\r
   HttpInstance = Wrap->HttpInstance;\r
   Wrap = (HTTP_TOKEN_WRAP *) Item->Value;\r
   ASSERT (Wrap != NULL);\r
   HttpInstance = Wrap->HttpInstance;\r
-\r
-  //\r
-  // Free resources.\r
-  //\r
-  NetMapRemoveItem (Map, Item, NULL); \r
   \r
   if (!HttpInstance->LocalAddressIsIPv6) {\r
   \r
   if (!HttpInstance->LocalAddressIsIPv6) {\r
-    if (Wrap->TcpWrap.Tx4Token.CompletionToken.Event != NULL) {\r
-      gBS->CloseEvent (Wrap->TcpWrap.Tx4Token.CompletionToken.Event);\r
-    }\r
-    \r
     if (Wrap->TcpWrap.Rx4Token.CompletionToken.Event != NULL) {\r
     if (Wrap->TcpWrap.Rx4Token.CompletionToken.Event != NULL) {\r
-      gBS->CloseEvent (Wrap->TcpWrap.Rx4Token.CompletionToken.Event);\r
-    }\r
-    \r
-    if (Wrap->TcpWrap.Rx4Token.Packet.RxData->FragmentTable[0].FragmentBuffer != NULL) {\r
-      FreePool (Wrap->TcpWrap.Rx4Token.Packet.RxData->FragmentTable[0].FragmentBuffer);\r
-    }\r
+      //\r
+      // Cancle the Token before close its Event.\r
+      //\r
+      HttpInstance->Tcp4->Cancel (HttpInstance->Tcp4, &Wrap->TcpWrap.Rx4Token.CompletionToken);\r
 \r
 \r
-  } else {\r
-    if (Wrap->TcpWrap.Tx6Token.CompletionToken.Event != NULL) {\r
-      gBS->CloseEvent (Wrap->TcpWrap.Tx6Token.CompletionToken.Event);\r
+      //\r
+      // Dispatch the DPC queued by the NotifyFunction of the canceled token's events.\r
+      //\r
+      DispatchDpc ();\r
     }\r
     }\r
-\r
+  } else {\r
     if (Wrap->TcpWrap.Rx6Token.CompletionToken.Event != NULL) {\r
     if (Wrap->TcpWrap.Rx6Token.CompletionToken.Event != NULL) {\r
-      gBS->CloseEvent (Wrap->TcpWrap.Rx6Token.CompletionToken.Event);\r
-    }\r
+      //\r
+      // Cancle the Token before close its Event.\r
+      //\r
+      HttpInstance->Tcp6->Cancel (HttpInstance->Tcp6, &Wrap->TcpWrap.Rx6Token.CompletionToken);\r
 \r
 \r
-    if (Wrap->TcpWrap.Rx6Token.Packet.RxData->FragmentTable[0].FragmentBuffer != NULL) {\r
-      FreePool (Wrap->TcpWrap.Rx6Token.Packet.RxData->FragmentTable[0].FragmentBuffer);\r
+      //\r
+      // Dispatch the DPC queued by the NotifyFunction of the canceled token's events.\r
+      //\r
+      DispatchDpc ();\r
     }\r
   }\r
 \r
     }\r
   }\r
 \r
-\r
-  FreePool (Wrap);\r
-\r
   //\r
   // If only one item is to be cancel, return EFI_ABORTED to stop\r
   // iterating the map any more.\r
   //\r
   // If only one item is to be cancel, return EFI_ABORTED to stop\r
   // iterating the map any more.\r
@@ -1009,6 +997,7 @@ HttpResponseWorker (
     //\r
     StatusCodeStr = HttpHeaders + AsciiStrLen (HTTP_VERSION_STR) + 1;\r
     if (StatusCodeStr == NULL) {\r
     //\r
     StatusCodeStr = HttpHeaders + AsciiStrLen (HTTP_VERSION_STR) + 1;\r
     if (StatusCodeStr == NULL) {\r
+      Status = EFI_NOT_READY;\r
       goto Error;\r
     }\r
 \r
       goto Error;\r
     }\r
 \r
@@ -1019,6 +1008,7 @@ HttpResponseWorker (
     //\r
     Tmp = AsciiStrStr (HttpHeaders, HTTP_CRLF_STR);\r
     if (Tmp == NULL) {\r
     //\r
     Tmp = AsciiStrStr (HttpHeaders, HTTP_CRLF_STR);\r
     if (Tmp == NULL) {\r
+      Status = EFI_NOT_READY;\r
       goto Error;\r
     }\r
 \r
       goto Error;\r
     }\r
 \r
@@ -1065,6 +1055,7 @@ HttpResponseWorker (
     if (SizeofHeaders != 0) {\r
       HeaderTmp = AllocateZeroPool (SizeofHeaders);\r
       if (HeaderTmp == NULL) {\r
     if (SizeofHeaders != 0) {\r
       HeaderTmp = AllocateZeroPool (SizeofHeaders);\r
       if (HeaderTmp == NULL) {\r
+        Status = EFI_OUT_OF_RESOURCES;\r
         goto Error2;\r
       }\r
 \r
         goto Error2;\r
       }\r
 \r
@@ -1204,42 +1195,14 @@ HttpResponseWorker (
 \r
   ASSERT (HttpInstance->MsgParser != NULL);\r
 \r
 \r
   ASSERT (HttpInstance->MsgParser != NULL);\r
 \r
-  if (HttpInstance->TimeoutEvent == NULL) {\r
-    //\r
-    // Create TimeoutEvent for response\r
-    //\r
-    Status = gBS->CreateEvent (\r
-                    EVT_TIMER,\r
-                    TPL_CALLBACK,\r
-                    NULL,\r
-                    NULL,\r
-                    &HttpInstance->TimeoutEvent\r
-                    );\r
-    if (EFI_ERROR (Status)) {\r
-      goto Error2;\r
-    }\r
-  }\r
-\r
-  //\r
-  // Start the timer, and wait Timeout seconds to receive the body packet.\r
-  //\r
-  Status = gBS->SetTimer (HttpInstance->TimeoutEvent, TimerRelative, HTTP_RESPONSE_TIMEOUT * TICKS_PER_SECOND);\r
-  if (EFI_ERROR (Status)) {\r
-    goto Error2;\r
-  }\r
-\r
   //\r
   // We still need receive more data when there is no cache data and MsgParser is not NULL;\r
   //\r
   //\r
   // We still need receive more data when there is no cache data and MsgParser is not NULL;\r
   //\r
-  Status = HttpTcpReceiveBody (Wrap, HttpMsg, HttpInstance->TimeoutEvent);\r
-\r
-  gBS->SetTimer (HttpInstance->TimeoutEvent, TimerCancel, 0);\r
-\r
+  Status = HttpTcpReceiveBody (Wrap, HttpMsg);\r
   if (EFI_ERROR (Status)) {\r
     goto Error2;\r
   }\r
 \r
   if (EFI_ERROR (Status)) {\r
     goto Error2;\r
   }\r
 \r
-  FreePool (Wrap);\r
   return Status;\r
 \r
 Exit:\r
   return Status;\r
 \r
 Exit:\r
@@ -1265,6 +1228,11 @@ Error2:
   }\r
 \r
 Error:\r
   }\r
 \r
 Error:\r
+  Item = NetMapFindKey (&Wrap->HttpInstance->RxTokens, Wrap->HttpToken);\r
+  if (Item != NULL) {\r
+    NetMapRemoveItem (&Wrap->HttpInstance->RxTokens, Item, NULL);\r
+  }\r
+  \r
   HttpTcpTokenCleanup (Wrap);\r
   \r
   if (HttpHeaders != NULL) {\r
   HttpTcpTokenCleanup (Wrap);\r
   \r
   if (HttpHeaders != NULL) {\r
index afa7fe4b3508bf7b286b45c715f745196db2df7c..486b20304f5274be71b35026c09c3f715438914b 100644 (file)
@@ -148,19 +148,41 @@ HttpTcpReceiveNotifyDpc (
   \r
   if (UsingIpv6) {\r
     gBS->CloseEvent (Wrap->TcpWrap.Rx6Token.CompletionToken.Event);\r
   \r
   if (UsingIpv6) {\r
     gBS->CloseEvent (Wrap->TcpWrap.Rx6Token.CompletionToken.Event);\r
+    Wrap->TcpWrap.Rx6Token.CompletionToken.Event = NULL;\r
     \r
     if (EFI_ERROR (Wrap->TcpWrap.Rx6Token.CompletionToken.Status)) {\r
     \r
     if (EFI_ERROR (Wrap->TcpWrap.Rx6Token.CompletionToken.Status)) {\r
+      DEBUG ((EFI_D_ERROR, "HttpTcpReceiveNotifyDpc: %r!\n", Wrap->TcpWrap.Rx6Token.CompletionToken.Status));\r
       Wrap->HttpToken->Status = Wrap->TcpWrap.Rx6Token.CompletionToken.Status;\r
       gBS->SignalEvent (Wrap->HttpToken->Event);\r
       Wrap->HttpToken->Status = Wrap->TcpWrap.Rx6Token.CompletionToken.Status;\r
       gBS->SignalEvent (Wrap->HttpToken->Event);\r
+\r
+      Item = NetMapFindKey (&HttpInstance->RxTokens, Wrap->HttpToken);\r
+      if (Item != NULL) {\r
+        NetMapRemoveItem (&HttpInstance->RxTokens, Item, NULL);\r
+      }\r
+      \r
+      FreePool (Wrap);\r
+      Wrap = NULL;\r
+      \r
       return ;\r
     }\r
 \r
   } else {\r
     gBS->CloseEvent (Wrap->TcpWrap.Rx4Token.CompletionToken.Event);\r
       return ;\r
     }\r
 \r
   } else {\r
     gBS->CloseEvent (Wrap->TcpWrap.Rx4Token.CompletionToken.Event);\r
+    Wrap->TcpWrap.Rx4Token.CompletionToken.Event = NULL;\r
     \r
     if (EFI_ERROR (Wrap->TcpWrap.Rx4Token.CompletionToken.Status)) {\r
     \r
     if (EFI_ERROR (Wrap->TcpWrap.Rx4Token.CompletionToken.Status)) {\r
+      DEBUG ((EFI_D_ERROR, "HttpTcpReceiveNotifyDpc: %r!\n", Wrap->TcpWrap.Rx4Token.CompletionToken.Status));\r
       Wrap->HttpToken->Status = Wrap->TcpWrap.Rx4Token.CompletionToken.Status;\r
       gBS->SignalEvent (Wrap->HttpToken->Event);\r
       Wrap->HttpToken->Status = Wrap->TcpWrap.Rx4Token.CompletionToken.Status;\r
       gBS->SignalEvent (Wrap->HttpToken->Event);\r
+      \r
+      Item = NetMapFindKey (&HttpInstance->RxTokens, Wrap->HttpToken);\r
+      if (Item != NULL) {\r
+        NetMapRemoveItem (&HttpInstance->RxTokens, Item, NULL);\r
+      }\r
+      \r
+      FreePool (Wrap);\r
+      Wrap = NULL;\r
+      \r
       return ;\r
     }\r
   }\r
       return ;\r
     }\r
   }\r
@@ -232,6 +254,9 @@ HttpTcpReceiveNotifyDpc (
   // Check pending RxTokens and receive the HTTP message.\r
   //\r
   NetMapIterate (&Wrap->HttpInstance->RxTokens, HttpTcpReceive, NULL);\r
   // Check pending RxTokens and receive the HTTP message.\r
   //\r
   NetMapIterate (&Wrap->HttpInstance->RxTokens, HttpTcpReceive, NULL);\r
+\r
+  FreePool (Wrap);\r
+  Wrap = NULL;\r
 }\r
 \r
 /**\r
 }\r
 \r
 /**\r
@@ -408,15 +433,15 @@ HttpCreateTcpTxEvent (
                     Wrap,\r
                     &TcpWrap->Tx4Token.CompletionToken.Event\r
                     );\r
                     Wrap,\r
                     &TcpWrap->Tx4Token.CompletionToken.Event\r
                     );\r
-      if (EFI_ERROR (Status)) {\r
-        return Status;\r
-      }\r
-    \r
-      TcpWrap->Tx4Data.Push = TRUE;\r
-      TcpWrap->Tx4Data.Urgent = FALSE;\r
-      TcpWrap->Tx4Data.FragmentCount = 1;\r
-      TcpWrap->Tx4Token.Packet.TxData = &Wrap->TcpWrap.Tx4Data;\r
-      TcpWrap->Tx4Token.CompletionToken.Status = EFI_NOT_READY;\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+  \r
+    TcpWrap->Tx4Data.Push = TRUE;\r
+    TcpWrap->Tx4Data.Urgent = FALSE;\r
+    TcpWrap->Tx4Data.FragmentCount = 1;\r
+    TcpWrap->Tx4Token.Packet.TxData = &Wrap->TcpWrap.Tx4Data;\r
+    TcpWrap->Tx4Token.CompletionToken.Status = EFI_NOT_READY;\r
 \r
   } else {\r
     Status = gBS->CreateEvent (\r
 \r
   } else {\r
     Status = gBS->CreateEvent (\r
@@ -436,7 +461,6 @@ HttpCreateTcpTxEvent (
     TcpWrap->Tx6Token.Packet.TxData = &Wrap->TcpWrap.Tx6Data;\r
     TcpWrap->Tx6Token.CompletionToken.Status =EFI_NOT_READY;\r
     \r
     TcpWrap->Tx6Token.Packet.TxData = &Wrap->TcpWrap.Tx6Data;\r
     TcpWrap->Tx6Token.CompletionToken.Status =EFI_NOT_READY;\r
     \r
-    \r
   }\r
   \r
   return EFI_SUCCESS;\r
   }\r
   \r
   return EFI_SUCCESS;\r
@@ -1607,6 +1631,10 @@ HttpTcpReceiveHeader (
       }\r
 \r
       if (!HttpInstance->IsRxDone) {\r
       }\r
 \r
       if (!HttpInstance->IsRxDone) {\r
+        //\r
+        // Cancle the Token before close its Event.\r
+        //\r
+        Tcp4->Cancel (HttpInstance->Tcp4, &Rx4Token->CompletionToken);\r
         gBS->CloseEvent (Rx4Token->CompletionToken.Event);\r
         Rx4Token->CompletionToken.Status = EFI_TIMEOUT;\r
       }\r
         gBS->CloseEvent (Rx4Token->CompletionToken.Event);\r
         Rx4Token->CompletionToken.Status = EFI_TIMEOUT;\r
       }\r
@@ -1673,6 +1701,10 @@ HttpTcpReceiveHeader (
       }\r
 \r
       if (!HttpInstance->IsRxDone) {\r
       }\r
 \r
       if (!HttpInstance->IsRxDone) {\r
+        //\r
+        // Cancle the Token before close its Event.\r
+        //\r
+        Tcp6->Cancel (HttpInstance->Tcp6, &Rx6Token->CompletionToken);\r
         gBS->CloseEvent (Rx6Token->CompletionToken.Event);\r
         Rx6Token->CompletionToken.Status = EFI_TIMEOUT;\r
       }\r
         gBS->CloseEvent (Rx6Token->CompletionToken.Event);\r
         Rx6Token->CompletionToken.Status = EFI_TIMEOUT;\r
       }\r
@@ -1728,7 +1760,6 @@ HttpTcpReceiveHeader (
 \r
   @param[in]  Wrap               The HTTP token's wrap data.\r
   @param[in]  HttpMsg            The HTTP message data.\r
 \r
   @param[in]  Wrap               The HTTP token's wrap data.\r
   @param[in]  HttpMsg            The HTTP message data.\r
-  @param[in]  Timeout            The time to wait for receiving the body packet.\r
 \r
   @retval EFI_SUCCESS            The HTTP body is received.                          \r
   @retval Others                 Other error as indicated.\r
 \r
   @retval EFI_SUCCESS            The HTTP body is received.                          \r
   @retval Others                 Other error as indicated.\r
@@ -1737,8 +1768,7 @@ HttpTcpReceiveHeader (
 EFI_STATUS\r
 HttpTcpReceiveBody (\r
   IN  HTTP_TOKEN_WRAP       *Wrap,\r
 EFI_STATUS\r
 HttpTcpReceiveBody (\r
   IN  HTTP_TOKEN_WRAP       *Wrap,\r
-  IN  EFI_HTTP_MESSAGE      *HttpMsg,\r
-  IN  EFI_EVENT             Timeout\r
+  IN  EFI_HTTP_MESSAGE      *HttpMsg\r
   )\r
 {\r
   EFI_STATUS                Status;\r
   )\r
 {\r
   EFI_STATUS                Status;\r
@@ -1772,17 +1802,6 @@ HttpTcpReceiveBody (
       DEBUG ((EFI_D_ERROR, "Tcp6 receive failed: %r\n", Status));\r
       return Status;\r
     }\r
       DEBUG ((EFI_D_ERROR, "Tcp6 receive failed: %r\n", Status));\r
       return Status;\r
     }\r
-\r
-    while (!Wrap->TcpWrap.IsRxDone && ((Timeout == NULL) || EFI_ERROR (gBS->CheckEvent (Timeout)))) {\r
-      Tcp6->Poll (Tcp6);\r
-    }\r
-\r
-    if (!Wrap->TcpWrap.IsRxDone) {\r
-      gBS->CloseEvent (Rx6Token->CompletionToken.Event);\r
-      Rx6Token->CompletionToken.Status = EFI_TIMEOUT;\r
-      Wrap->HttpToken->Status = Rx6Token->CompletionToken.Status;\r
-      gBS->SignalEvent (Wrap->HttpToken->Event);\r
-    }\r
   } else {\r
     Rx4Token = &Wrap->TcpWrap.Rx4Token;\r
     Rx4Token->Packet.RxData->DataLength = (UINT32) HttpMsg->BodyLength;\r
   } else {\r
     Rx4Token = &Wrap->TcpWrap.Rx4Token;\r
     Rx4Token->Packet.RxData->DataLength = (UINT32) HttpMsg->BodyLength;\r
@@ -1795,17 +1814,6 @@ HttpTcpReceiveBody (
       DEBUG ((EFI_D_ERROR, "Tcp4 receive failed: %r\n", Status));\r
       return Status;\r
     }\r
       DEBUG ((EFI_D_ERROR, "Tcp4 receive failed: %r\n", Status));\r
       return Status;\r
     }\r
-\r
-    while (!Wrap->TcpWrap.IsRxDone && ((Timeout == NULL) || EFI_ERROR (gBS->CheckEvent (Timeout)))) {\r
-      Tcp4->Poll (Tcp4);\r
-    }\r
-\r
-    if (!Wrap->TcpWrap.IsRxDone) {\r
-      gBS->CloseEvent (Rx4Token->CompletionToken.Event);\r
-      Rx4Token->CompletionToken.Status = EFI_TIMEOUT;\r
-      Wrap->HttpToken->Status = Rx4Token->CompletionToken.Status;\r
-      gBS->SignalEvent (Wrap->HttpToken->Event);\r
-    }\r
   }\r
 \r
   return EFI_SUCCESS;\r
   }\r
 \r
   return EFI_SUCCESS;\r
index cdad5b0e48c84aa91426941b2a79def54cab95d0..e1fd785b2cefe2d01c0f70c05e5cacefa8fe8f60 100644 (file)
@@ -525,7 +525,6 @@ HttpTcpReceiveHeader (
 \r
   @param[in]  Wrap               The HTTP token's wrap data.\r
   @param[in]  HttpMsg            The HTTP message data.\r
 \r
   @param[in]  Wrap               The HTTP token's wrap data.\r
   @param[in]  HttpMsg            The HTTP message data.\r
-  @param[in]  Timeout            The time to wait for receiving the body packet.\r
 \r
   @retval EFI_SUCCESS            The HTTP body is received.                          \r
   @retval Others                 Other error as indicated.\r
 \r
   @retval EFI_SUCCESS            The HTTP body is received.                          \r
   @retval Others                 Other error as indicated.\r
@@ -534,8 +533,7 @@ HttpTcpReceiveHeader (
 EFI_STATUS\r
 HttpTcpReceiveBody (\r
   IN  HTTP_TOKEN_WRAP       *Wrap,\r
 EFI_STATUS\r
 HttpTcpReceiveBody (\r
   IN  HTTP_TOKEN_WRAP       *Wrap,\r
-  IN  EFI_HTTP_MESSAGE      *HttpMsg,\r
-  IN  EFI_EVENT             Timeout\r
+  IN  EFI_HTTP_MESSAGE      *HttpMsg\r
   );\r
 \r
 /**\r
   );\r
 \r
 /**\r