+\r
+\r
+/**\r
+ Get the next string, which is distinguished by specified separator.\r
+\r
+ @param[in] String Pointer to the string.\r
+ @param[in] Separator Specified separator used to distinguish where is the beginning\r
+ of next string.\r
+\r
+ @return Pointer to the next string.\r
+ @return NULL if not find or String is NULL.\r
+\r
+**/\r
+CHAR8 *\r
+AsciiStrGetNextToken (\r
+ IN CONST CHAR8 *String,\r
+ IN CHAR8 Separator\r
+ )\r
+{\r
+ CONST CHAR8 *Token;\r
+\r
+ Token = String;\r
+ while (TRUE) {\r
+ if (*Token == 0) {\r
+ return NULL;\r
+ }\r
+ if (*Token == Separator) {\r
+ return (CHAR8 *)(Token + 1);\r
+ }\r
+ Token++;\r
+ }\r
+}\r
+\r
+/**\r
+ Set FieldName and FieldValue into specified HttpHeader.\r
+\r
+ @param[in,out] HttpHeader Specified HttpHeader.\r
+ @param[in] FieldName FieldName of this HttpHeader, a NULL terminated ASCII string.\r
+ @param[in] FieldValue FieldValue of this HttpHeader, a NULL terminated ASCII string.\r
+\r
+\r
+ @retval EFI_SUCCESS The FieldName and FieldValue are set into HttpHeader successfully.\r
+ @retval EFI_INVALID_PARAMETER The parameter is invalid.\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+HttpSetFieldNameAndValue (\r
+ IN OUT EFI_HTTP_HEADER *HttpHeader,\r
+ IN CONST CHAR8 *FieldName,\r
+ IN CONST CHAR8 *FieldValue\r
+ )\r
+{\r
+ UINTN FieldNameSize;\r
+ UINTN FieldValueSize;\r
+\r
+ if (HttpHeader == NULL || FieldName == NULL || FieldValue == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (HttpHeader->FieldName != NULL) {\r
+ FreePool (HttpHeader->FieldName);\r
+ }\r
+ if (HttpHeader->FieldValue != NULL) {\r
+ FreePool (HttpHeader->FieldValue);\r
+ }\r
+\r
+ FieldNameSize = AsciiStrSize (FieldName);\r
+ HttpHeader->FieldName = AllocateZeroPool (FieldNameSize);\r
+ if (HttpHeader->FieldName == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ CopyMem (HttpHeader->FieldName, FieldName, FieldNameSize);\r
+ HttpHeader->FieldName[FieldNameSize - 1] = 0;\r
+\r
+ FieldValueSize = AsciiStrSize (FieldValue);\r
+ HttpHeader->FieldValue = AllocateZeroPool (FieldValueSize);\r
+ if (HttpHeader->FieldValue == NULL) {\r
+ FreePool (HttpHeader->FieldName);\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ CopyMem (HttpHeader->FieldValue, FieldValue, FieldValueSize);\r
+ HttpHeader->FieldValue[FieldValueSize - 1] = 0;\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Get one key/value header pair from the raw string.\r
+\r
+ @param[in] String Pointer to the raw string.\r
+ @param[out] FieldName Points directly to field name within 'HttpHeader'.\r
+ @param[out] FieldValue Points directly to field value within 'HttpHeader'.\r
+\r
+ @return Pointer to the next raw string.\r
+ @return NULL if no key/value header pair from this raw string.\r
+\r
+**/\r
+CHAR8 *\r
+EFIAPI\r
+HttpGetFieldNameAndValue (\r
+ IN CHAR8 *String,\r
+ OUT CHAR8 **FieldName,\r
+ OUT CHAR8 **FieldValue\r
+ )\r
+{\r
+ CHAR8 *FieldNameStr;\r
+ CHAR8 *FieldValueStr;\r
+ CHAR8 *StrPtr;\r
+ CHAR8 *EndofHeader;\r
+\r
+ if (String == NULL || FieldName == NULL || FieldValue == NULL) {\r
+ return NULL;\r
+ }\r
+\r
+ *FieldName = NULL;\r
+ *FieldValue = NULL;\r
+ FieldNameStr = NULL;\r
+ FieldValueStr = NULL;\r
+ StrPtr = NULL;\r
+ EndofHeader = NULL;\r
+\r
+\r
+ //\r
+ // Check whether the raw HTTP header string is valid or not.\r
+ //\r
+ EndofHeader = AsciiStrStr (String, "\r\n\r\n");\r
+ if (EndofHeader == NULL) {\r
+ return NULL;\r
+ }\r
+\r
+ //\r
+ // Each header field consists of a name followed by a colon (":") and the field value.\r
+ // The field value MAY be preceded by any amount of LWS, though a single SP is preferred.\r
+ //\r
+ // message-header = field-name ":" [ field-value ]\r
+ // field-name = token\r
+ // field-value = *( field-content | LWS )\r
+ //\r
+ // Note: "*(element)" allows any number element, including zero; "1*(element)" requires at least one element.\r
+ // [element] means element is optional.\r
+ // LWS = [CRLF] 1*(SP|HT), it can be ' ' or '\t' or '\r\n ' or '\r\n\t'.\r
+ // CRLF = '\r\n'.\r
+ // SP = ' '.\r
+ // HT = '\t' (Tab).\r
+ //\r
+ FieldNameStr = String;\r
+ FieldValueStr = AsciiStrGetNextToken (FieldNameStr, ':');\r
+ if (FieldValueStr == NULL) {\r
+ return NULL;\r
+ }\r
+\r
+ //\r
+ // Replace ':' with 0, then FieldName has been retrived from String.\r
+ //\r
+ *(FieldValueStr - 1) = 0;\r
+\r
+ //\r
+ // Handle FieldValueStr, skip all the preceded LWS.\r
+ //\r
+ while (TRUE) {\r
+ if (*FieldValueStr == ' ' || *FieldValueStr == '\t') {\r
+ //\r
+ // Boundary condition check.\r
+ //\r
+ if ((UINTN) EndofHeader - (UINTN) FieldValueStr < 1) {\r
+ //\r
+ // Wrong String format!\r
+ //\r
+ return NULL;\r
+ }\r
+\r
+ FieldValueStr ++;\r
+ } else if (*FieldValueStr == '\r') {\r
+ //\r
+ // Boundary condition check.\r
+ //\r
+ if ((UINTN) EndofHeader - (UINTN) FieldValueStr < 3) {\r
+ //\r
+ // No more preceded LWS, so break here.\r
+ //\r
+ break;\r
+ }\r
+\r
+ if (*(FieldValueStr + 1) == '\n' ) {\r
+ if (*(FieldValueStr + 2) == ' ' || *(FieldValueStr + 2) == '\t') {\r
+ FieldValueStr = FieldValueStr + 3;\r
+ } else {\r
+ //\r
+ // No more preceded LWS, so break here.\r
+ //\r
+ break;\r
+ }\r
+ } else {\r
+ //\r
+ // Wrong String format!\r
+ //\r
+ return NULL;\r
+ }\r
+ } else {\r
+ //\r
+ // No more preceded LWS, so break here.\r
+ //\r
+ break;\r
+ }\r
+ }\r
+\r
+ StrPtr = FieldValueStr;\r
+ do {\r
+ //\r
+ // Handle the LWS within the field value.\r
+ //\r
+ StrPtr = AsciiStrGetNextToken (StrPtr, '\r');\r
+ if (StrPtr == NULL || *StrPtr != '\n') {\r
+ //\r
+ // Wrong String format!\r
+ //\r
+ return NULL;\r
+ }\r
+\r
+ StrPtr++;\r
+ } while (*StrPtr == ' ' || *StrPtr == '\t');\r
+\r
+ //\r
+ // Replace '\r' with 0\r
+ //\r
+ *(StrPtr - 2) = 0;\r
+\r
+ //\r
+ // Get FieldName and FieldValue.\r
+ //\r
+ *FieldName = FieldNameStr;\r
+ *FieldValue = FieldValueStr;\r
+\r
+ return StrPtr;\r
+}\r
+\r
+/**\r
+ Free existing HeaderFields.\r
+\r
+ @param[in] HeaderFields Pointer to array of key/value header pairs waitting for free.\r
+ @param[in] FieldCount The number of header pairs in HeaderFields.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+HttpFreeHeaderFields (\r
+ IN EFI_HTTP_HEADER *HeaderFields,\r
+ IN UINTN FieldCount\r
+ )\r
+{\r
+ UINTN Index;\r
+\r
+ if (HeaderFields != NULL) {\r
+ for (Index = 0; Index < FieldCount; Index++) {\r
+ if (HeaderFields[Index].FieldName != NULL) {\r
+ FreePool (HeaderFields[Index].FieldName);\r
+ }\r
+ if (HeaderFields[Index].FieldValue != NULL) {\r
+ FreePool (HeaderFields[Index].FieldValue);\r
+ }\r
+ }\r
+\r
+ FreePool (HeaderFields);\r
+ }\r
+}\r
+\r
+/**\r
+ Generate HTTP request message.\r
+\r
+ This function will allocate memory for the whole HTTP message and generate a\r
+ well formatted HTTP Request message in it, include the Request-Line, header\r
+ fields and also the message body. It is the caller's responsibility to free\r
+ the buffer returned in *RequestMsg.\r
+\r
+ @param[in] Message Pointer to the EFI_HTTP_MESSAGE structure which\r
+ contains the required information to generate\r
+ the HTTP request message.\r
+ @param[in] Url The URL of a remote host.\r
+ @param[out] RequestMsg Pointer to the created HTTP request message.\r
+ NULL if any error occured.\r
+ @param[out] RequestMsgSize Size of the RequestMsg (in bytes).\r
+\r
+ @retval EFI_SUCCESS If HTTP request string was created successfully.\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.\r
+ @retval EFI_INVALID_PARAMETER The input arguments are invalid.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+HttpGenRequestMessage (\r
+ IN CONST EFI_HTTP_MESSAGE *Message,\r
+ IN CONST CHAR8 *Url,\r
+ OUT CHAR8 **RequestMsg,\r
+ OUT UINTN *RequestMsgSize\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINTN StrLength;\r
+ CHAR8 *RequestPtr;\r
+ UINTN HttpHdrSize;\r
+ UINTN MsgSize;\r
+ BOOLEAN Success;\r
+ VOID *HttpHdr;\r
+ EFI_HTTP_HEADER **AppendList;\r
+ UINTN Index;\r
+ EFI_HTTP_UTILITIES_PROTOCOL *HttpUtilitiesProtocol;\r
+\r
+ Status = EFI_SUCCESS;\r
+ HttpHdrSize = 0;\r
+ MsgSize = 0;\r
+ Success = FALSE;\r
+ HttpHdr = NULL;\r
+ AppendList = NULL;\r
+ HttpUtilitiesProtocol = NULL;\r
+\r
+ //\r
+ // 1. If we have a Request, we cannot have a NULL Url\r
+ // 2. If we have a Request, HeaderCount can not be non-zero\r
+ // 3. If we do not have a Request, HeaderCount should be zero\r
+ // 4. If we do not have Request and Headers, we need at least a message-body\r
+ //\r
+ if ((Message == NULL || RequestMsg == NULL || RequestMsgSize == NULL) ||\r
+ (Message->Data.Request != NULL && Url == NULL) ||\r
+ (Message->Data.Request != NULL && Message->HeaderCount == 0) ||\r
+ (Message->Data.Request == NULL && Message->HeaderCount != 0) ||\r
+ (Message->Data.Request == NULL && Message->HeaderCount == 0 && Message->BodyLength == 0)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (Message->HeaderCount != 0) {\r
+ //\r
+ // Locate the HTTP_UTILITIES protocol.\r
+ //\r
+ Status = gBS->LocateProtocol (\r
+ &gEfiHttpUtilitiesProtocolGuid,\r
+ NULL,\r
+ (VOID **) &HttpUtilitiesProtocol\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((DEBUG_ERROR,"Failed to locate Http Utilities protocol. Status = %r.\n", Status));\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Build AppendList to send into HttpUtilitiesBuild\r
+ //\r
+ AppendList = AllocateZeroPool (sizeof (EFI_HTTP_HEADER *) * (Message->HeaderCount));\r
+ if (AppendList == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ for(Index = 0; Index < Message->HeaderCount; Index++){\r
+ AppendList[Index] = &Message->Headers[Index];\r
+ }\r
+\r
+ //\r
+ // Build raw HTTP Headers\r
+ //\r
+ Status = HttpUtilitiesProtocol->Build (\r
+ HttpUtilitiesProtocol,\r
+ 0,\r
+ NULL,\r
+ 0,\r
+ NULL,\r
+ Message->HeaderCount,\r
+ AppendList,\r
+ &HttpHdrSize,\r
+ &HttpHdr\r
+ );\r
+\r
+ FreePool (AppendList);\r
+\r
+ if (EFI_ERROR (Status) || HttpHdr == NULL){\r
+ return Status;\r
+ }\r
+ }\r
+\r
+ //\r
+ // If we have headers to be sent, account for it.\r
+ //\r
+ if (Message->HeaderCount != 0) {\r
+ MsgSize = HttpHdrSize;\r
+ }\r
+\r
+ //\r
+ // If we have a request line, account for the fields.\r
+ //\r
+ if (Message->Data.Request != NULL) {\r
+ MsgSize += HTTP_METHOD_MAXIMUM_LEN + AsciiStrLen (HTTP_VERSION_CRLF_STR) + AsciiStrLen (Url);\r
+ }\r
+\r
+\r
+ //\r
+ // If we have a message body to be sent, account for it.\r
+ //\r
+ MsgSize += Message->BodyLength;\r
+\r
+ //\r
+ // memory for the string that needs to be sent to TCP\r
+ //\r
+ *RequestMsg = NULL;\r
+ *RequestMsg = AllocateZeroPool (MsgSize);\r
+ if (*RequestMsg == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto Exit;\r
+ }\r
+\r
+ RequestPtr = *RequestMsg;\r
+ //\r
+ // Construct header request\r
+ //\r
+ if (Message->Data.Request != NULL) {\r
+ switch (Message->Data.Request->Method) {\r
+ case HttpMethodGet:\r
+ StrLength = sizeof (HTTP_METHOD_GET) - 1;\r
+ CopyMem (RequestPtr, HTTP_METHOD_GET, StrLength);\r
+ RequestPtr += StrLength;\r
+ break;\r
+ case HttpMethodPut:\r
+ StrLength = sizeof (HTTP_METHOD_PUT) - 1;\r
+ CopyMem (RequestPtr, HTTP_METHOD_PUT, StrLength);\r
+ RequestPtr += StrLength;\r
+ break;\r
+ case HttpMethodPatch:\r
+ StrLength = sizeof (HTTP_METHOD_PATCH) - 1;\r
+ CopyMem (RequestPtr, HTTP_METHOD_PATCH, StrLength);\r
+ RequestPtr += StrLength;\r
+ break;\r
+ case HttpMethodPost:\r
+ StrLength = sizeof (HTTP_METHOD_POST) - 1;\r
+ CopyMem (RequestPtr, HTTP_METHOD_POST, StrLength);\r
+ RequestPtr += StrLength;\r
+ break;\r
+ case HttpMethodHead:\r
+ StrLength = sizeof (HTTP_METHOD_HEAD) - 1;\r
+ CopyMem (RequestPtr, HTTP_METHOD_HEAD, StrLength);\r
+ RequestPtr += StrLength;\r
+ break;\r
+ case HttpMethodDelete:\r
+ StrLength = sizeof (HTTP_METHOD_DELETE) - 1;\r
+ CopyMem (RequestPtr, HTTP_METHOD_DELETE, StrLength);\r
+ RequestPtr += StrLength;\r
+ break;\r
+ default:\r
+ ASSERT (FALSE);\r
+ Status = EFI_INVALID_PARAMETER;\r
+ goto Exit;\r
+ }\r
+\r
+ StrLength = AsciiStrLen(EMPTY_SPACE);\r
+ CopyMem (RequestPtr, EMPTY_SPACE, StrLength);\r
+ RequestPtr += StrLength;\r
+\r
+ StrLength = AsciiStrLen (Url);\r
+ CopyMem (RequestPtr, Url, StrLength);\r
+ RequestPtr += StrLength;\r
+\r
+ StrLength = sizeof (HTTP_VERSION_CRLF_STR) - 1;\r
+ CopyMem (RequestPtr, HTTP_VERSION_CRLF_STR, StrLength);\r
+ RequestPtr += StrLength;\r
+\r
+ if (HttpHdr != NULL) {\r
+ //\r
+ // Construct header\r
+ //\r
+ CopyMem (RequestPtr, HttpHdr, HttpHdrSize);\r
+ RequestPtr += HttpHdrSize;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Construct body\r
+ //\r
+ if (Message->Body != NULL) {\r
+ CopyMem (RequestPtr, Message->Body, Message->BodyLength);\r
+ RequestPtr += Message->BodyLength;\r
+ }\r
+\r
+ //\r
+ // Done\r
+ //\r
+ (*RequestMsgSize) = (UINTN)(RequestPtr) - (UINTN)(*RequestMsg);\r
+ Success = TRUE;\r
+\r
+Exit:\r
+\r
+ if (!Success) {\r
+ if (*RequestMsg != NULL) {\r
+ FreePool (*RequestMsg);\r
+ }\r
+ *RequestMsg = NULL;\r
+ return Status;\r
+ }\r
+\r
+ if (HttpHdr != NULL) {\r
+ FreePool (HttpHdr);\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Translate the status code in HTTP message to EFI_HTTP_STATUS_CODE defined\r
+ in UEFI 2.5 specification.\r
+\r
+ @param[in] StatusCode The status code value in HTTP message.\r
+\r
+ @return Value defined in EFI_HTTP_STATUS_CODE .\r
+\r
+**/\r
+EFI_HTTP_STATUS_CODE\r
+EFIAPI\r
+HttpMappingToStatusCode (\r
+ IN UINTN StatusCode\r
+ )\r
+{\r
+ switch (StatusCode) {\r
+ case 100:\r
+ return HTTP_STATUS_100_CONTINUE;\r
+ case 101:\r
+ return HTTP_STATUS_101_SWITCHING_PROTOCOLS;\r
+ case 200:\r
+ return HTTP_STATUS_200_OK;\r
+ case 201:\r
+ return HTTP_STATUS_201_CREATED;\r
+ case 202:\r
+ return HTTP_STATUS_202_ACCEPTED;\r
+ case 203:\r
+ return HTTP_STATUS_203_NON_AUTHORITATIVE_INFORMATION;\r
+ case 204:\r
+ return HTTP_STATUS_204_NO_CONTENT;\r
+ case 205:\r
+ return HTTP_STATUS_205_RESET_CONTENT;\r
+ case 206:\r
+ return HTTP_STATUS_206_PARTIAL_CONTENT;\r
+ case 300:\r
+ return HTTP_STATUS_300_MULTIPLE_CHOICES;\r
+ case 301:\r
+ return HTTP_STATUS_301_MOVED_PERMANENTLY;\r
+ case 302:\r
+ return HTTP_STATUS_302_FOUND;\r
+ case 303:\r
+ return HTTP_STATUS_303_SEE_OTHER;\r
+ case 304:\r
+ return HTTP_STATUS_304_NOT_MODIFIED;\r
+ case 305:\r
+ return HTTP_STATUS_305_USE_PROXY;\r
+ case 307:\r
+ return HTTP_STATUS_307_TEMPORARY_REDIRECT;\r
+ case 308:\r
+ return HTTP_STATUS_308_PERMANENT_REDIRECT;\r
+ case 400:\r
+ return HTTP_STATUS_400_BAD_REQUEST;\r
+ case 401:\r
+ return HTTP_STATUS_401_UNAUTHORIZED;\r
+ case 402:\r
+ return HTTP_STATUS_402_PAYMENT_REQUIRED;\r
+ case 403:\r
+ return HTTP_STATUS_403_FORBIDDEN;\r
+ case 404:\r
+ return HTTP_STATUS_404_NOT_FOUND;\r
+ case 405:\r
+ return HTTP_STATUS_405_METHOD_NOT_ALLOWED;\r
+ case 406:\r
+ return HTTP_STATUS_406_NOT_ACCEPTABLE;\r
+ case 407:\r
+ return HTTP_STATUS_407_PROXY_AUTHENTICATION_REQUIRED;\r
+ case 408:\r
+ return HTTP_STATUS_408_REQUEST_TIME_OUT;\r
+ case 409:\r
+ return HTTP_STATUS_409_CONFLICT;\r
+ case 410:\r
+ return HTTP_STATUS_410_GONE;\r
+ case 411:\r
+ return HTTP_STATUS_411_LENGTH_REQUIRED;\r
+ case 412:\r
+ return HTTP_STATUS_412_PRECONDITION_FAILED;\r
+ case 413:\r
+ return HTTP_STATUS_413_REQUEST_ENTITY_TOO_LARGE;\r
+ case 414:\r
+ return HTTP_STATUS_414_REQUEST_URI_TOO_LARGE;\r
+ case 415:\r
+ return HTTP_STATUS_415_UNSUPPORTED_MEDIA_TYPE;\r
+ case 416:\r
+ return HTTP_STATUS_416_REQUESTED_RANGE_NOT_SATISFIED;\r
+ case 417:\r
+ return HTTP_STATUS_417_EXPECTATION_FAILED;\r
+ case 500:\r
+ return HTTP_STATUS_500_INTERNAL_SERVER_ERROR;\r
+ case 501:\r
+ return HTTP_STATUS_501_NOT_IMPLEMENTED;\r
+ case 502:\r
+ return HTTP_STATUS_502_BAD_GATEWAY;\r
+ case 503:\r
+ return HTTP_STATUS_503_SERVICE_UNAVAILABLE;\r
+ case 504:\r
+ return HTTP_STATUS_504_GATEWAY_TIME_OUT;\r
+ case 505:\r
+ return HTTP_STATUS_505_HTTP_VERSION_NOT_SUPPORTED;\r
+\r
+ default:\r
+ return HTTP_STATUS_UNSUPPORTED_STATUS;\r
+ }\r
+}\r
+\r
+/**\r
+ Check whether header field called FieldName is in DeleteList.\r
+\r
+ @param[in] DeleteList Pointer to array of key/value header pairs.\r
+ @param[in] DeleteCount The number of header pairs.\r
+ @param[in] FieldName Pointer to header field's name.\r
+\r
+ @return TRUE if FieldName is not in DeleteList, that means this header field is valid.\r
+ @return FALSE if FieldName is in DeleteList, that means this header field is invalid.\r
+\r
+**/\r
+BOOLEAN\r
+EFIAPI\r
+HttpIsValidHttpHeader (\r
+ IN CHAR8 *DeleteList[],\r
+ IN UINTN DeleteCount,\r
+ IN CHAR8 *FieldName\r
+ )\r
+{\r
+ UINTN Index;\r
+\r
+ if (FieldName == NULL) {\r
+ return FALSE;\r
+ }\r
+\r
+ for (Index = 0; Index < DeleteCount; Index++) {\r
+ if (DeleteList[Index] == NULL) {\r
+ continue;\r
+ }\r
+\r
+ if (AsciiStrCmp (FieldName, DeleteList[Index]) == 0) {\r
+ return FALSE;\r
+ }\r
+ }\r
+\r
+ return TRUE;\r
+}\r
+\r