summaryrefslogtreecommitdiffstats
path: root/RedfishPkg/RedfishRestExDxe/RedfishRestExImpl.c
blob: 41f2b29c83bee1c0afcddaa6163b036cb6d6f2b1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
/** @file
  RestExDxe support functions implementation.

  Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
  (C) Copyright 2020 Hewlett Packard Enterprise Development LP<BR>
  Copyright (c) 2023, American Megatrends International LLC.

  SPDX-License-Identifier: BSD-2-Clause-Patent

**/
#include <Uefi.h>
#include "RedfishRestExInternal.h"

/**
  Create a new TLS session becuase the previous on is closed.
  status.

  @param[in]  Instance            Pointer to EFI_REST_EX_PROTOCOL instance for a particular
                                  REST service.
  @retval EFI_SUCCESS             operation succeeded.
  @retval EFI_ERROR               Other errors.

**/
EFI_STATUS
ResetHttpTslSession (
  IN   RESTEX_INSTANCE  *Instance
  )
{
  EFI_STATUS  Status;

  DEBUG ((DEBUG_INFO, "%a: TCP connection is finished. Could be TSL session closure, reset HTTP instance for the new TLS session.\n", __func__));

  Status = Instance->HttpIo.Http->Configure (Instance->HttpIo.Http, NULL);
  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_ERROR, "%a: Error to reset HTTP instance.\n", __func__));
    return Status;
  }

  Status = Instance->HttpIo.Http->Configure (Instance->HttpIo.Http, &((EFI_REST_EX_HTTP_CONFIG_DATA *)Instance->ConfigData)->HttpConfigData);
  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_ERROR, "%a: Error to re-initiate HTTP instance.\n", __func__));
  }

  return Status;
}

/**
  This function check Http receive status.

  @param[in]  Instance             Pointer to EFI_REST_EX_PROTOCOL instance for a particular
                                   REST service.
  @param[in]  HttpIoReceiveStatus  This is the status return from HttpIoRecvResponse

  @retval EFI_SUCCESS           The payload receive from Redfish service in successfully.
  @retval EFI_NOT_READY         May need to resend the HTTP request.
  @retval EFI_DEVICE_ERROR      Something wrong and can't be resolved.
  @retval Others                Other errors as indicated.

**/
EFI_STATUS
RedfishCheckHttpReceiveStatus (
  IN RESTEX_INSTANCE  *Instance,
  IN EFI_STATUS       HttpIoReceiveStatus
  )
{
  EFI_STATUS  Status;
  EFI_STATUS  ReturnStatus;

  if (!EFI_ERROR (HttpIoReceiveStatus)) {
    ReturnStatus = EFI_SUCCESS;
  } else if (HttpIoReceiveStatus != EFI_CONNECTION_FIN) {
    if ((Instance->Flags & RESTEX_INSTANCE_FLAGS_TCP_ERROR_RETRY) == 0) {
      DEBUG ((DEBUG_ERROR, "%a: TCP error, reset HTTP session.\n", __func__));
      Instance->Flags |= RESTEX_INSTANCE_FLAGS_TCP_ERROR_RETRY;
      gBS->Stall (500);
      Status = ResetHttpTslSession (Instance);
      if (!EFI_ERROR (Status)) {
        return EFI_NOT_READY;
      }

      DEBUG ((DEBUG_ERROR, "%a: Reset HTTP instance fail.\n", __func__));
    }

    ReturnStatus = EFI_DEVICE_ERROR;
  } else {
    if ((Instance->Flags & RESTEX_INSTANCE_FLAGS_TLS_RETRY) != 0) {
      DEBUG ((DEBUG_ERROR, "%a: REST_EX Send and receive fail even with a new TLS session.\n", __func__));
      ReturnStatus = EFI_DEVICE_ERROR;
    }

    Instance->Flags |= RESTEX_INSTANCE_FLAGS_TLS_RETRY;
    Status           = ResetHttpTslSession (Instance);
    if (EFI_ERROR (Status)) {
      DEBUG ((DEBUG_ERROR, "%a: Reset HTTP instance fail.\n", __func__));
      ReturnStatus = EFI_DEVICE_ERROR;
    }

    return EFI_NOT_READY;
  }

  //
  // Clean TLS new session retry and error try flags.
  //
  Instance->Flags &= ~(RESTEX_INSTANCE_FLAGS_TLS_RETRY | RESTEX_INSTANCE_FLAGS_TCP_ERROR_RETRY);
  return ReturnStatus;
}

/**
  This function send the HTTP request without body to see
  if the write to URL is permitted by Redfish service. This function
  checks if the HTTP request has Content-length in HTTP header. If yes,
  set HTTP body to NULL and then send to service. Check the HTTP status
  for the firther actions.

  @param[in]  This                    Pointer to EFI_REST_EX_PROTOCOL instance for a particular
                                      REST service.
  @param[in]  RequestMessage          Pointer to the HTTP request data for this resource
  @param[in]  PreservedRequestHeaders The pointer to save the request headers
  @param[in]  ItsWrite                This is write method to URL.

  @retval EFI_INVALID_PARAMETER  Improper given parameters.
  @retval EFI_SUCCESS            This HTTP request is free to send to Redfish service.
  @retval EFI_OUT_OF_RESOURCES   NOt enough memory to process.
  @retval EFI_ACCESS_DENIED      Not allowed to write to this URL.

  @retval Others                 Other errors as indicated.

**/
EFI_STATUS
RedfishHttpAddExpectation (
  IN EFI_REST_EX_PROTOCOL  *This,
  IN EFI_HTTP_MESSAGE      *RequestMessage,
  IN EFI_HTTP_HEADER       **PreservedRequestHeaders,
  IN BOOLEAN               *ItsWrite
  )
{
  EFI_HTTP_HEADER  *NewHeaders;

  if ((This == NULL) || (RequestMessage == NULL)) {
    return EFI_INVALID_PARAMETER;
  }

  *ItsWrite = FALSE;

  if ((RequestMessage->Data.Request->Method != HttpMethodPut) && (RequestMessage->Data.Request->Method != HttpMethodPost) &&
      (RequestMessage->Data.Request->Method != HttpMethodPatch))
  {
    return EFI_SUCCESS;
  }

  *ItsWrite = TRUE;

  //
  // Check PCD before adding Expect header
  //
  if (FixedPcdGetBool (PcdRedfishRestExAddingExpect)) {
    if (PreservedRequestHeaders != NULL) {
      *PreservedRequestHeaders = RequestMessage->Headers;
    }

    NewHeaders = AllocateZeroPool ((RequestMessage->HeaderCount + 1) * sizeof (EFI_HTTP_HEADER));
    CopyMem ((VOID *)NewHeaders, (VOID *)RequestMessage->Headers, RequestMessage->HeaderCount * sizeof (EFI_HTTP_HEADER));
    HttpSetFieldNameAndValue (NewHeaders + RequestMessage->HeaderCount, HTTP_HEADER_EXPECT, HTTP_EXPECT_100_CONTINUE);
    RequestMessage->HeaderCount++;
    RequestMessage->Headers = NewHeaders;
  }

  return EFI_SUCCESS;
}