Beispiel #1
0
/**
  The work function of EfiHttpResponse().

  @param[in]  Wrap                Pointer to HTTP token's wrap data.

  @retval EFI_SUCCESS             Allocation succeeded.
  @retval EFI_OUT_OF_RESOURCES    Failed to complete the opration due to lack of resources.
  @retval EFI_NOT_READY           Can't find a corresponding Tx4Token/Tx6Token or 
                                  the EFI_HTTP_UTILITIES_PROTOCOL is not available.

**/
EFI_STATUS
HttpResponseWorker (
  IN  HTTP_TOKEN_WRAP           *Wrap
  )
{
  EFI_STATUS                    Status;
  EFI_HTTP_MESSAGE              *HttpMsg;
  CHAR8                         *EndofHeader;
  CHAR8                         *HttpHeaders;
  UINTN                         SizeofHeaders;
  UINTN                         BufferSize;
  UINTN                         StatusCode;
  CHAR8                         *Tmp;
  CHAR8                         *HeaderTmp;
  CHAR8                         *StatusCodeStr;
  UINTN                         BodyLen;
  HTTP_PROTOCOL                 *HttpInstance;
  EFI_HTTP_TOKEN                *Token;
  NET_MAP_ITEM                  *Item;
  HTTP_TOKEN_WRAP               *ValueInItem;
  UINTN                         HdrLen;

  if (Wrap == NULL || Wrap->HttpInstance == NULL) {
    return EFI_INVALID_PARAMETER;
  }
  
  HttpInstance = Wrap->HttpInstance;
  Token = Wrap->HttpToken;
  HttpMsg = Token->Message;

  HttpInstance->EndofHeader = NULL;
  HttpInstance->HttpHeaders = NULL;
  HttpMsg->Headers          = NULL;
  HttpHeaders               = NULL;
  SizeofHeaders             = 0;
  BufferSize                = 0;
  EndofHeader               = NULL;
 
  if (HttpMsg->Data.Response != NULL) {
    //
    // Need receive the HTTP headers, prepare buffer.
    //
    Status = HttpCreateTcpRxEventForHeader (HttpInstance);
    if (EFI_ERROR (Status)) {
      goto Error;
    }

    //
    // Check whether we have cached header from previous call.
    //
    if ((HttpInstance->CacheBody != NULL) && (HttpInstance->NextMsg != NULL)) {
      //
      // The data is stored at [NextMsg, CacheBody + CacheLen].
      //
      HdrLen = HttpInstance->CacheBody + HttpInstance->CacheLen - HttpInstance->NextMsg;
      HttpHeaders = AllocateZeroPool (HdrLen);
      if (HttpHeaders == NULL) {
        Status = EFI_OUT_OF_RESOURCES;
        goto Error;
      }

      CopyMem (HttpHeaders, HttpInstance->NextMsg, HdrLen);
      FreePool (HttpInstance->CacheBody);
      HttpInstance->CacheBody   = NULL;
      HttpInstance->NextMsg     = NULL;
      HttpInstance->CacheOffset = 0;
      SizeofHeaders = HdrLen;
      BufferSize = HttpInstance->CacheLen;

      //
      // Check whether we cached the whole HTTP headers.
      //
      EndofHeader = AsciiStrStr (HttpHeaders, HTTP_END_OF_HDR_STR); 
    }   

    HttpInstance->EndofHeader = &EndofHeader;
    HttpInstance->HttpHeaders = &HttpHeaders;

    Status = HttpTcpReceiveHeader (HttpInstance, &SizeofHeaders, &BufferSize);
    if (EFI_ERROR (Status)) {
      goto Error;
    }

    ASSERT (HttpHeaders != NULL);

    //
    // Cache the part of body.
    //
    BodyLen = BufferSize - (EndofHeader - HttpHeaders);
    if (BodyLen > 0) {
      if (HttpInstance->CacheBody != NULL) {
        FreePool (HttpInstance->CacheBody);
      }

      HttpInstance->CacheBody = AllocateZeroPool (BodyLen);
      if (HttpInstance->CacheBody == NULL) {
        Status = EFI_OUT_OF_RESOURCES;
        goto Error;
      }

      CopyMem (HttpInstance->CacheBody, EndofHeader, BodyLen);
      HttpInstance->CacheLen = BodyLen;
    }

    //
    // Search for Status Code.
    //
    StatusCodeStr = HttpHeaders + AsciiStrLen (HTTP_VERSION_STR) + 1;
    if (StatusCodeStr == NULL) {
      goto Error;
    }

    StatusCode = AsciiStrDecimalToUintn (StatusCodeStr);

    //
    // Remove the first line of HTTP message, e.g. "HTTP/1.1 200 OK\r\n".
    //
    Tmp = AsciiStrStr (HttpHeaders, HTTP_CRLF_STR);
    if (Tmp == NULL) {
      goto Error;
    }

    Tmp = Tmp + AsciiStrLen (HTTP_CRLF_STR);
    SizeofHeaders = SizeofHeaders - (Tmp - HttpHeaders);
    HeaderTmp = AllocateZeroPool (SizeofHeaders);
    if (HeaderTmp == NULL) {
      goto Error;
    }

    CopyMem (HeaderTmp, Tmp, SizeofHeaders);
    FreePool (HttpHeaders);
    HttpHeaders = HeaderTmp;

    //
    // Check whether the EFI_HTTP_UTILITIES_PROTOCOL is available.
    //
    if (mHttpUtilities == NULL) {
      Status = EFI_NOT_READY;
      goto Error;
    }
    
    //
    // Parse the HTTP header into array of key/value pairs.
    //
    Status = mHttpUtilities->Parse (
                               mHttpUtilities, 
                               HttpHeaders, 
                               SizeofHeaders, 
                               &HttpMsg->Headers, 
                               &HttpMsg->HeaderCount
                               );
    if (EFI_ERROR (Status)) {
      goto Error;
    }

    FreePool (HttpHeaders);
    HttpHeaders = NULL;
    
    HttpMsg->Data.Response->StatusCode = HttpMappingToStatusCode (StatusCode);
    HttpInstance->StatusCode = StatusCode;
    //
    // Init message-body parser by header information.  
    //
    Status = EFI_NOT_READY;
    ValueInItem = NULL;
    NetMapRemoveHead (&HttpInstance->TxTokens, (VOID**) &ValueInItem);
    if (ValueInItem == NULL)  {
      goto Error;
    }

    //
    // The first Tx Token not transmitted yet, insert back and return error.
    //
    if (!ValueInItem->TcpWrap.IsTxDone) {
      goto Error2;
    }

    Status = HttpInitMsgParser (
               ValueInItem->TcpWrap.Method,
               HttpMsg->Data.Response->StatusCode,
               HttpMsg->HeaderCount,
               HttpMsg->Headers,
               HttpBodyParserCallback,
               (VOID *) ValueInItem,
               &HttpInstance->MsgParser
               );
    if (EFI_ERROR (Status)) {       
      goto Error2;
    }

    //
    // Check whether we received a complete HTTP message.
    //
    if (HttpInstance->CacheBody != NULL) {
      Status = HttpParseMessageBody (HttpInstance->MsgParser, HttpInstance->CacheLen, HttpInstance->CacheBody);
      if (EFI_ERROR (Status)) {
        goto Error2;
      }

      if (HttpIsMessageComplete (HttpInstance->MsgParser)) {
        //
        // Free the MsgParse since we already have a full HTTP message.
        //
        HttpFreeMsgParser (HttpInstance->MsgParser);
        HttpInstance->MsgParser = NULL;
      }
    }

    if ((HttpMsg->Body == NULL) || (HttpMsg->BodyLength == 0)) {    
      Status = EFI_SUCCESS;
      goto Exit;
    }
  }  

  //
  // Receive the response body.
  //
  BodyLen = 0;

  //
  // First check whether we cached some data.
  //
  if (HttpInstance->CacheBody != NULL) {
    //
    // Calculate the length of the cached data.
    //
    if (HttpInstance->NextMsg != NULL) {
      //
      // We have a cached HTTP message which includes a part of HTTP header of next message.
      //
      BodyLen = HttpInstance->NextMsg - (HttpInstance->CacheBody + HttpInstance->CacheOffset);      
    } else {
      BodyLen = HttpInstance->CacheLen - HttpInstance->CacheOffset;
    }

    if (BodyLen > 0) {
      //
      // We have some cached data. Just copy the data and return.
      //
      if (HttpMsg->BodyLength < BodyLen) {
        CopyMem (HttpMsg->Body, HttpInstance->CacheBody + HttpInstance->CacheOffset, HttpMsg->BodyLength);
        HttpInstance->CacheOffset = HttpInstance->CacheOffset + HttpMsg->BodyLength;
      } else {
        //
        // Copy all cached data out.
        //
        CopyMem (HttpMsg->Body, HttpInstance->CacheBody + HttpInstance->CacheOffset, BodyLen);
        HttpInstance->CacheOffset = BodyLen + HttpInstance->CacheOffset;
        HttpMsg->BodyLength = BodyLen;

        if (HttpInstance->NextMsg == NULL) {
          //
          // There is no HTTP header of next message. Just free the cache buffer.
          //
          FreePool (HttpInstance->CacheBody);
          HttpInstance->CacheBody   = NULL;
          HttpInstance->NextMsg     = NULL;
          HttpInstance->CacheOffset = 0;
        }
      }
      //
      // Return since we aready received required data.
      //
      Status = EFI_SUCCESS;
      goto Exit;
    } 

    if (BodyLen == 0 && HttpInstance->MsgParser == NULL) {
      //
      // We received a complete HTTP message, and we don't have more data to return to caller.
      //
      HttpMsg->BodyLength = 0;
      Status = EFI_SUCCESS;
      goto Exit;      
    }    
  }

  ASSERT (HttpInstance->MsgParser != NULL);

  //
  // We still need receive more data when there is no cache data and MsgParser is not NULL;
  //
  Status = HttpTcpReceiveBody (Wrap, HttpMsg);
  if (EFI_ERROR (Status)) {
    goto Error;
  }

  return Status;

Exit:
  Item = NetMapFindKey (&Wrap->HttpInstance->RxTokens, Wrap->HttpToken);
  if (Item != NULL) {
    NetMapRemoveItem (&Wrap->HttpInstance->RxTokens, Item, NULL);
  }

  if (HttpInstance->StatusCode >= HTTP_ERROR_OR_NOT_SUPPORT_STATUS_CODE) {
    Token->Status = EFI_HTTP_ERROR;
  } else {
    Token->Status = Status;
  }

  gBS->SignalEvent (Token->Event);
  HttpCloseTcpRxEvent (Wrap);
  FreePool (Wrap);
  return Status;

Error2:
  NetMapInsertHead (&HttpInstance->TxTokens, ValueInItem->HttpToken, ValueInItem);

Error:
  HttpTcpTokenCleanup (Wrap);
  
  if (HttpHeaders != NULL) {
    FreePool (HttpHeaders);
  }

  if (HttpMsg->Headers != NULL) {
    FreePool (HttpMsg->Headers);
  }

  if (HttpInstance->CacheBody != NULL) {
    FreePool (HttpInstance->CacheBody);
    HttpInstance->CacheBody = NULL;
  }

  if (HttpInstance->StatusCode >= HTTP_ERROR_OR_NOT_SUPPORT_STATUS_CODE) {
    Token->Status = EFI_HTTP_ERROR;
  } else {
    Token->Status = Status;
  }

  gBS->SignalEvent (Token->Event);

  return Status;  

}
Beispiel #2
0
/**
  The work function of EfiHttpResponse().

  @param[in]  Wrap                Pointer to HTTP token's wrap data.

  @retval EFI_SUCCESS             Allocation succeeded.
  @retval EFI_OUT_OF_RESOURCES    Failed to complete the opration due to lack of resources.
  @retval EFI_NOT_READY           Can't find a corresponding TxToken.

**/
EFI_STATUS
HttpResponseWorker (
  IN  HTTP_TOKEN_WRAP           *Wrap
  )
{
  EFI_STATUS                    Status;
  EFI_HTTP_MESSAGE              *HttpMsg;
  EFI_TCP4_IO_TOKEN             *RxToken;
  EFI_TCP4_PROTOCOL             *Tcp4;
  CHAR8                         *EndofHeader;
  CHAR8                         *HttpHeaders;
  UINTN                         SizeofHeaders;
  CHAR8                         *Buffer;
  UINTN                         BufferSize;
  UINTN                         StatusCode;
  CHAR8                         *Tmp;
  CHAR8                         *HeaderTmp;
  CHAR8                         *StatusCodeStr;
  UINTN                         BodyLen;
  HTTP_PROTOCOL                 *HttpInstance;
  EFI_HTTP_TOKEN                *Token;
  NET_MAP_ITEM                  *Item;
  HTTP_TOKEN_WRAP               *ValueInItem;
  UINTN                         HdrLen;

  if (Wrap == NULL || Wrap->HttpInstance == NULL) {
    return EFI_INVALID_PARAMETER;
  }
  
  HttpInstance = Wrap->HttpInstance;
  Token = Wrap->HttpToken;

  HttpMsg = Token->Message;

  Tcp4 = HttpInstance->Tcp4;
  ASSERT (Tcp4 != NULL);
  HttpMsg->Headers = NULL;
  HttpHeaders   = NULL;
  SizeofHeaders = 0;
  Buffer        = NULL;
  BufferSize    = 0;
  EndofHeader   = NULL;
 
  if (HttpMsg->Data.Response != NULL) {
    //
    // Need receive the HTTP headers, prepare buffer.
    //
    Status = HttpCreateTcp4RxEventForHeader (HttpInstance);
    if (EFI_ERROR (Status)) {
      goto Error;
    }

    //
    // Check whether we have cached header from previous call.
    //
    if ((HttpInstance->CacheBody != NULL) && (HttpInstance->NextMsg != NULL)) {
      //
      // The data is stored at [NextMsg, CacheBody + CacheLen].
      //
      HdrLen = HttpInstance->CacheBody + HttpInstance->CacheLen - HttpInstance->NextMsg;
      HttpHeaders = AllocateZeroPool (HdrLen);
      if (HttpHeaders == NULL) {
        Status = EFI_OUT_OF_RESOURCES;
        goto Error;
      }

      CopyMem (HttpHeaders, HttpInstance->NextMsg, HdrLen);
      FreePool (HttpInstance->CacheBody);
      HttpInstance->CacheBody   = NULL;
      HttpInstance->NextMsg     = NULL;
      HttpInstance->CacheOffset = 0;
      SizeofHeaders = HdrLen;
      BufferSize = HttpInstance->CacheLen;

      //
      // Check whether we cached the whole HTTP headers.
      //
      EndofHeader = AsciiStrStr (HttpHeaders, HTTP_END_OF_HDR_STR); 
    }
    
    RxToken = &HttpInstance->RxToken;
    RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer = AllocateZeroPool (DEF_BUF_LEN);
    if (RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer == NULL) {
      Status = EFI_OUT_OF_RESOURCES;
      goto Error;
    }

    //
    // Receive the HTTP headers only when EFI_HTTP_RESPONSE_DATA is not NULL.
    //
    while (EndofHeader == NULL) {   
      HttpInstance->IsRxDone = FALSE;
      RxToken->Packet.RxData->DataLength = DEF_BUF_LEN;
      RxToken->Packet.RxData->FragmentTable[0].FragmentLength = DEF_BUF_LEN;
      Status = Tcp4->Receive (Tcp4, RxToken);
      if (EFI_ERROR (Status)) {
        DEBUG ((EFI_D_ERROR, "Tcp4 receive failed: %r\n", Status));
        goto Error;
      }
      
      while (!HttpInstance->IsRxDone) {
       Tcp4->Poll (Tcp4);
      }    

      Status = RxToken->CompletionToken.Status;
      if (EFI_ERROR (Status)) {
        goto Error;
      }

      //
      // Append the response string.
      //
      BufferSize = SizeofHeaders + RxToken->Packet.RxData->FragmentTable[0].FragmentLength;
      Buffer = AllocateZeroPool (BufferSize);
      if (Buffer == NULL) {
        Status = EFI_OUT_OF_RESOURCES;
        goto Error;
      }

      if (HttpHeaders != NULL) {
        CopyMem (Buffer, HttpHeaders, SizeofHeaders);
        FreePool (HttpHeaders);
      }

      CopyMem (
        Buffer + SizeofHeaders,
        RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer,
        RxToken->Packet.RxData->FragmentTable[0].FragmentLength
        );
      HttpHeaders   = Buffer;
      SizeofHeaders = BufferSize;

      //
      // Check whether we received end of HTTP headers.
      //
      EndofHeader = AsciiStrStr (HttpHeaders, HTTP_END_OF_HDR_STR); 
    };

    //
    // Skip the CRLF after the HTTP headers.
    //
    EndofHeader = EndofHeader + AsciiStrLen (HTTP_END_OF_HDR_STR);

    //
    // Cache the part of body.
    //
    BodyLen = BufferSize - (EndofHeader - HttpHeaders);
    if (BodyLen > 0) {
      if (HttpInstance->CacheBody != NULL) {
        FreePool (HttpInstance->CacheBody);
      }

      HttpInstance->CacheBody = AllocateZeroPool (BodyLen);
      if (HttpInstance->CacheBody == NULL) {
        Status = EFI_OUT_OF_RESOURCES;
        goto Error;
      }

      CopyMem (HttpInstance->CacheBody, EndofHeader, BodyLen);
      HttpInstance->CacheLen = BodyLen;
    }

    FreePool (RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer);
    RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer = NULL;

    //
    // Search for Status Code.
    //
    StatusCodeStr = HttpHeaders + AsciiStrLen (HTTP_VERSION_STR) + 1;
    if (StatusCodeStr == NULL) {
      goto Error;
    }

    StatusCode = AsciiStrDecimalToUintn (StatusCodeStr);

    //
    // Remove the first line of HTTP message, e.g. "HTTP/1.1 200 OK\r\n".
    //
    Tmp = AsciiStrStr (HttpHeaders, HTTP_CRLF_STR);
    if (Tmp == NULL) {
      goto Error;
    }

    Tmp = Tmp + AsciiStrLen (HTTP_CRLF_STR);
    SizeofHeaders = SizeofHeaders - (Tmp - HttpHeaders);
    HeaderTmp = AllocateZeroPool (SizeofHeaders);
    if (HeaderTmp == NULL) {
      goto Error;
    }

    CopyMem (HeaderTmp, Tmp, SizeofHeaders);
    FreePool (HttpHeaders);
    HttpHeaders = HeaderTmp;
    //
    // Parse the HTTP header into array of key/value pairs.
    //
    Status = HttpUtilitiesParse (HttpHeaders, SizeofHeaders, &HttpMsg->Headers, &HttpMsg->HeaderCount);
    if (EFI_ERROR (Status)) {
      goto Error;
    }

    FreePool (HttpHeaders);
    HttpHeaders = NULL;
    
    HttpMsg->Data.Response->StatusCode = HttpMappingToStatusCode (StatusCode);

    //
    // Init message-body parser by header information.  
    //
    Status = EFI_NOT_READY;
    ValueInItem = NULL;
    NetMapRemoveHead (&HttpInstance->TxTokens, (VOID**) &ValueInItem);
    if (ValueInItem == NULL)  {
      goto Error;
    }

    //
    // The first TxToken not transmitted yet, insert back and return error.
    //
    if (!ValueInItem->TcpWrap.IsTxDone) {
      goto Error2;
    }

    Status = HttpInitMsgParser (
               ValueInItem->TcpWrap.Method,
               HttpMsg->Data.Response->StatusCode,
               HttpMsg->HeaderCount,
               HttpMsg->Headers,
               HttpBodyParserCallback,
               (VOID *) ValueInItem,
               &HttpInstance->MsgParser
               );
    if (EFI_ERROR (Status)) {       
      goto Error2;
    }

    //
    // Check whether we received a complete HTTP message.
    //
    if (HttpInstance->CacheBody != NULL) {
      Status = HttpParseMessageBody (HttpInstance->MsgParser, HttpInstance->CacheLen, HttpInstance->CacheBody);
      if (EFI_ERROR (Status)) {
        goto Error2;
      }

      if (HttpIsMessageComplete (HttpInstance->MsgParser)) {
        //
        // Free the MsgParse since we already have a full HTTP message.
        //
        HttpFreeMsgParser (HttpInstance->MsgParser);
        HttpInstance->MsgParser = NULL;
      }
    }

    if ((HttpMsg->Body == NULL) || (HttpMsg->BodyLength == 0)) {    
      Status = EFI_SUCCESS;
      goto Exit;
    }
  }  

  //
  // Receive the response body.
  //
  BodyLen = 0;

  //
  // First check whether we cached some data.
  //
  if (HttpInstance->CacheBody != NULL) {
    //
    // Calculate the length of the cached data.
    //
    if (HttpInstance->NextMsg != NULL) {
      //
      // We have a cached HTTP message which includes a part of HTTP header of next message.
      //
      BodyLen = HttpInstance->NextMsg - (HttpInstance->CacheBody + HttpInstance->CacheOffset);      
    } else {
      BodyLen = HttpInstance->CacheLen - HttpInstance->CacheOffset;
    }

    if (BodyLen > 0) {
      //
      // We have some cached data. Just copy the data and return.
      //
      if (HttpMsg->BodyLength < BodyLen) {
        CopyMem (HttpMsg->Body, HttpInstance->CacheBody + HttpInstance->CacheOffset, HttpMsg->BodyLength);
        HttpInstance->CacheOffset = HttpInstance->CacheOffset + HttpMsg->BodyLength;
      } else {
        //
        // Copy all cached data out.
        //
        CopyMem (HttpMsg->Body, HttpInstance->CacheBody + HttpInstance->CacheOffset, BodyLen);
        HttpInstance->CacheOffset = BodyLen + HttpInstance->CacheOffset;
        HttpMsg->BodyLength = BodyLen;

        if (HttpInstance->NextMsg == NULL) {
          //
          // There is no HTTP header of next message. Just free the cache buffer.
          //
          FreePool (HttpInstance->CacheBody);
          HttpInstance->CacheBody   = NULL;
          HttpInstance->NextMsg     = NULL;
          HttpInstance->CacheOffset = 0;
        }
      }
      //
      // Return since we aready received required data.
      //
      Status = EFI_SUCCESS;
      goto Exit;
    } 

    if (BodyLen == 0 && HttpInstance->MsgParser == NULL) {
      //
      // We received a complete HTTP message, and we don't have more data to return to caller.
      //
      HttpMsg->BodyLength = 0;
      Status = EFI_SUCCESS;
      goto Exit;      
    }    
  }

  ASSERT (HttpInstance->MsgParser != NULL);

  //
  // We still need receive more data when there is no cache data and MsgParser is not NULL;
  //
  RxToken = &Wrap->TcpWrap.RxToken;

  RxToken->Packet.RxData->DataLength = (UINT32) HttpMsg->BodyLength;
  RxToken->Packet.RxData->FragmentTable[0].FragmentLength = (UINT32) HttpMsg->BodyLength;
  RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer = (VOID *) HttpMsg->Body;

  RxToken->CompletionToken.Status = EFI_NOT_READY;
  Status = Tcp4->Receive (Tcp4, RxToken);
  if (EFI_ERROR (Status)) {
    DEBUG ((EFI_D_ERROR, "Tcp4 receive failed: %r\n", Status));
    goto Error;
  }

  return Status;

Exit:
  Item = NetMapFindKey (&Wrap->HttpInstance->RxTokens, Wrap->HttpToken);
  if (Item != NULL) {
    NetMapRemoveItem (&Wrap->HttpInstance->RxTokens, Item, NULL);
  }
  Token->Status = Status;
  gBS->SignalEvent (Token->Event);
  FreePool (Wrap);
  return Status;

Error2:
  NetMapInsertHead (&HttpInstance->TxTokens, ValueInItem->HttpToken, ValueInItem);

Error:
  if (Wrap != NULL) {
    if (Wrap->TcpWrap.RxToken.CompletionToken.Event != NULL) {
      gBS->CloseEvent (Wrap->TcpWrap.RxToken.CompletionToken.Event);
    }
    RxToken = &Wrap->TcpWrap.RxToken;
    if (RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer != NULL) {
      FreePool (RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer);
      RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer = NULL;
    }
    FreePool (Wrap);
  }

  if (HttpInstance->RxToken.CompletionToken.Event != NULL) {
    gBS->CloseEvent (HttpInstance->RxToken.CompletionToken.Event);
    HttpInstance->RxToken.CompletionToken.Event = NULL;
  }

  RxToken = &HttpInstance->RxToken;
  if (RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer != NULL) {
    FreePool (RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer);
    RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer = NULL;
  }
  
  if (HttpHeaders != NULL) {
    FreePool (HttpHeaders);
  }

  if (HttpMsg->Headers != NULL) {
    FreePool (HttpMsg->Headers);
  }

  if (HttpInstance->CacheBody != NULL) {
    FreePool (HttpInstance->CacheBody);
    HttpInstance->CacheBody = NULL;
  }

  Token->Status = Status;
  gBS->SignalEvent (Token->Event);

  return Status;  

}
Beispiel #3
0
/**
  The work function of EfiHttpResponse().

  @param[in]  Wrap                Pointer to HTTP token's wrap data.

  @retval EFI_SUCCESS             Allocation succeeded.
  @retval EFI_OUT_OF_RESOURCES    Failed to complete the opration due to lack of resources.
  @retval EFI_NOT_READY           Can't find a corresponding Tx4Token/Tx6Token or 
                                  the EFI_HTTP_UTILITIES_PROTOCOL is not available.

**/
EFI_STATUS
HttpResponseWorker (
  IN  HTTP_TOKEN_WRAP           *Wrap
  )
{
  EFI_STATUS                    Status;
  EFI_HTTP_MESSAGE              *HttpMsg;
  CHAR8                         *EndofHeader;
  CHAR8                         *HttpHeaders;
  UINTN                         SizeofHeaders;
  UINTN                         BufferSize;
  UINTN                         StatusCode;
  CHAR8                         *Tmp;
  CHAR8                         *HeaderTmp;
  CHAR8                         *StatusCodeStr;
  UINTN                         BodyLen;
  HTTP_PROTOCOL                 *HttpInstance;
  EFI_HTTP_TOKEN                *Token;
  NET_MAP_ITEM                  *Item;
  HTTP_TOKEN_WRAP               *ValueInItem;
  UINTN                         HdrLen;

  if (Wrap == NULL || Wrap->HttpInstance == NULL) {
    return EFI_INVALID_PARAMETER;
  }
  
  HttpInstance = Wrap->HttpInstance;
  Token = Wrap->HttpToken;
  HttpMsg = Token->Message;

  HttpInstance->EndofHeader = NULL;
  HttpInstance->HttpHeaders = NULL;
  HttpMsg->Headers          = NULL;
  HttpHeaders               = NULL;
  SizeofHeaders             = 0;
  BufferSize                = 0;
  EndofHeader               = NULL;
 
  if (HttpMsg->Data.Response != NULL) {
    //
    // Need receive the HTTP headers, prepare buffer.
    //
    Status = HttpCreateTcpRxEventForHeader (HttpInstance);
    if (EFI_ERROR (Status)) {
      goto Error;
    }

    //
    // Check whether we have cached header from previous call.
    //
    if ((HttpInstance->CacheBody != NULL) && (HttpInstance->NextMsg != NULL)) {
      //
      // The data is stored at [NextMsg, CacheBody + CacheLen].
      //
      HdrLen = HttpInstance->CacheBody + HttpInstance->CacheLen - HttpInstance->NextMsg;
      HttpHeaders = AllocateZeroPool (HdrLen);
      if (HttpHeaders == NULL) {
        Status = EFI_OUT_OF_RESOURCES;
        goto Error;
      }

      CopyMem (HttpHeaders, HttpInstance->NextMsg, HdrLen);
      FreePool (HttpInstance->CacheBody);
      HttpInstance->CacheBody   = NULL;
      HttpInstance->NextMsg     = NULL;
      HttpInstance->CacheOffset = 0;
      SizeofHeaders = HdrLen;
      BufferSize = HttpInstance->CacheLen;

      //
      // Check whether we cached the whole HTTP headers.
      //
      EndofHeader = AsciiStrStr (HttpHeaders, HTTP_END_OF_HDR_STR); 
    }   

    HttpInstance->EndofHeader = &EndofHeader;
    HttpInstance->HttpHeaders = &HttpHeaders;


    if (HttpInstance->TimeoutEvent == NULL) {
      //
      // Create TimeoutEvent for response
      //
      Status = gBS->CreateEvent (
                      EVT_TIMER,
                      TPL_CALLBACK,
                      NULL,
                      NULL,
                      &HttpInstance->TimeoutEvent
                      );
      if (EFI_ERROR (Status)) {
        goto Error;
      }
    }

    //
    // Start the timer, and wait Timeout seconds to receive the header packet.
    //
    Status = gBS->SetTimer (HttpInstance->TimeoutEvent, TimerRelative, HTTP_RESPONSE_TIMEOUT * TICKS_PER_SECOND);
    if (EFI_ERROR (Status)) {
      goto Error;
    }

    Status = HttpTcpReceiveHeader (HttpInstance, &SizeofHeaders, &BufferSize, HttpInstance->TimeoutEvent);

    gBS->SetTimer (HttpInstance->TimeoutEvent, TimerCancel, 0);

    if (EFI_ERROR (Status)) {
      goto Error;
    }

    ASSERT (HttpHeaders != NULL);

    //
    // Cache the part of body.
    //
    BodyLen = BufferSize - (EndofHeader - HttpHeaders);
    if (BodyLen > 0) {
      if (HttpInstance->CacheBody != NULL) {
        FreePool (HttpInstance->CacheBody);
      }

      HttpInstance->CacheBody = AllocateZeroPool (BodyLen);
      if (HttpInstance->CacheBody == NULL) {
        Status = EFI_OUT_OF_RESOURCES;
        goto Error;
      }

      CopyMem (HttpInstance->CacheBody, EndofHeader, BodyLen);
      HttpInstance->CacheLen = BodyLen;
    }

    //
    // Search for Status Code.
    //
    StatusCodeStr = HttpHeaders + AsciiStrLen (HTTP_VERSION_STR) + 1;
    if (StatusCodeStr == NULL) {
      goto Error;
    }

    StatusCode = AsciiStrDecimalToUintn (StatusCodeStr);

    //
    // Remove the first line of HTTP message, e.g. "HTTP/1.1 200 OK\r\n".
    //
    Tmp = AsciiStrStr (HttpHeaders, HTTP_CRLF_STR);
    if (Tmp == NULL) {
      goto Error;
    }

    //
    // We could have response with just a HTTP message and no headers. For Example,
    // "100 Continue". In such cases, we would not want to unnecessarily call a Parse
    // method. A "\r\n" following Tmp string again would indicate an end. Compare and
    // set SizeofHeaders to 0.
    //
    Tmp = Tmp + AsciiStrLen (HTTP_CRLF_STR);
    if (CompareMem (Tmp, HTTP_CRLF_STR, AsciiStrLen (HTTP_CRLF_STR)) == 0) {
      SizeofHeaders = 0;
    } else {
      SizeofHeaders = SizeofHeaders - (Tmp - HttpHeaders);
    }

    HttpMsg->Data.Response->StatusCode = HttpMappingToStatusCode (StatusCode);
    HttpInstance->StatusCode = StatusCode;

    Status = EFI_NOT_READY;
    ValueInItem = NULL;

    //
    // In cases of PUT/POST, after an initial request-response pair, we would do a
    // continuous request without a response call. So, we would not do an insert of
    // TxToken. After we have sent the complete file, we will call a response to get
    // a final response from server. In such a case, we would not have any TxTokens.
    // Hence, check that case before doing a NetMapRemoveHead.
    //
    if (!NetMapIsEmpty (&HttpInstance->TxTokens)) {
      NetMapRemoveHead (&HttpInstance->TxTokens, (VOID**) &ValueInItem);
      if (ValueInItem == NULL)  {
        goto Error;
      }

      //
      // The first Tx Token not transmitted yet, insert back and return error.
      //
      if (!ValueInItem->TcpWrap.IsTxDone) {
        goto Error2;
      }
    }

    if (SizeofHeaders != 0) {
      HeaderTmp = AllocateZeroPool (SizeofHeaders);
      if (HeaderTmp == NULL) {
        goto Error;
      }

      CopyMem (HeaderTmp, Tmp, SizeofHeaders);
      FreePool (HttpHeaders);
      HttpHeaders = HeaderTmp;

      //
      // Check whether the EFI_HTTP_UTILITIES_PROTOCOL is available.
      //
      if (mHttpUtilities == NULL) {
        Status = EFI_NOT_READY;
        goto Error;
      }

      //
      // Parse the HTTP header into array of key/value pairs.
      //
      Status = mHttpUtilities->Parse (
                                 mHttpUtilities,
                                 HttpHeaders,
                                 SizeofHeaders,
                                 &HttpMsg->Headers,
                                 &HttpMsg->HeaderCount
                                 );
      if (EFI_ERROR (Status)) {
        goto Error;
      }

      FreePool (HttpHeaders);
      HttpHeaders = NULL;


      //
      // Init message-body parser by header information.
      //
      Status = HttpInitMsgParser (
                 HttpInstance->Method,
                 HttpMsg->Data.Response->StatusCode,
                 HttpMsg->HeaderCount,
                 HttpMsg->Headers,
                 HttpBodyParserCallback,
                 (VOID *) ValueInItem,
                 &HttpInstance->MsgParser
                 );
      if (EFI_ERROR (Status)) {
        goto Error2;
      }

      //
      // Check whether we received a complete HTTP message.
      //
      if (HttpInstance->CacheBody != NULL) {
        Status = HttpParseMessageBody (HttpInstance->MsgParser, HttpInstance->CacheLen, HttpInstance->CacheBody);
        if (EFI_ERROR (Status)) {
          goto Error2;
        }

        if (HttpIsMessageComplete (HttpInstance->MsgParser)) {
          //
          // Free the MsgParse since we already have a full HTTP message.
          //
          HttpFreeMsgParser (HttpInstance->MsgParser);
          HttpInstance->MsgParser = NULL;
        }
      }
    }

    if ((HttpMsg->Body == NULL) || (HttpMsg->BodyLength == 0)) {
      Status = EFI_SUCCESS;
      goto Exit;
    }
  }

  //
  // Receive the response body.
  //
  BodyLen = 0;

  //
  // First check whether we cached some data.
  //
  if (HttpInstance->CacheBody != NULL) {
    //
    // Calculate the length of the cached data.
    //
    if (HttpInstance->NextMsg != NULL) {
      //
      // We have a cached HTTP message which includes a part of HTTP header of next message.
      //
      BodyLen = HttpInstance->NextMsg - (HttpInstance->CacheBody + HttpInstance->CacheOffset);      
    } else {
      BodyLen = HttpInstance->CacheLen - HttpInstance->CacheOffset;
    }

    if (BodyLen > 0) {
      //
      // We have some cached data. Just copy the data and return.
      //
      if (HttpMsg->BodyLength < BodyLen) {
        CopyMem (HttpMsg->Body, HttpInstance->CacheBody + HttpInstance->CacheOffset, HttpMsg->BodyLength);
        HttpInstance->CacheOffset = HttpInstance->CacheOffset + HttpMsg->BodyLength;
      } else {
        //
        // Copy all cached data out.
        //
        CopyMem (HttpMsg->Body, HttpInstance->CacheBody + HttpInstance->CacheOffset, BodyLen);
        HttpInstance->CacheOffset = BodyLen + HttpInstance->CacheOffset;
        HttpMsg->BodyLength = BodyLen;

        if (HttpInstance->NextMsg == NULL) {
          //
          // There is no HTTP header of next message. Just free the cache buffer.
          //
          FreePool (HttpInstance->CacheBody);
          HttpInstance->CacheBody   = NULL;
          HttpInstance->NextMsg     = NULL;
          HttpInstance->CacheOffset = 0;
        }
      }
      //
      // Return since we aready received required data.
      //
      Status = EFI_SUCCESS;
      goto Exit;
    } 

    if (BodyLen == 0 && HttpInstance->MsgParser == NULL) {
      //
      // We received a complete HTTP message, and we don't have more data to return to caller.
      //
      HttpMsg->BodyLength = 0;
      Status = EFI_SUCCESS;
      goto Exit;      
    }    
  }

  ASSERT (HttpInstance->MsgParser != NULL);

  if (HttpInstance->TimeoutEvent == NULL) {
    //
    // Create TimeoutEvent for response
    //
    Status = gBS->CreateEvent (
                    EVT_TIMER,
                    TPL_CALLBACK,
                    NULL,
                    NULL,
                    &HttpInstance->TimeoutEvent
                    );
    if (EFI_ERROR (Status)) {
      goto Error;
    }
  }

  //
  // Start the timer, and wait Timeout seconds to receive the body packet.
  //
  Status = gBS->SetTimer (HttpInstance->TimeoutEvent, TimerRelative, HTTP_RESPONSE_TIMEOUT * TICKS_PER_SECOND);
  if (EFI_ERROR (Status)) {
    goto Error;
  }

  //
  // We still need receive more data when there is no cache data and MsgParser is not NULL;
  //
  Status = HttpTcpReceiveBody (Wrap, HttpMsg, HttpInstance->TimeoutEvent);

  gBS->SetTimer (HttpInstance->TimeoutEvent, TimerCancel, 0);

  if (EFI_ERROR (Status)) {
    goto Error;
  }

  FreePool (Wrap);
  return Status;

Exit:
  Item = NetMapFindKey (&Wrap->HttpInstance->RxTokens, Wrap->HttpToken);
  if (Item != NULL) {
    NetMapRemoveItem (&Wrap->HttpInstance->RxTokens, Item, NULL);
  }

  if (HttpInstance->StatusCode >= HTTP_ERROR_OR_NOT_SUPPORT_STATUS_CODE) {
    Token->Status = EFI_HTTP_ERROR;
  } else {
    Token->Status = Status;
  }

  gBS->SignalEvent (Token->Event);
  HttpCloseTcpRxEvent (Wrap);
  FreePool (Wrap);
  return Status;

Error2:
  NetMapInsertHead (&HttpInstance->TxTokens, ValueInItem->HttpToken, ValueInItem);

Error:
  HttpTcpTokenCleanup (Wrap);
  
  if (HttpHeaders != NULL) {
    FreePool (HttpHeaders);
  }

  if (HttpMsg->Headers != NULL) {
    FreePool (HttpMsg->Headers);
  }

  if (HttpInstance->CacheBody != NULL) {
    FreePool (HttpInstance->CacheBody);
    HttpInstance->CacheBody = NULL;
  }

  if (HttpInstance->StatusCode >= HTTP_ERROR_OR_NOT_SUPPORT_STATUS_CODE) {
    Token->Status = EFI_HTTP_ERROR;
  } else {
    Token->Status = Status;
  }

  gBS->SignalEvent (Token->Event);

  return Status;  

}