12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685 |
- /** @file
- Implementation of EFI_HTTP_PROTOCOL protocol interfaces.
- Copyright (c) 2015 - 2021, Intel Corporation. All rights reserved.<BR>
- (C) Copyright 2015-2016 Hewlett Packard Enterprise Development LP<BR>
- SPDX-License-Identifier: BSD-2-Clause-Patent
- **/
- #include "HttpDriver.h"
- EFI_HTTP_PROTOCOL mEfiHttpTemplate = {
- EfiHttpGetModeData,
- EfiHttpConfigure,
- EfiHttpRequest,
- EfiHttpCancel,
- EfiHttpResponse,
- EfiHttpPoll
- };
- /**
- Returns the operational parameters for the current HTTP child instance.
- The GetModeData() function is used to read the current mode data (operational
- parameters) for this HTTP protocol instance.
- @param[in] This Pointer to EFI_HTTP_PROTOCOL instance.
- @param[out] HttpConfigData Point to buffer for operational parameters of this
- HTTP instance. It is the responsibility of the caller
- to allocate the memory for HttpConfigData and
- HttpConfigData->AccessPoint.IPv6Node/IPv4Node. In fact,
- it is recommended to allocate sufficient memory to record
- IPv6Node since it is big enough for all possibilities.
- @retval EFI_SUCCESS Operation succeeded.
- @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
- This is NULL.
- HttpConfigData is NULL.
- HttpConfigData->AccessPoint.IPv4Node or
- HttpConfigData->AccessPoint.IPv6Node is NULL.
- @retval EFI_NOT_STARTED This EFI HTTP Protocol instance has not been started.
- **/
- EFI_STATUS
- EFIAPI
- EfiHttpGetModeData (
- IN EFI_HTTP_PROTOCOL *This,
- OUT EFI_HTTP_CONFIG_DATA *HttpConfigData
- )
- {
- HTTP_PROTOCOL *HttpInstance;
- //
- // Check input parameters.
- //
- if ((This == NULL) || (HttpConfigData == NULL)) {
- return EFI_INVALID_PARAMETER;
- }
- HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This);
- if ((HttpConfigData->AccessPoint.IPv6Node == NULL) ||
- (HttpConfigData->AccessPoint.IPv4Node == NULL))
- {
- return EFI_INVALID_PARAMETER;
- }
- if (HttpInstance->State < HTTP_STATE_HTTP_CONFIGED) {
- return EFI_NOT_STARTED;
- }
- HttpConfigData->HttpVersion = HttpInstance->HttpVersion;
- HttpConfigData->TimeOutMillisec = HttpInstance->TimeOutMillisec;
- HttpConfigData->LocalAddressIsIPv6 = HttpInstance->LocalAddressIsIPv6;
- if (HttpInstance->LocalAddressIsIPv6) {
- CopyMem (
- HttpConfigData->AccessPoint.IPv6Node,
- &HttpInstance->Ipv6Node,
- sizeof (HttpInstance->Ipv6Node)
- );
- } else {
- CopyMem (
- HttpConfigData->AccessPoint.IPv4Node,
- &HttpInstance->IPv4Node,
- sizeof (HttpInstance->IPv4Node)
- );
- }
- return EFI_SUCCESS;
- }
- /**
- Initialize or brutally reset the operational parameters for this EFI HTTP instance.
- The Configure() function does the following:
- When HttpConfigData is not NULL Initialize this EFI HTTP instance by configuring
- timeout, local address, port, etc.
- When HttpConfigData is NULL, reset this EFI HTTP instance by closing all active
- connections with remote hosts, canceling all asynchronous tokens, and flush request
- and response buffers without informing the appropriate hosts.
- No other EFI HTTP function can be executed by this instance until the Configure()
- function is executed and returns successfully.
- @param[in] This Pointer to EFI_HTTP_PROTOCOL instance.
- @param[in] HttpConfigData Pointer to the configure data to configure the instance.
- @retval EFI_SUCCESS Operation succeeded.
- @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
- This is NULL.
- HttpConfigData->LocalAddressIsIPv6 is FALSE and
- HttpConfigData->AccessPoint.IPv4Node is NULL.
- HttpConfigData->LocalAddressIsIPv6 is TRUE and
- HttpConfigData->AccessPoint.IPv6Node is NULL.
- @retval EFI_ALREADY_STARTED Reinitialize this HTTP instance without calling
- Configure() with NULL to reset it.
- @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
- @retval EFI_OUT_OF_RESOURCES Could not allocate enough system resources when
- executing Configure().
- @retval EFI_UNSUPPORTED One or more options in HttpConfigData are not supported
- in the implementation.
- **/
- EFI_STATUS
- EFIAPI
- EfiHttpConfigure (
- IN EFI_HTTP_PROTOCOL *This,
- IN EFI_HTTP_CONFIG_DATA *HttpConfigData OPTIONAL
- )
- {
- HTTP_PROTOCOL *HttpInstance;
- EFI_STATUS Status;
- //
- // Check input parameters.
- //
- if ((This == NULL) ||
- ((HttpConfigData != NULL) &&
- ((HttpConfigData->LocalAddressIsIPv6 && (HttpConfigData->AccessPoint.IPv6Node == NULL)) ||
- (!HttpConfigData->LocalAddressIsIPv6 && (HttpConfigData->AccessPoint.IPv4Node == NULL)))))
- {
- return EFI_INVALID_PARAMETER;
- }
- HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This);
- ASSERT (HttpInstance->Service != NULL);
- if (HttpConfigData != NULL) {
- if (HttpConfigData->HttpVersion >= HttpVersionUnsupported) {
- return EFI_UNSUPPORTED;
- }
- //
- // Now configure this HTTP instance.
- //
- if (HttpInstance->State != HTTP_STATE_UNCONFIGED) {
- return EFI_ALREADY_STARTED;
- }
- HttpInstance->HttpVersion = HttpConfigData->HttpVersion;
- HttpInstance->TimeOutMillisec = HttpConfigData->TimeOutMillisec;
- HttpInstance->LocalAddressIsIPv6 = HttpConfigData->LocalAddressIsIPv6;
- if (HttpConfigData->LocalAddressIsIPv6) {
- CopyMem (
- &HttpInstance->Ipv6Node,
- HttpConfigData->AccessPoint.IPv6Node,
- sizeof (HttpInstance->Ipv6Node)
- );
- } else {
- CopyMem (
- &HttpInstance->IPv4Node,
- HttpConfigData->AccessPoint.IPv4Node,
- sizeof (HttpInstance->IPv4Node)
- );
- }
- //
- // Creat Tcp child
- //
- Status = HttpInitProtocol (HttpInstance, HttpInstance->LocalAddressIsIPv6);
- if (EFI_ERROR (Status)) {
- return Status;
- }
- HttpInstance->State = HTTP_STATE_HTTP_CONFIGED;
- return EFI_SUCCESS;
- } else {
- //
- // Reset all the resources related to HttpInstance.
- //
- HttpCleanProtocol (HttpInstance);
- HttpInstance->State = HTTP_STATE_UNCONFIGED;
- return EFI_SUCCESS;
- }
- }
- /**
- The Request() function queues an HTTP request to this HTTP instance.
- Similar to Transmit() function in the EFI TCP driver. When the HTTP request is sent
- successfully, or if there is an error, Status in token will be updated and Event will
- be signaled.
- @param[in] This Pointer to EFI_HTTP_PROTOCOL instance.
- @param[in] Token Pointer to storage containing HTTP request token.
- @retval EFI_SUCCESS Outgoing data was processed.
- @retval EFI_NOT_STARTED This EFI HTTP Protocol instance has not been started.
- @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
- @retval EFI_TIMEOUT Data was dropped out of the transmit or receive queue.
- @retval EFI_OUT_OF_RESOURCES Could not allocate enough system resources.
- @retval EFI_UNSUPPORTED The HTTP method is not supported in current
- implementation.
- @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
- This is NULL.
- Token is NULL.
- Token->Message is NULL.
- Token->Message->Body is not NULL,
- Token->Message->BodyLength is non-zero, and
- Token->Message->Data is NULL, but a previous call to
- Request()has not been completed successfully.
- **/
- EFI_STATUS
- EFIAPI
- EfiHttpRequest (
- IN EFI_HTTP_PROTOCOL *This,
- IN EFI_HTTP_TOKEN *Token
- )
- {
- EFI_HTTP_MESSAGE *HttpMsg;
- EFI_HTTP_REQUEST_DATA *Request;
- VOID *UrlParser;
- EFI_STATUS Status;
- CHAR8 *HostName;
- UINTN HostNameSize;
- UINT16 RemotePort;
- HTTP_PROTOCOL *HttpInstance;
- BOOLEAN Configure;
- BOOLEAN ReConfigure;
- BOOLEAN TlsConfigure;
- CHAR8 *RequestMsg;
- CHAR8 *Url;
- UINTN UrlLen;
- CHAR16 *HostNameStr;
- HTTP_TOKEN_WRAP *Wrap;
- CHAR8 *FileUrl;
- UINTN RequestMsgSize;
- EFI_HANDLE ImageHandle;
- //
- // Initializations
- //
- Url = NULL;
- UrlParser = NULL;
- RemotePort = 0;
- HostName = NULL;
- RequestMsg = NULL;
- HostNameStr = NULL;
- Wrap = NULL;
- FileUrl = NULL;
- TlsConfigure = FALSE;
- if ((This == NULL) || (Token == NULL)) {
- return EFI_INVALID_PARAMETER;
- }
- HttpMsg = Token->Message;
- if (HttpMsg == NULL) {
- return EFI_INVALID_PARAMETER;
- }
- Request = HttpMsg->Data.Request;
- //
- // Only support GET, HEAD, DELETE, PATCH, PUT and POST method in current implementation.
- //
- if ((Request != NULL) && (Request->Method != HttpMethodGet) &&
- (Request->Method != HttpMethodHead) && (Request->Method != HttpMethodDelete) &&
- (Request->Method != HttpMethodPut) && (Request->Method != HttpMethodPost) &&
- (Request->Method != HttpMethodPatch))
- {
- return EFI_UNSUPPORTED;
- }
- HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This);
- //
- // Capture the method into HttpInstance.
- //
- if (Request != NULL) {
- HttpInstance->Method = Request->Method;
- }
- if (HttpInstance->State < HTTP_STATE_HTTP_CONFIGED) {
- return EFI_NOT_STARTED;
- }
- if (Request == NULL) {
- //
- // Request would be NULL only for PUT/POST/PATCH operation (in the current implementation)
- //
- if ((HttpInstance->Method != HttpMethodPut) &&
- (HttpInstance->Method != HttpMethodPost) &&
- (HttpInstance->Method != HttpMethodPatch))
- {
- return EFI_INVALID_PARAMETER;
- }
- //
- // For PUT/POST/PATCH, we need to have the TCP already configured. Bail out if it is not!
- //
- if (HttpInstance->State < HTTP_STATE_TCP_CONFIGED) {
- return EFI_INVALID_PARAMETER;
- }
- //
- // We need to have the Message Body for sending the HTTP message across in these cases.
- //
- if ((HttpMsg->Body == NULL) || (HttpMsg->BodyLength == 0)) {
- return EFI_INVALID_PARAMETER;
- }
- //
- // Use existing TCP instance to transmit the packet.
- //
- Configure = FALSE;
- ReConfigure = FALSE;
- } else {
- //
- // Check whether the token already existed.
- //
- if (EFI_ERROR (NetMapIterate (&HttpInstance->TxTokens, HttpTokenExist, Token))) {
- return EFI_ACCESS_DENIED;
- }
- //
- // Parse the URI of the remote host.
- //
- Url = HttpInstance->Url;
- UrlLen = StrLen (Request->Url) + 1;
- if (UrlLen > HTTP_URL_BUFFER_LEN) {
- Url = AllocateZeroPool (UrlLen);
- if (Url == NULL) {
- return EFI_OUT_OF_RESOURCES;
- }
- FreePool (HttpInstance->Url);
- HttpInstance->Url = Url;
- }
- UnicodeStrToAsciiStrS (Request->Url, Url, UrlLen);
- //
- // From the information in Url, the HTTP instance will
- // be able to determine whether to use http or https.
- //
- HttpInstance->UseHttps = IsHttpsUrl (Url);
- //
- // HTTP is disabled, return directly if the URI is not HTTPS.
- //
- if (!PcdGetBool (PcdAllowHttpConnections) && !(HttpInstance->UseHttps)) {
- DEBUG ((DEBUG_ERROR, "EfiHttpRequest: HTTP is disabled.\n"));
- return EFI_ACCESS_DENIED;
- }
- //
- // Check whether we need to create Tls child and open the TLS protocol.
- //
- if (HttpInstance->UseHttps && (HttpInstance->TlsChildHandle == NULL)) {
- //
- // Use TlsSb to create Tls child and open the TLS protocol.
- //
- if (HttpInstance->LocalAddressIsIPv6) {
- ImageHandle = HttpInstance->Service->Ip6DriverBindingHandle;
- } else {
- ImageHandle = HttpInstance->Service->Ip4DriverBindingHandle;
- }
- HttpInstance->TlsChildHandle = TlsCreateChild (
- ImageHandle,
- &(HttpInstance->TlsSb),
- &(HttpInstance->Tls),
- &(HttpInstance->TlsConfiguration)
- );
- if (HttpInstance->TlsChildHandle == NULL) {
- return EFI_DEVICE_ERROR;
- }
- TlsConfigure = TRUE;
- }
- UrlParser = NULL;
- Status = HttpParseUrl (Url, (UINT32)AsciiStrLen (Url), FALSE, &UrlParser);
- if (EFI_ERROR (Status)) {
- goto Error1;
- }
- Status = HttpUrlGetHostName (Url, UrlParser, &HostName);
- if (EFI_ERROR (Status)) {
- goto Error1;
- }
- if (HttpInstance->LocalAddressIsIPv6) {
- HostNameSize = AsciiStrSize (HostName);
- if ((HostNameSize > 2) && (HostName[0] == '[') && (HostName[HostNameSize - 2] == ']')) {
- //
- // HostName format is expressed as IPv6, so, remove '[' and ']'.
- //
- HostNameSize -= 2;
- CopyMem (HostName, HostName + 1, HostNameSize - 1);
- HostName[HostNameSize - 1] = '\0';
- }
- }
- Status = HttpUrlGetPort (Url, UrlParser, &RemotePort);
- if (EFI_ERROR (Status)) {
- if (HttpInstance->UseHttps) {
- RemotePort = HTTPS_DEFAULT_PORT;
- } else {
- RemotePort = HTTP_DEFAULT_PORT;
- }
- }
- //
- // If Configure is TRUE, it indicates the first time to call Request();
- // If ReConfigure is TRUE, it indicates the request URL is not same
- // with the previous call to Request();
- //
- Configure = TRUE;
- ReConfigure = TRUE;
- if (HttpInstance->RemoteHost == NULL) {
- //
- // Request() is called the first time.
- //
- ReConfigure = FALSE;
- } else {
- if ((HttpInstance->RemotePort == RemotePort) &&
- (AsciiStrCmp (HttpInstance->RemoteHost, HostName) == 0) &&
- (!HttpInstance->UseHttps || (HttpInstance->UseHttps &&
- !TlsConfigure &&
- (HttpInstance->TlsSessionState == EfiTlsSessionDataTransferring))))
- {
- //
- // Host Name and port number of the request URL are the same with previous call to Request().
- // If Https protocol used, the corresponding SessionState is EfiTlsSessionDataTransferring.
- // Check whether previous TCP packet sent out.
- //
- if (EFI_ERROR (NetMapIterate (&HttpInstance->TxTokens, HttpTcpNotReady, NULL))) {
- //
- // Wrap the HTTP token in HTTP_TOKEN_WRAP
- //
- Wrap = AllocateZeroPool (sizeof (HTTP_TOKEN_WRAP));
- if (Wrap == NULL) {
- Status = EFI_OUT_OF_RESOURCES;
- goto Error1;
- }
- Wrap->HttpToken = Token;
- Wrap->HttpInstance = HttpInstance;
- Status = HttpCreateTcpTxEvent (Wrap);
- if (EFI_ERROR (Status)) {
- goto Error1;
- }
- Status = NetMapInsertTail (&HttpInstance->TxTokens, Token, Wrap);
- if (EFI_ERROR (Status)) {
- goto Error1;
- }
- Wrap->TcpWrap.Method = Request->Method;
- FreePool (HostName);
- HttpUrlFreeParser (UrlParser);
- //
- // Queue the HTTP token and return.
- //
- return EFI_SUCCESS;
- } else {
- //
- // Use existing TCP instance to transmit the packet.
- //
- Configure = FALSE;
- ReConfigure = FALSE;
- }
- } else {
- //
- // Need close existing TCP instance and create a new TCP instance for data transmit.
- //
- if (HttpInstance->RemoteHost != NULL) {
- FreePool (HttpInstance->RemoteHost);
- HttpInstance->RemoteHost = NULL;
- HttpInstance->RemotePort = 0;
- }
- }
- }
- }
- if (Configure) {
- //
- // Parse Url for IPv4 or IPv6 address, if failed, perform DNS resolution.
- //
- if (!HttpInstance->LocalAddressIsIPv6) {
- Status = NetLibAsciiStrToIp4 (HostName, &HttpInstance->RemoteAddr);
- } else {
- Status = HttpUrlGetIp6 (Url, UrlParser, &HttpInstance->RemoteIpv6Addr);
- }
- if (EFI_ERROR (Status)) {
- HostNameSize = AsciiStrSize (HostName);
- HostNameStr = AllocateZeroPool (HostNameSize * sizeof (CHAR16));
- if (HostNameStr == NULL) {
- Status = EFI_OUT_OF_RESOURCES;
- goto Error1;
- }
- AsciiStrToUnicodeStrS (HostName, HostNameStr, HostNameSize);
- if (!HttpInstance->LocalAddressIsIPv6) {
- Status = HttpDns4 (HttpInstance, HostNameStr, &HttpInstance->RemoteAddr);
- } else {
- Status = HttpDns6 (HttpInstance, HostNameStr, &HttpInstance->RemoteIpv6Addr);
- }
- HttpNotify (HttpEventDns, Status);
- FreePool (HostNameStr);
- if (EFI_ERROR (Status)) {
- DEBUG ((DEBUG_ERROR, "Error: Could not retrieve the host address from DNS server.\n"));
- goto Error1;
- }
- }
- //
- // Save the RemotePort and RemoteHost.
- //
- ASSERT (HttpInstance->RemoteHost == NULL);
- HttpInstance->RemotePort = RemotePort;
- HttpInstance->RemoteHost = HostName;
- HostName = NULL;
- }
- if (ReConfigure) {
- //
- // The request URL is different from previous calls to Request(), close existing TCP instance.
- //
- if (!HttpInstance->LocalAddressIsIPv6) {
- ASSERT (HttpInstance->Tcp4 != NULL);
- } else {
- ASSERT (HttpInstance->Tcp6 != NULL);
- }
- if (HttpInstance->UseHttps && !TlsConfigure) {
- Status = TlsCloseSession (HttpInstance);
- if (EFI_ERROR (Status)) {
- goto Error1;
- }
- TlsCloseTxRxEvent (HttpInstance);
- }
- HttpCloseConnection (HttpInstance);
- EfiHttpCancel (This, NULL);
- }
- //
- // Wrap the HTTP token in HTTP_TOKEN_WRAP
- //
- Wrap = AllocateZeroPool (sizeof (HTTP_TOKEN_WRAP));
- if (Wrap == NULL) {
- Status = EFI_OUT_OF_RESOURCES;
- goto Error1;
- }
- Wrap->HttpToken = Token;
- Wrap->HttpInstance = HttpInstance;
- if (Request != NULL) {
- Wrap->TcpWrap.Method = Request->Method;
- }
- Status = HttpInitSession (
- HttpInstance,
- Wrap,
- Configure || ReConfigure,
- TlsConfigure
- );
- HttpNotify (HttpEventInitSession, Status);
- if (EFI_ERROR (Status)) {
- goto Error2;
- }
- if (!Configure && !ReConfigure && !TlsConfigure) {
- //
- // For the new HTTP token, create TX TCP token events.
- //
- Status = HttpCreateTcpTxEvent (Wrap);
- if (EFI_ERROR (Status)) {
- goto Error1;
- }
- }
- //
- // Create request message.
- //
- FileUrl = Url;
- if ((Url != NULL) && (*FileUrl != '/')) {
- //
- // Convert the absolute-URI to the absolute-path
- //
- while (*FileUrl != ':') {
- FileUrl++;
- }
- if ((*(FileUrl+1) == '/') && (*(FileUrl+2) == '/')) {
- FileUrl += 3;
- while (*FileUrl != '/') {
- FileUrl++;
- }
- } else {
- Status = EFI_INVALID_PARAMETER;
- goto Error3;
- }
- }
- Status = HttpGenRequestMessage (HttpMsg, FileUrl, &RequestMsg, &RequestMsgSize);
- if (EFI_ERROR (Status) || (NULL == RequestMsg)) {
- goto Error3;
- }
- //
- // Every request we insert a TxToken and a response call would remove the TxToken.
- // In cases of PUT/POST/PATCH, after an initial request-response pair, we would do a
- // continuous request without a response call. So, in such cases, where Request
- // structure is NULL, we would not insert a TxToken.
- //
- if (Request != NULL) {
- Status = NetMapInsertTail (&HttpInstance->TxTokens, Token, Wrap);
- if (EFI_ERROR (Status)) {
- goto Error4;
- }
- }
- //
- // Transmit the request message.
- //
- Status = HttpTransmitTcp (
- HttpInstance,
- Wrap,
- (UINT8 *)RequestMsg,
- RequestMsgSize
- );
- if (EFI_ERROR (Status)) {
- goto Error5;
- }
- DispatchDpc ();
- if (HostName != NULL) {
- FreePool (HostName);
- }
- if (UrlParser != NULL) {
- HttpUrlFreeParser (UrlParser);
- }
- return EFI_SUCCESS;
- Error5:
- //
- // We would have inserted a TxToken only if Request structure is not NULL.
- // Hence check before we do a remove in this error case.
- //
- if (Request != NULL) {
- NetMapRemoveTail (&HttpInstance->TxTokens, NULL);
- }
- Error4:
- if (RequestMsg != NULL) {
- FreePool (RequestMsg);
- }
- Error3:
- if (HttpInstance->UseHttps) {
- TlsCloseSession (HttpInstance);
- TlsCloseTxRxEvent (HttpInstance);
- }
- Error2:
- HttpCloseConnection (HttpInstance);
- HttpCloseTcpConnCloseEvent (HttpInstance);
- if (NULL != Wrap->TcpWrap.Tx4Token.CompletionToken.Event) {
- gBS->CloseEvent (Wrap->TcpWrap.Tx4Token.CompletionToken.Event);
- Wrap->TcpWrap.Tx4Token.CompletionToken.Event = NULL;
- }
- if (NULL != Wrap->TcpWrap.Tx6Token.CompletionToken.Event) {
- gBS->CloseEvent (Wrap->TcpWrap.Tx6Token.CompletionToken.Event);
- Wrap->TcpWrap.Tx6Token.CompletionToken.Event = NULL;
- }
- Error1:
- if (HostName != NULL) {
- FreePool (HostName);
- }
- if (Wrap != NULL) {
- FreePool (Wrap);
- }
- if (UrlParser != NULL) {
- HttpUrlFreeParser (UrlParser);
- }
- return Status;
- }
- /**
- Cancel a user's Token.
- @param[in] Map The HTTP instance's token queue.
- @param[in] Item Object container for one HTTP token and token's wrap.
- @param[in] Context The user's token to cancel.
- @retval EFI_SUCCESS Continue to check the next Item.
- @retval EFI_ABORTED The user's Token (Token != NULL) is cancelled.
- **/
- EFI_STATUS
- EFIAPI
- HttpCancelTokens (
- IN NET_MAP *Map,
- IN NET_MAP_ITEM *Item,
- IN VOID *Context
- )
- {
- EFI_HTTP_TOKEN *Token;
- HTTP_TOKEN_WRAP *Wrap;
- HTTP_PROTOCOL *HttpInstance;
- Token = (EFI_HTTP_TOKEN *)Context;
- //
- // Return EFI_SUCCESS to check the next item in the map if
- // this one doesn't match.
- //
- if ((Token != NULL) && (Token != Item->Key)) {
- return EFI_SUCCESS;
- }
- Wrap = (HTTP_TOKEN_WRAP *)Item->Value;
- ASSERT (Wrap != NULL);
- HttpInstance = Wrap->HttpInstance;
- if (!HttpInstance->LocalAddressIsIPv6) {
- if (Wrap->TcpWrap.Rx4Token.CompletionToken.Event != NULL) {
- //
- // Cancel the Token before close its Event.
- //
- HttpInstance->Tcp4->Cancel (HttpInstance->Tcp4, &Wrap->TcpWrap.Rx4Token.CompletionToken);
- //
- // Dispatch the DPC queued by the NotifyFunction of the canceled token's events.
- //
- DispatchDpc ();
- }
- } else {
- if (Wrap->TcpWrap.Rx6Token.CompletionToken.Event != NULL) {
- //
- // Cancel the Token before close its Event.
- //
- HttpInstance->Tcp6->Cancel (HttpInstance->Tcp6, &Wrap->TcpWrap.Rx6Token.CompletionToken);
- //
- // Dispatch the DPC queued by the NotifyFunction of the canceled token's events.
- //
- DispatchDpc ();
- }
- }
- //
- // If only one item is to be cancel, return EFI_ABORTED to stop
- // iterating the map any more.
- //
- if (Token != NULL) {
- return EFI_ABORTED;
- }
- return EFI_SUCCESS;
- }
- /**
- Cancel the user's receive/transmit request. It is the worker function of
- EfiHttpCancel API. If a matching token is found, it will call HttpCancelTokens to cancel the
- token.
- @param[in] HttpInstance Pointer to HTTP_PROTOCOL structure.
- @param[in] Token The token to cancel. If NULL, all token will be
- cancelled.
- @retval EFI_SUCCESS The token is cancelled.
- @retval EFI_NOT_FOUND The asynchronous request or response token is not found.
- @retval Others Other error as indicated.
- **/
- EFI_STATUS
- HttpCancel (
- IN HTTP_PROTOCOL *HttpInstance,
- IN EFI_HTTP_TOKEN *Token
- )
- {
- EFI_STATUS Status;
- //
- // First check the tokens queued by EfiHttpRequest().
- //
- Status = NetMapIterate (&HttpInstance->TxTokens, HttpCancelTokens, Token);
- if (EFI_ERROR (Status)) {
- if (Token != NULL) {
- if (Status == EFI_ABORTED) {
- return EFI_SUCCESS;
- }
- } else {
- return Status;
- }
- }
- if (!HttpInstance->UseHttps) {
- //
- // Then check the tokens queued by EfiHttpResponse(), except for Https.
- //
- Status = NetMapIterate (&HttpInstance->RxTokens, HttpCancelTokens, Token);
- if (EFI_ERROR (Status)) {
- if (Token != NULL) {
- if (Status == EFI_ABORTED) {
- return EFI_SUCCESS;
- } else {
- return EFI_NOT_FOUND;
- }
- } else {
- return Status;
- }
- }
- } else {
- if (!HttpInstance->LocalAddressIsIPv6) {
- HttpInstance->Tcp4->Cancel (HttpInstance->Tcp4, &HttpInstance->Tcp4TlsRxToken.CompletionToken);
- } else {
- HttpInstance->Tcp6->Cancel (HttpInstance->Tcp6, &HttpInstance->Tcp6TlsRxToken.CompletionToken);
- }
- }
- return EFI_SUCCESS;
- }
- /**
- Abort an asynchronous HTTP request or response token.
- The Cancel() function aborts a pending HTTP request or response transaction. If
- Token is not NULL and the token is in transmit or receive queues when it is being
- cancelled, its Token->Status will be set to EFI_ABORTED and then Token->Event will
- be signaled. If the token is not in one of the queues, which usually means that the
- asynchronous operation has completed, EFI_NOT_FOUND is returned. If Token is NULL,
- all asynchronous tokens issued by Request() or Response() will be aborted.
- @param[in] This Pointer to EFI_HTTP_PROTOCOL instance.
- @param[in] Token Point to storage containing HTTP request or response
- token.
- @retval EFI_SUCCESS Request and Response queues are successfully flushed.
- @retval EFI_INVALID_PARAMETER This is NULL.
- @retval EFI_NOT_STARTED This instance hasn't been configured.
- @retval EFI_NOT_FOUND The asynchronous request or response token is not
- found.
- @retval EFI_UNSUPPORTED The implementation does not support this function.
- **/
- EFI_STATUS
- EFIAPI
- EfiHttpCancel (
- IN EFI_HTTP_PROTOCOL *This,
- IN EFI_HTTP_TOKEN *Token
- )
- {
- HTTP_PROTOCOL *HttpInstance;
- if (This == NULL) {
- return EFI_INVALID_PARAMETER;
- }
- HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This);
- if (HttpInstance->State != HTTP_STATE_TCP_CONNECTED) {
- return EFI_NOT_STARTED;
- }
- return HttpCancel (HttpInstance, Token);
- }
- /**
- A callback function to intercept events during message parser.
- This function will be invoked during HttpParseMessageBody() with various events type. An error
- return status of the callback function will cause the HttpParseMessageBody() aborted.
- @param[in] EventType Event type of this callback call.
- @param[in] Data A pointer to data buffer.
- @param[in] Length Length in bytes of the Data.
- @param[in] Context Callback context set by HttpInitMsgParser().
- @retval EFI_SUCCESS Continue to parser the message body.
- **/
- EFI_STATUS
- EFIAPI
- HttpBodyParserCallback (
- IN HTTP_BODY_PARSE_EVENT EventType,
- IN CHAR8 *Data,
- IN UINTN Length,
- IN VOID *Context
- )
- {
- HTTP_CALLBACK_DATA *CallbackData;
- HTTP_TOKEN_WRAP *Wrap;
- UINTN BodyLength;
- CHAR8 *Body;
- if (EventType != BodyParseEventOnComplete) {
- return EFI_SUCCESS;
- }
- if ((Data == NULL) || (Length != 0) || (Context == NULL)) {
- return EFI_SUCCESS;
- }
- CallbackData = (HTTP_CALLBACK_DATA *)Context;
- Wrap = (HTTP_TOKEN_WRAP *)(CallbackData->Wrap);
- Body = CallbackData->ParseData;
- BodyLength = CallbackData->ParseDataLength;
- if (Data < Body + BodyLength) {
- Wrap->HttpInstance->NextMsg = Data;
- } else {
- Wrap->HttpInstance->NextMsg = NULL;
- }
- return EFI_SUCCESS;
- }
- /**
- 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 operation 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;
- NET_FRAGMENT Fragment;
- UINT32 TimeoutValue;
- 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;
- ValueInItem = NULL;
- Fragment.Len = 0;
- Fragment.Bulk = NULL;
- if (HttpMsg->Data.Response != NULL) {
- //
- // 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;
- }
- }
- //
- // Get HTTP timeout value
- //
- TimeoutValue = PcdGet32 (PcdHttpIoTimeout);
- //
- // Start the timer, and wait Timeout seconds to receive the header packet.
- //
- Status = gBS->SetTimer (HttpInstance->TimeoutEvent, TimerRelative, TimeoutValue * TICKS_PER_MS);
- 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) {
- Status = EFI_NOT_READY;
- 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) {
- Status = EFI_NOT_READY;
- 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/PATCH, 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) {
- Status = EFI_OUT_OF_RESOURCES;
- goto Error2;
- }
- 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 Error2;
- }
- //
- // 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 Error2;
- }
- 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 *)(&HttpInstance->CallbackData),
- &HttpInstance->MsgParser
- );
- if (EFI_ERROR (Status)) {
- goto Error2;
- }
- //
- // Check whether we received a complete HTTP message.
- //
- if (HttpInstance->CacheBody != NULL) {
- //
- // Record the CallbackData data.
- //
- HttpInstance->CallbackData.Wrap = (VOID *)Wrap;
- HttpInstance->CallbackData.ParseData = (VOID *)HttpInstance->CacheBody;
- HttpInstance->CallbackData.ParseDataLength = HttpInstance->CacheLen;
- //
- // Parse message with CallbackData data.
- //
- 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 already 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;
- //
- if (!HttpInstance->UseHttps) {
- Status = HttpTcpReceiveBody (Wrap, HttpMsg);
- if (EFI_ERROR (Status)) {
- goto Error2;
- }
- } else {
- if (HttpInstance->TimeoutEvent == NULL) {
- //
- // Create TimeoutEvent for response
- //
- Status = gBS->CreateEvent (
- EVT_TIMER,
- TPL_CALLBACK,
- NULL,
- NULL,
- &HttpInstance->TimeoutEvent
- );
- if (EFI_ERROR (Status)) {
- goto Error2;
- }
- }
- //
- // Get HTTP timeout value
- //
- TimeoutValue = PcdGet32 (PcdHttpIoTimeout);
- //
- // Start the timer, and wait Timeout seconds to receive the body packet.
- //
- Status = gBS->SetTimer (HttpInstance->TimeoutEvent, TimerRelative, TimeoutValue * TICKS_PER_MS);
- if (EFI_ERROR (Status)) {
- goto Error2;
- }
- Status = HttpsReceive (HttpInstance, &Fragment, HttpInstance->TimeoutEvent);
- gBS->SetTimer (HttpInstance->TimeoutEvent, TimerCancel, 0);
- if (EFI_ERROR (Status)) {
- goto Error2;
- }
- //
- // Process the received the body packet.
- //
- HttpMsg->BodyLength = MIN ((UINTN)Fragment.Len, HttpMsg->BodyLength);
- CopyMem (HttpMsg->Body, Fragment.Bulk, HttpMsg->BodyLength);
- //
- // Record the CallbackData data.
- //
- HttpInstance->CallbackData.Wrap = (VOID *)Wrap;
- HttpInstance->CallbackData.ParseData = HttpMsg->Body;
- HttpInstance->CallbackData.ParseDataLength = HttpMsg->BodyLength;
- //
- // Parse Body with CallbackData data.
- //
- Status = HttpParseMessageBody (
- HttpInstance->MsgParser,
- HttpMsg->BodyLength,
- HttpMsg->Body
- );
- 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;
- }
- //
- // Check whether there is the next message header in the HttpMsg->Body.
- //
- if (HttpInstance->NextMsg != NULL) {
- HttpMsg->BodyLength = HttpInstance->NextMsg - (CHAR8 *)HttpMsg->Body;
- }
- HttpInstance->CacheLen = Fragment.Len - HttpMsg->BodyLength;
- if (HttpInstance->CacheLen != 0) {
- if (HttpInstance->CacheBody != NULL) {
- FreePool (HttpInstance->CacheBody);
- }
- HttpInstance->CacheBody = AllocateZeroPool (HttpInstance->CacheLen);
- if (HttpInstance->CacheBody == NULL) {
- Status = EFI_OUT_OF_RESOURCES;
- goto Error2;
- }
- CopyMem (HttpInstance->CacheBody, Fragment.Bulk + HttpMsg->BodyLength, HttpInstance->CacheLen);
- HttpInstance->CacheOffset = 0;
- if (HttpInstance->NextMsg != NULL) {
- HttpInstance->NextMsg = HttpInstance->CacheBody;
- }
- }
- if (Fragment.Bulk != NULL) {
- FreePool (Fragment.Bulk);
- Fragment.Bulk = NULL;
- }
- goto Exit;
- }
- 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:
- if (ValueInItem != NULL) {
- NetMapInsertHead (&HttpInstance->TxTokens, ValueInItem->HttpToken, ValueInItem);
- }
- Error:
- Item = NetMapFindKey (&Wrap->HttpInstance->RxTokens, Wrap->HttpToken);
- if (Item != NULL) {
- NetMapRemoveItem (&Wrap->HttpInstance->RxTokens, Item, NULL);
- }
- if (!HttpInstance->UseHttps) {
- HttpTcpTokenCleanup (Wrap);
- } else {
- FreePool (Wrap);
- }
- if (HttpHeaders != NULL) {
- FreePool (HttpHeaders);
- HttpHeaders = NULL;
- }
- if (Fragment.Bulk != NULL) {
- FreePool (Fragment.Bulk);
- Fragment.Bulk = NULL;
- }
- if (HttpMsg->Headers != NULL) {
- FreePool (HttpMsg->Headers);
- HttpMsg->Headers = NULL;
- }
- 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;
- }
- /**
- The Response() function queues an HTTP response to this HTTP instance, similar to
- Receive() function in the EFI TCP driver. When the HTTP response is received successfully,
- or if there is an error, Status in token will be updated and Event will be signaled.
- The HTTP driver will queue a receive token to the underlying TCP instance. When data
- is received in the underlying TCP instance, the data will be parsed and Token will
- be populated with the response data. If the data received from the remote host
- contains an incomplete or invalid HTTP header, the HTTP driver will continue waiting
- (asynchronously) for more data to be sent from the remote host before signaling
- Event in Token.
- It is the responsibility of the caller to allocate a buffer for Body and specify the
- size in BodyLength. If the remote host provides a response that contains a content
- body, up to BodyLength bytes will be copied from the receive buffer into Body and
- BodyLength will be updated with the amount of bytes received and copied to Body. This
- allows the client to download a large file in chunks instead of into one contiguous
- block of memory. Similar to HTTP request, if Body is not NULL and BodyLength is
- non-zero and all other fields are NULL or 0, the HTTP driver will queue a receive
- token to underlying TCP instance. If data arrives in the receive buffer, up to
- BodyLength bytes of data will be copied to Body. The HTTP driver will then update
- BodyLength with the amount of bytes received and copied to Body.
- If the HTTP driver does not have an open underlying TCP connection with the host
- specified in the response URL, Request() will return EFI_ACCESS_DENIED. This is
- consistent with RFC 2616 recommendation that HTTP clients should attempt to maintain
- an open TCP connection between client and host.
- @param[in] This Pointer to EFI_HTTP_PROTOCOL instance.
- @param[in] Token Pointer to storage containing HTTP response token.
- @retval EFI_SUCCESS Allocation succeeded.
- @retval EFI_NOT_STARTED This EFI HTTP Protocol instance has not been
- initialized.
- @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
- This is NULL.
- Token is NULL.
- Token->Message->Headers is NULL.
- Token->Message is NULL.
- Token->Message->Body is not NULL,
- Token->Message->BodyLength is non-zero, and
- Token->Message->Data is NULL, but a previous call to
- Response() has not been completed successfully.
- @retval EFI_OUT_OF_RESOURCES Could not allocate enough system resources.
- @retval EFI_ACCESS_DENIED An open TCP connection is not present with the host
- specified by response URL.
- **/
- EFI_STATUS
- EFIAPI
- EfiHttpResponse (
- IN EFI_HTTP_PROTOCOL *This,
- IN EFI_HTTP_TOKEN *Token
- )
- {
- EFI_STATUS Status;
- EFI_HTTP_MESSAGE *HttpMsg;
- HTTP_PROTOCOL *HttpInstance;
- HTTP_TOKEN_WRAP *Wrap;
- if ((This == NULL) || (Token == NULL)) {
- return EFI_INVALID_PARAMETER;
- }
- HttpMsg = Token->Message;
- if (HttpMsg == NULL) {
- return EFI_INVALID_PARAMETER;
- }
- HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This);
- if (HttpInstance->State != HTTP_STATE_TCP_CONNECTED) {
- return EFI_NOT_STARTED;
- }
- //
- // Check whether the token already existed.
- //
- if (EFI_ERROR (NetMapIterate (&HttpInstance->RxTokens, HttpTokenExist, Token))) {
- return EFI_ACCESS_DENIED;
- }
- Wrap = AllocateZeroPool (sizeof (HTTP_TOKEN_WRAP));
- if (Wrap == NULL) {
- return EFI_OUT_OF_RESOURCES;
- }
- Wrap->HttpInstance = HttpInstance;
- Wrap->HttpToken = Token;
- //
- // Notes: For Https, receive token wrapped in HTTP_TOKEN_WRAP is not used to
- // receive the https response. A special TlsRxToken is used for receiving TLS
- // related messages. It should be a blocking response.
- //
- if (!HttpInstance->UseHttps) {
- Status = HttpCreateTcpRxEvent (Wrap);
- if (EFI_ERROR (Status)) {
- goto Error;
- }
- }
- Status = NetMapInsertTail (&HttpInstance->RxTokens, Token, Wrap);
- if (EFI_ERROR (Status)) {
- goto Error;
- }
- //
- // If already have pending RxTokens, return directly.
- //
- if (NetMapGetCount (&HttpInstance->RxTokens) > 1) {
- return EFI_SUCCESS;
- }
- return HttpResponseWorker (Wrap);
- Error:
- if (Wrap != NULL) {
- if (Wrap->TcpWrap.Rx4Token.CompletionToken.Event != NULL) {
- gBS->CloseEvent (Wrap->TcpWrap.Rx4Token.CompletionToken.Event);
- }
- if (Wrap->TcpWrap.Rx6Token.CompletionToken.Event != NULL) {
- gBS->CloseEvent (Wrap->TcpWrap.Rx6Token.CompletionToken.Event);
- }
- FreePool (Wrap);
- }
- return Status;
- }
- /**
- The Poll() function can be used by network drivers and applications to increase the
- rate that data packets are moved between the communication devices and the transmit
- and receive queues.
- In some systems, the periodic timer event in the managed network driver may not poll
- the underlying communications device fast enough to transmit and/or receive all data
- packets without missing incoming packets or dropping outgoing packets. Drivers and
- applications that are experiencing packet loss should try calling the Poll() function
- more often.
- @param[in] This Pointer to EFI_HTTP_PROTOCOL instance.
- @retval EFI_SUCCESS Incoming or outgoing data was processed.
- @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
- @retval EFI_INVALID_PARAMETER This is NULL.
- @retval EFI_NOT_READY No incoming or outgoing data is processed.
- @retval EFI_NOT_STARTED This EFI HTTP Protocol instance has not been started.
- **/
- EFI_STATUS
- EFIAPI
- EfiHttpPoll (
- IN EFI_HTTP_PROTOCOL *This
- )
- {
- EFI_STATUS Status;
- HTTP_PROTOCOL *HttpInstance;
- if (This == NULL) {
- return EFI_INVALID_PARAMETER;
- }
- HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This);
- if (HttpInstance->State != HTTP_STATE_TCP_CONNECTED) {
- return EFI_NOT_STARTED;
- }
- if (HttpInstance->LocalAddressIsIPv6) {
- if (HttpInstance->Tcp6 == NULL) {
- return EFI_NOT_STARTED;
- }
- Status = HttpInstance->Tcp6->Poll (HttpInstance->Tcp6);
- } else {
- if (HttpInstance->Tcp4 == NULL) {
- return EFI_NOT_STARTED;
- }
- Status = HttpInstance->Tcp4->Poll (HttpInstance->Tcp4);
- }
- DispatchDpc ();
- return Status;
- }
|