UEFI应用HTTP GET请求无法获取响应体问题排查求助
UEFI HTTP GET请求响应体读取问题排查
问题描述
基于EDK2环境开发UEFI应用,调用UEFI API发起HTTP GET请求时遇到以下问题:
- 调用
Http->Response后,绑定的事件始终未触发; - 移除事件等待逻辑后,能成功建立连接并获取
200 OK响应头,但响应体始终为空。
已尝试的排查操作
- 增加延迟等待响应体接收,响应体仍为空;
- 使用事件处理响应,事件未被触发。
排查思路
1. 事件回调与TPL优先级检查
- 确认
httpnotify回调函数是否正确实现,必须在回调内将response_done(或request_done)设为TRUE。UEFI事件回调不能阻塞,且创建事件时指定的TPL_CALLBACK优先级需与回调执行环境一致,否则可能导致事件无法触发。 - 检查事件创建参数,
EVT_NOTIFY_SIGNAL是UEFI HTTP协议要求的事件类型,但需确保事件对象未被意外释放或修改。
2. 响应消息参数配置
- 响应体缓冲区:当前代码中
ResponseMessage.BodyLength固定为BUFFER_SIZE,若服务器返回的响应体大小超过该值,UEFI HTTP协议可能无法填充完整数据。建议先通过响应头获取Content-Length,再动态分配对应大小的缓冲区;若服务器使用分块传输编码(Transfer-Encoding: chunked),则需循环读取直到结束。 - 响应头缓冲区:当前
ResponseMessage.HeaderCount = 0且Headers = NULL,UEFI HTTP协议要求调用Response前需预留足够的响应头存储空间,即使不关心响应头,也应分配至少1个元素的EFI_HTTP_HEADER数组,避免协议内部报错。
3. Poll调用与状态处理
- 轮询
Http->Poll时,需检查返回状态:- 返回
EFI_NOT_READY表示仍在处理,需继续轮询; - 返回
EFI_SUCCESS后,必须检查ResponseToken.Status是否为EFI_SUCCESS,不能仅依赖response_done标志。
- 返回
- 轮询循环中需加入延迟,如
gBS->Stall(10000)(10毫秒),避免过度占用系统资源。
4. HTTP配置优化
- 调整
HttpConfigData.TimeOutMillisec:当前设置为500毫秒过短,若服务器响应较慢会直接超时,建议改为5000毫秒(5秒)。 - 调用
Http->Response前,通过Http->GetModeData获取当前连接状态,确认连接未被意外关闭。
5. 分段读取响应体
UEFI HTTP协议支持分段读取响应体,若响应体较大,单次Response调用无法获取全部数据。需循环调用直到ResponseToken.Status返回EFI_END_OF_FILE:
do { Status = Http->Response(Http, &ResponseToken); if (EFI_ERROR(Status)) break; response_done = FALSE; while (!response_done) { Status = Http->Poll(Http); gBS->Stall(10000); } // 处理当前读取到的响应体数据 if (ResponseMessage.Body && ResponseMessage.BodyLength > 0) { // 示例:打印当前分段数据 Print(L"Received body segment: %a\n", Buffer); } // 重置事件,准备下一次读取 gBS->CloseEvent(ResponseToken.Event); Status = gBS->CreateEvent(EVT_NOTIFY_SIGNAL, TPL_CALLBACK, httpnotify, &response_done, &ResponseToken.Event); } while (ResponseToken.Status == EFI_SUCCESS);
相关代码
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; }
内容的提问来源于stack exchange,提问作者Abeer Ul Haq
相关产品推荐
相关产品推荐

