Search code examples
httpeventshttpsuefiget-request

UEFI Application: Unable to fetch the body of Http GET request


I am developing a UEFI application using the EDK2 environment and attempting to perform an HTTP GET request using the UEFI APIs. The event is not getting signaled after the Http->Response call. If i remove the part where i am waiting for the event then the connection is successfully established to the server, and I am able to receive the HTTP response headers without any issues. The response status code is 200 OK, indicating that the request was successful. But the body is still empty in this case.

  1. I attempted to introduce a delay to wait for the response body to be fully received, but the body remains empty.
  2. I tried using events to handle the response, but they were not getting signaled.

I would appreciate any insights or suggestions on how to troubleshoot this issue. Are there specific UEFI APIs or steps I should review to ensure that I am correctly reading the response body? Any help would be greatly appreciated!

sendHttpRequest(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable)
{
    EFI_STATUS Status;

    EFI_HTTP_PROTOCOL *Http;
    EFI_SERVICE_BINDING_PROTOCOL *HttpServiceBinding;
    EFI_HANDLE HttpHandle = NULL;
    EFI_HTTP_CONFIG_DATA HttpConfigData;
    EFI_HTTP_REQUEST_DATA RequestData;
    EFI_HTTP_RESPONSE_DATA ResponseData;
    EFI_HTTP_TOKEN RequestToken, ResponseToken;
    EFI_HTTP_MESSAGE RequestMessage, ResponseMessage;
    EFI_HTTP_HEADER Headers[2];
    CHAR16 *RequestUrl = L"http://" BOOT_SERVER_IP BOOT_CONFIG_PATH;
    CHAR8 *Buffer;
    BOOLEAN response_done;
    BOOLEAN request_done;
    UINTN Index;

    Print(L"Starting HttpBootClient default DHCP example\r\n");

    Status = gBS->LocateProtocol(&gEfiHttpServiceBindingProtocolGuid, NULL, (VOID **)&HttpServiceBinding);
    if (EFI_ERROR(Status))
    {
        Print(L"Failed to locate HTTP service binding protocol: %r\n", Status);
        return Status;
    }

    Status = HttpServiceBinding->CreateChild(HttpServiceBinding, &HttpHandle);
    if (EFI_ERROR(Status))
    {
        Print(L"Failed to create HTTP child: %r\n", Status);
        return Status;
    }

    Status = gBS->HandleProtocol(HttpHandle, &gEfiHttpProtocolGuid, (VOID **)&Http);
    if (EFI_ERROR(Status))
    {
        Print(L"Failed to locate HTTP protocol: %r\n", Status);
        return Status;
    }

    ZeroMem(&HttpConfigData, sizeof(HttpConfigData));
    HttpConfigData.HttpVersion = HttpVersion11;
    HttpConfigData.TimeOutMillisec = 500;
    HttpConfigData.LocalAddressIsIPv6 = FALSE;
    HttpConfigData.AccessPoint.IPv4Node = AllocatePool(sizeof(EFI_HTTPv4_ACCESS_POINT));
    if (!HttpConfigData.AccessPoint.IPv4Node)
    {
        Print(L"Failed to allocate memory for the structure\n");
        return EFI_OUT_OF_RESOURCES;
    }
    HttpConfigData.AccessPoint.IPv4Node->UseDefaultAddress = TRUE;

    Status = Http->Configure(Http, &HttpConfigData);
    if (EFI_ERROR(Status))
    {
        Print(L"Failed to configure HTTP protocol: %r\n", Status);
        return Status;
    }

    ZeroMem(&RequestData, sizeof(RequestData));
    RequestData.Method = HttpMethodGet;
    RequestData.Url = RequestUrl;

    ZeroMem(Headers, sizeof(Headers));
    Headers[0].FieldName = "Host";
    Headers[0].FieldValue = BOOT_SERVER_IP;
    Headers[1].FieldName = "User-Agent";
    Headers[1].FieldValue = "UEFI-Client";

    ZeroMem(&RequestMessage, sizeof(RequestMessage));
    RequestMessage.Data.Request = &RequestData;
    RequestMessage.Headers = Headers;
    RequestMessage.HeaderCount = 2;
    RequestMessage.BodyLength = 0;
    RequestMessage.Body = NULL;

    ZeroMem(&RequestToken, sizeof(RequestToken));
    RequestToken.Event = NULL;
    RequestToken.Message = &RequestMessage;

    request_done = FALSE;
    Status = gBS->CreateEvent(EVT_NOTIFY_SIGNAL, TPL_CALLBACK, httpnotify, &request_done, &RequestToken.Event);
    if (EFI_ERROR(Status))
    {
        Print(L"Failed to create request event: %r\n", Status);
        Http->Configure(Http, NULL);
        HttpServiceBinding->DestroyChild(HttpServiceBinding, HttpHandle);
        return Status;
    }
    else{
        Print(L"Event value : %p\n", RequestToken.Event);
    }

    Status = Http->Request(Http, &RequestToken);
    if (EFI_ERROR(Status))
    {
        Print(L"Failed to send HTTP request: %r\n", Status);
        Http->Configure(Http, NULL);
        HttpServiceBinding->DestroyChild(HttpServiceBinding, HttpHandle);
        return Status;
    }

    while (!request_done)
    {
        Status = Http->Poll(Http);
        Print(L"HTTP Poll: %r\n", Status);
    }
    Print(L"Request Sent : %r\n", Status);

    ZeroMem(&ResponseMessage, sizeof(ResponseMessage));
    ResponseData.StatusCode = HTTP_STATUS_UNSUPPORTED_STATUS;
    ResponseMessage.Data.Response = &ResponseData;
    ResponseMessage.HeaderCount = 0;
    ResponseMessage.Headers = NULL;
    ResponseMessage.BodyLength = BUFFER_SIZE; 

    Status = gBS->AllocatePool(
        EfiBootServicesData,
        ResponseMessage.BodyLength,
        (VOID **)&Buffer);
    if (!Buffer)
    {
        Print(L"Failed to allocate memory for response body\n");
        Http->Configure(Http, NULL);
        HttpServiceBinding->DestroyChild(HttpServiceBinding, HttpHandle);
        return EFI_OUT_OF_RESOURCES;
    }
    ZeroMem(Buffer, sizeof(BUFFER_SIZE));
    ResponseMessage.Body = Buffer;

    ZeroMem(&ResponseToken, sizeof(ResponseToken));
    ResponseToken.Status = EFI_SUCCESS;
    ResponseToken.Message = &ResponseMessage;
    ResponseToken.Event = NULL;
    response_done = FALSE;
    Status = gBS->CreateEvent(EVT_NOTIFY_SIGNAL, TPL_CALLBACK, httpnotify, &response_done, &ResponseToken.Event);
    if (EFI_ERROR(Status))
    {
        Print(L"Failed to create response event: %r\n", Status);
        Http->Configure(Http, NULL);
        HttpServiceBinding->DestroyChild(HttpServiceBinding, HttpHandle);
        return Status;
    }
    else{
        Print(L"Event value : %p\n", ResponseToken.Event);
    }

    Status = Http->Response(Http, &ResponseToken);
    
    if (!EFI_ERROR(Status))
    {
        while (!response_done)
        {
            Status = Http->Poll(Http);
        }

        Print(L"HTTP response token: %r\n", ResponseToken.Status);

        Print(L"Status Code : %d\n", ResponseToken.Message->Data.Response->StatusCode);
        for (Index = 0; Index < ResponseMessage.HeaderCount; ++Index)
        {
            Print(L"%a: %a\n", ResponseMessage.Headers[Index].FieldName, ResponseMessage.Headers[Index].FieldValue);
        }

        if (ResponseMessage.Body && ResponseMessage.BodyLength > 0)
        {            
            Print(L"HTTP Response Body: %a\n", Buffer);
            Print(L"HTTP Response Body: %s\n", Buffer);
            Print(L"Body Length: %d\n", ResponseMessage.BodyLength);
        }
        else
        {
            Print(L"Response body is empty\n");
        }

    }
    else
    {
        Print(L"Failed to receive HTTP response: %r\n", Status);
    }

    if (Buffer)
    {
        FreePool(Buffer);
    }
    Http->Configure(Http, NULL);
    HttpServiceBinding->DestroyChild(HttpServiceBinding, HttpHandle);

    return EFI_SUCCESS;
}

Solution

  • The server was not sending the Content-Length header, which caused the client to be unable to determine when to terminate the request. After the required header was sent, the issue was resolved.