VirtualBox

Ignore:
Timestamp:
Mar 31, 2025 11:31:09 AM (2 weeks ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
168237
Message:

Devices/EFI/FirmwareNew: Merge edk2-stable202502 from the vendor branch and make it build for the important platforms, bugref:4643

Location:
trunk/src/VBox/Devices/EFI/FirmwareNew
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Devices/EFI/FirmwareNew

  • trunk/src/VBox/Devices/EFI/FirmwareNew/NetworkPkg/HttpBootDxe/HttpBootClient.c

    r99404 r108794  
    924924                                   the request.
    925925  @retval EFI_ACCESS_DENIED        The server needs to authenticate the client.
     926  @retval EFI_NOT_READY            Data transfer has timed-out, call HttpBootGetBootFile again to resume
     927                                   the download operation using HTTP Range headers.
     928  @retval EFI_UNSUPPORTED          Some HTTP response header is not supported.
    926929  @retval Others                   Unexpected error happened.
    927930
     
    956959  EFI_HTTP_HEADER          *HttpHeader;
    957960  CHAR8                    *Data;
     961  UINTN                    HeadersCount;
     962  BOOLEAN                  ResumingOperation;
     963  CHAR8                    *ContentRangeResponseValue;
     964  CHAR8                    RangeValue[64];
    958965
    959966  ASSERT (Private != NULL);
     
    986993  }
    987994
     995  // Check if this is a previous download that has failed and need to be resumed
     996  if ((!HeaderOnly) &&
     997      (Private->PartialTransferredSize > 0) &&
     998      (Private->BootFileSize == *BufferSize))
     999  {
     1000    ResumingOperation = TRUE;
     1001  } else {
     1002    ResumingOperation = FALSE;
     1003  }
     1004
    9881005  //
    9891006  // Not found in cache, try to download it through HTTP.
     
    10151032  //       User-Agent
    10161033  //       [Authorization]
    1017   //
    1018   HttpIoHeader = HttpIoCreateHeader ((Private->AuthData != NULL) ? 4 : 3);
     1034  //       [Range]
     1035  //       [If-Match]|[If-Unmodified-Since]
     1036  //
     1037  HeadersCount = 3;
     1038  if (Private->AuthData != NULL) {
     1039    HeadersCount++;
     1040  }
     1041
     1042  if (ResumingOperation) {
     1043    HeadersCount++;
     1044    if (Private->LastModifiedOrEtag) {
     1045      HeadersCount++;
     1046    }
     1047  }
     1048
     1049  HttpIoHeader = HttpIoCreateHeader (HeadersCount);
     1050
    10191051  if (HttpIoHeader == NULL) {
    10201052    Status = EFI_OUT_OF_RESOURCES;
     
    10951127    if (EFI_ERROR (Status)) {
    10961128      goto ERROR_3;
     1129    }
     1130  }
     1131
     1132  //
     1133  // Add HTTP header field 5 (optional): Range
     1134  //
     1135  if (ResumingOperation) {
     1136    // Resuming a failed download. Prepare the HTTP Range Header
     1137    Status = AsciiSPrint (
     1138               RangeValue,
     1139               sizeof (RangeValue),
     1140               "bytes=%lu-%lu",
     1141               Private->PartialTransferredSize,
     1142               Private->BootFileSize - 1
     1143               );
     1144    if (EFI_ERROR (Status)) {
     1145      goto ERROR_3;
     1146    }
     1147
     1148    Status = HttpIoSetHeader (HttpIoHeader, "Range", RangeValue);
     1149    if (EFI_ERROR (Status)) {
     1150      goto ERROR_3;
     1151    }
     1152
     1153    DEBUG (
     1154      (DEBUG_WARN | DEBUG_INFO,
     1155       "HttpBootGetBootFile: Resuming failed download. Range: %a\n",
     1156       RangeValue)
     1157      );
     1158
     1159    //
     1160    // Add HTTP header field 6 (optional): If-Match or If-Unmodified-Since
     1161    //
     1162    if (Private->LastModifiedOrEtag) {
     1163      if (Private->LastModifiedOrEtag[0] == '"') {
     1164        // An ETag value starts with "
     1165        DEBUG (
     1166          (DEBUG_WARN | DEBUG_INFO,
     1167           "HttpBootGetBootFile: If-Match=%a\n",
     1168           Private->LastModifiedOrEtag)
     1169          );
     1170        // Add If-Match header with the ETag value got from the first request.
     1171        Status = HttpIoSetHeader (HttpIoHeader, HTTP_HEADER_IF_MATCH, Private->LastModifiedOrEtag);
     1172      } else {
     1173        DEBUG (
     1174          (DEBUG_WARN | DEBUG_INFO,
     1175           "HttpBootGetBootFile: If-Unmodified-Since=%a\n",
     1176           Private->LastModifiedOrEtag)
     1177          );
     1178        // Add If-Unmodified-Since header with the timestamp value (Last-Modified) got from the first request.
     1179        Status = HttpIoSetHeader (HttpIoHeader, HTTP_HEADER_IF_UNMODIFIED_SINCE, Private->LastModifiedOrEtag);
     1180      }
     1181
     1182      if (EFI_ERROR (Status)) {
     1183        goto ERROR_3;
     1184      }
    10971185    }
    10981186  }
     
    12461334  }
    12471335
     1336  // Cache ETag or Last-Modified response header value to
     1337  // be used when resuming an interrupted download.
     1338  HttpHeader = HttpFindHeader (
     1339                 ResponseData->HeaderCount,
     1340                 ResponseData->Headers,
     1341                 HTTP_HEADER_ETAG
     1342                 );
     1343  if (HttpHeader == NULL) {
     1344    HttpHeader = HttpFindHeader (
     1345                   ResponseData->HeaderCount,
     1346                   ResponseData->Headers,
     1347                   HTTP_HEADER_LAST_MODIFIED
     1348                   );
     1349  }
     1350
     1351  if (HttpHeader) {
     1352    if (Private->LastModifiedOrEtag) {
     1353      FreePool (Private->LastModifiedOrEtag);
     1354    }
     1355
     1356    Private->LastModifiedOrEtag = AllocateCopyPool (AsciiStrSize (HttpHeader->FieldValue), HttpHeader->FieldValue);
     1357  }
     1358
     1359  //
     1360  // 3.2.2 Validate the range response. If operation is being resumed,
     1361  // server must respond with Content-Range.
     1362  //
     1363  if (ResumingOperation) {
     1364    HttpHeader = HttpFindHeader (
     1365                   ResponseData->HeaderCount,
     1366                   ResponseData->Headers,
     1367                   HTTP_HEADER_CONTENT_RANGE
     1368                   );
     1369    if ((HttpHeader == NULL) ||
     1370        (AsciiStrnCmp (HttpHeader->FieldValue, "bytes", 5) != 0))
     1371    {
     1372      Status = EFI_UNSUPPORTED;
     1373      goto ERROR_5;
     1374    }
     1375
     1376    // Gets the total size of ranged data (Content-Range: <unit> <range-start>-<range-end>/<size>)
     1377    // and check if it remains the same
     1378    ContentRangeResponseValue = AsciiStrStr (HttpHeader->FieldValue, "/");
     1379    if (ContentRangeResponseValue == NULL) {
     1380      Status = EFI_INVALID_PARAMETER;
     1381      goto ERROR_5;
     1382    }
     1383
     1384    ContentRangeResponseValue++;
     1385    ContentLength = AsciiStrDecimalToUintn (ContentRangeResponseValue);
     1386    if (ContentLength != *BufferSize) {
     1387      Status = EFI_INVALID_PARAMETER;
     1388      goto ERROR_5;
     1389    }
     1390  }
     1391
    12481392  //
    12491393  // 3.3 Init a message-body parser from the header information.
     
    12961440      // just download the message body to the user provided buffer directly.
    12971441      //
     1442      if (ResumingOperation && ((ContentLength + Private->PartialTransferredSize) > *BufferSize)) {
     1443        Status = EFI_INVALID_PARAMETER;
     1444        goto ERROR_6;
     1445      }
     1446
    12981447      ReceivedSize = 0;
    12991448      while (ReceivedSize < ContentLength) {
    1300         ResponseBody.Body       = (CHAR8 *)Buffer + ReceivedSize;
    1301         ResponseBody.BodyLength = *BufferSize - ReceivedSize;
     1449        ResponseBody.Body       = (CHAR8 *)Buffer + (ReceivedSize + Private->PartialTransferredSize);
     1450        ResponseBody.BodyLength = *BufferSize - (ReceivedSize + Private->PartialTransferredSize);
    13021451        Status                  = HttpIoRecvResponse (
    13031452                                    &Private->HttpIo,
     
    13081457          if (EFI_ERROR (ResponseBody.Status)) {
    13091458            Status = ResponseBody.Status;
     1459          }
     1460
     1461          if ((Status == EFI_TIMEOUT) || (Status == EFI_DEVICE_ERROR)) {
     1462            // For EFI_TIMEOUT and EFI_DEVICE_ERROR errors, we may resume the operation.
     1463            // We will not check if server sent Accept-Ranges header, because some back-ends
     1464            // do not report this header, even when supporting it. Know example: CloudFlare CDN Cache.
     1465            Private->PartialTransferredSize = ReceivedSize;
     1466            DEBUG (
     1467              (
     1468               DEBUG_WARN | DEBUG_INFO,
     1469               "HttpBootGetBootFile: Transfer error. Bytes transferred so far: %lu.\n",
     1470               ReceivedSize
     1471              )
     1472              );
    13101473          }
    13111474
     
    13271490        }
    13281491      }
     1492
     1493      // download completed, there is no more partial data
     1494      Private->PartialTransferredSize = 0;
    13291495    } else {
    13301496      //
     
    13861552  // 3.5 Message-body receive & parse is completed, we should be able to get the file size now.
    13871553  //
    1388   Status = HttpGetEntityLength (Parser, &ContentLength);
    1389   if (EFI_ERROR (Status)) {
    1390     goto ERROR_6;
     1554  if (!ResumingOperation) {
     1555    Status = HttpGetEntityLength (Parser, &ContentLength);
     1556    if (EFI_ERROR (Status)) {
     1557      goto ERROR_6;
     1558    }
     1559  } else {
     1560    ContentLength = Private->BootFileSize;
    13911561  }
    13921562
Note: See TracChangeset for help on using the changeset viewer.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette