]> git.proxmox.com Git - mirror_edk2.git/blob - NetworkPkg/HttpDxe/HttpUtilities.c
c2a99a40bf1c37eeb943ea37692155cb7eae9c8d
[mirror_edk2.git] / NetworkPkg / HttpDxe / HttpUtilities.c
1 /** @file
2
3 Implementation of help functions to parse HTTP message header.
4
5 Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
6 This program and the accompanying materials
7 are licensed and made available under the terms and conditions of the BSD License
8 which accompanies this distribution. The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php
10
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13
14 **/
15
16 #include "HttpDriver.h"
17
18 /**
19 Get the next string, which is distinguished by specified seperator.
20
21 @param[in] String Pointer to the string.
22 @param[in] Seperator Specified seperator used to distinguish where is the beginning
23 of next string.
24
25 @return Pointer to the next string.
26 @return NULL if not find or String is NULL.
27
28 **/
29 CHAR8 *
30 AsciiStrGetNextToken (
31 IN CONST CHAR8 *String,
32 IN CHAR8 Seperator
33 )
34 {
35 CONST CHAR8 *Token;
36
37 Token = String;
38 while (TRUE) {
39 if (*Token == 0) {
40 return NULL;
41 }
42 if (*Token == Seperator) {
43 return (CHAR8 *) (Token + 1);
44 }
45 Token++;
46 }
47 }
48
49 /**
50 Free existing HeaderFields.
51
52 @param[in] HeaderFields Pointer to array of key/value header pairs waitting for free.
53 @param[in] FieldCount The number of header pairs in HeaderFields.
54
55 **/
56 VOID
57 FreeHeaderFields (
58 IN EFI_HTTP_HEADER *HeaderFields,
59 IN UINTN FieldCount
60 )
61 {
62 UINTN Index;
63
64 if (HeaderFields != NULL) {
65 for (Index = 0; Index < FieldCount; Index++) {
66 if(HeaderFields[Index].FieldName != NULL) {
67 FreePool (HeaderFields[Index].FieldName);
68 }
69 if(HeaderFields[Index].FieldValue != NULL) {
70 FreePool (HeaderFields[Index].FieldValue);
71 }
72 }
73
74 FreePool (HeaderFields);
75 }
76 }
77
78 /**
79 Find required header field in HeaderFields.
80
81 @param[in] HeaderFields Pointer to array of key/value header pairs.
82 @param[in] FieldCount The number of header pairs.
83 @param[in] FieldName Pointer to header field's name.
84
85 @return Pointer to the queried header field.
86 @return NULL if not find this required header field.
87
88 **/
89 EFI_HTTP_HEADER *
90 FindHttpHeader (
91 IN EFI_HTTP_HEADER *HeaderFields,
92 IN UINTN FieldCount,
93 IN CHAR8 *FieldName
94 )
95 {
96 UINTN Index;
97
98 for (Index = 0; Index < FieldCount; Index++) {
99 if (AsciiStrCmp (FieldName, HeaderFields[Index].FieldName) == 0) {
100 //
101 // Find the required header field.
102 //
103 return &HeaderFields[Index];
104 }
105 }
106 return NULL;
107 }
108
109 /**
110 Check whether header field called FieldName is in DeleteList.
111
112 @param[in] DeleteList Pointer to array of key/value header pairs.
113 @param[in] DeleteCount The number of header pairs.
114 @param[in] FieldName Pointer to header field's name.
115
116 @return TRUE if FieldName is not in DeleteList, that means this header field is valid.
117 @return FALSE if FieldName is in DeleteList, that means this header field is invalid.
118
119 **/
120 BOOLEAN
121 IsValidHttpHeader (
122 IN CHAR8 *DeleteList[],
123 IN UINTN DeleteCount,
124 IN CHAR8 *FieldName
125 )
126 {
127 UINTN Index;
128
129 for (Index = 0; Index < DeleteCount; Index++) {
130 if (AsciiStrCmp (FieldName, DeleteList[Index]) == 0) {
131 return FALSE;
132 }
133 }
134
135 return TRUE;
136 }
137
138 /**
139 Set FieldName and FieldValue into specified HttpHeader.
140
141 @param[in] HttpHeader Specified HttpHeader.
142 @param[in] FieldName FieldName of this HttpHeader.
143 @param[in] FieldValue FieldValue of this HttpHeader.
144
145
146 @retval EFI_SUCCESS The FieldName and FieldValue are set into HttpHeader successfully.
147 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
148
149 **/
150 EFI_STATUS
151 SetFieldNameAndValue (
152 IN EFI_HTTP_HEADER *HttpHeader,
153 IN CHAR8 *FieldName,
154 IN CHAR8 *FieldValue
155 )
156 {
157 UINTN FieldNameSize;
158 UINTN FieldValueSize;
159
160 if (HttpHeader->FieldName != NULL) {
161 FreePool (HttpHeader->FieldName);
162 }
163 if (HttpHeader->FieldValue != NULL) {
164 FreePool (HttpHeader->FieldValue);
165 }
166
167 FieldNameSize = AsciiStrSize (FieldName);
168 HttpHeader->FieldName = AllocateZeroPool (FieldNameSize);
169 if (HttpHeader->FieldName == NULL) {
170 return EFI_OUT_OF_RESOURCES;
171 }
172 CopyMem (HttpHeader->FieldName, FieldName, FieldNameSize);
173 HttpHeader->FieldName[FieldNameSize - 1] = 0;
174
175 FieldValueSize = AsciiStrSize (FieldValue);
176 HttpHeader->FieldValue = AllocateZeroPool (FieldValueSize);
177 if (HttpHeader->FieldValue == NULL) {
178 FreePool (HttpHeader->FieldName);
179 return EFI_OUT_OF_RESOURCES;
180 }
181 CopyMem (HttpHeader->FieldValue, FieldValue, FieldValueSize);
182 HttpHeader->FieldValue[FieldValueSize - 1] = 0;
183
184 return EFI_SUCCESS;
185 }
186
187 /**
188 Get one key/value header pair from the raw string.
189
190 @param[in] String Pointer to the raw string.
191 @param[out] FieldName Pointer to header field's name.
192 @param[out] FieldValue Pointer to header field's value.
193
194 @return Pointer to the next raw string.
195 @return NULL if no key/value header pair from this raw string.
196
197 **/
198 CHAR8 *
199 GetFieldNameAndValue (
200 IN CHAR8 *String,
201 OUT CHAR8 **FieldName,
202 OUT CHAR8 **FieldValue
203 )
204 {
205 CHAR8 *FieldNameStr;
206 CHAR8 *FieldValueStr;
207 CHAR8 *StrPtr;
208
209 if (String == NULL || FieldName == NULL || FieldValue == NULL) {
210 return NULL;
211 }
212
213 *FieldName = NULL;
214 *FieldValue = NULL;
215 FieldNameStr = NULL;
216 FieldValueStr = NULL;
217 StrPtr = NULL;
218
219 //
220 // Each header field consists of a name followed by a colon (":") and the field value.
221 //
222 FieldNameStr = String;
223 FieldValueStr = AsciiStrGetNextToken (FieldNameStr, ':');
224 if (FieldValueStr == NULL) {
225 return NULL;
226 }
227
228 *(FieldValueStr - 1) = 0; /// Replace ':' with 0
229
230 //
231 // The field value MAY be preceded by any amount of LWS, though a single SP is preferred.
232 //
233 while (TRUE) {
234 if(*FieldValueStr == ' ' || *FieldValueStr == '\t') {
235 FieldValueStr ++;
236 } else if (*FieldValueStr == '\r' && *(FieldValueStr + 1) == '\n' &&
237 (*(FieldValueStr + 2) == ' ' || *(FieldValueStr + 2) == '\t')) {
238 FieldValueStr = FieldValueStr + 3;
239 } else {
240 break;
241 }
242 }
243
244 //
245 // Header fields can be extended over multiple lines by preceding each extra
246 // line with at least one SP or HT.
247 //
248 StrPtr = FieldValueStr;
249 do {
250 StrPtr = AsciiStrGetNextToken (StrPtr, '\r');
251 if (StrPtr == NULL || *StrPtr != '\n') {
252 return NULL;
253 }
254
255 StrPtr++;
256 } while (*StrPtr == ' ' || *StrPtr == '\t');
257
258 //
259 // Replace '\r' with 0.
260 //
261 *(StrPtr - 2) = 0;
262
263 //
264 // Get FieldName and FieldValue.
265 //
266 *FieldName = FieldNameStr;
267 *FieldValue = FieldValueStr;
268
269 return StrPtr;
270 }
271
272 /**
273 This function is used to manage the headers portion of an HTTP message by providing
274 the ability to add, remove, or replace HTTP headers.
275
276 @param[in] SeedMessageSize Size in bytes of the initial HTTP header. This can be zero.
277 @param[in] SeedMessage Initial raw unformatted HTTP header to be used as a base for
278 building a new unformatted HTTP header. If NULL, SeedMessageSize
279 is ignored. The buffer containing this message will be allocated
280 and released by the caller.
281 @param[in] DeleteCount Number of null-terminated HTTP header field names in DeleteList.
282 @param[in] DeleteList List of null-terminated HTTP header field names to remove from SeedMessage.
283 Only the field names are in this list because the field values are irrelevant
284 to this operation. If NULL, DeleteCount is ignored. The buffer containing the
285 list will be allocated and released by the caller.
286 @param[in] AppendCount Number of header fields in AppendList.
287 @param[in] AppendList List of HTTP headers to populate NewMessage with. If SeedMessage is not NULL,
288 AppendList will be appended to the existing list from SeedMessage in NewMessage.
289 @param[out] NewMessageSize Pointer to the size in bytes of the new unformatted HTTP header in NewMessage.
290 @param[out] NewMessage Pointer to a new unformatted HTTP header. The storage for this NewMessage is
291 allocated by the driver publishing this protocol, and must be freed by the caller.
292
293 @retval EFI_SUCCESS Add, remove, and replace operations succeeded.
294 @retval EFI_OUT_OF_RESOURCES Could not allocate memory for NewMessage.
295
296 **/
297 EFI_STATUS
298 HttpUtilitiesBuild(
299 IN UINTN SeedMessageSize,
300 IN VOID *SeedMessage, OPTIONAL
301 IN UINTN DeleteCount,
302 IN CHAR8 *DeleteList[], OPTIONAL
303 IN UINTN AppendCount,
304 IN EFI_HTTP_HEADER *AppendList[], OPTIONAL
305 OUT UINTN *NewMessageSize,
306 OUT VOID **NewMessage
307 )
308 {
309 EFI_STATUS Status;
310 EFI_HTTP_HEADER *SeedHeaderFields;
311 UINTN SeedFieldCount;
312 UINTN Index;
313 EFI_HTTP_HEADER *TempHeaderFields;
314 UINTN TempFieldCount;
315 EFI_HTTP_HEADER *NewHeaderFields;
316 UINTN NewFieldCount;
317 EFI_HTTP_HEADER *HttpHeader;
318 UINTN StrLength;
319 UINT8 *NewMessagePtr;
320
321 SeedHeaderFields = NULL;
322 SeedFieldCount = 0;
323 TempHeaderFields = NULL;
324 TempFieldCount = 0;
325 NewHeaderFields = NULL;
326 NewFieldCount = 0;
327
328 HttpHeader = NULL;
329 StrLength = 0;
330 NewMessagePtr = NULL;
331 *NewMessageSize = 0;
332 Status = EFI_SUCCESS;
333
334 if (SeedMessage != NULL) {
335 Status = HttpUtilitiesParse (
336 SeedMessage,
337 SeedMessageSize,
338 &SeedHeaderFields,
339 &SeedFieldCount
340 );
341 if (EFI_ERROR (Status)){
342 goto ON_EXIT;
343 }
344 }
345
346 //
347 // Handle DeleteList
348 //
349 if(SeedFieldCount != 0 && DeleteCount != 0) {
350 TempHeaderFields = AllocateZeroPool (SeedFieldCount * sizeof(EFI_HTTP_HEADER));
351 if (TempHeaderFields == NULL) {
352 Status = EFI_OUT_OF_RESOURCES;
353 goto ON_EXIT;
354 }
355
356 for (Index = 0, TempFieldCount = 0; Index < SeedFieldCount; Index++) {
357 //
358 // Check whether each SeedHeaderFields member is in DeleteList
359 //
360 if (IsValidHttpHeader(DeleteList, DeleteCount, SeedHeaderFields[Index].FieldName)) {
361 Status = SetFieldNameAndValue(
362 &TempHeaderFields[TempFieldCount],
363 SeedHeaderFields[Index].FieldName,
364 SeedHeaderFields[Index].FieldValue
365 );
366 if (EFI_ERROR (Status)){
367 goto ON_EXIT;
368 }
369 TempFieldCount++;
370 }
371 }
372 } else {
373 TempHeaderFields = SeedHeaderFields;
374 TempFieldCount = SeedFieldCount;
375 }
376
377 //
378 // Handle AppendList
379 //
380 NewHeaderFields = AllocateZeroPool ((TempFieldCount + AppendCount) * sizeof(EFI_HTTP_HEADER));
381 if (NewHeaderFields == NULL) {
382 Status = EFI_OUT_OF_RESOURCES;
383 goto ON_EXIT;
384 }
385
386 for (Index = 0; Index < TempFieldCount; Index++) {
387 Status = SetFieldNameAndValue(
388 &NewHeaderFields[Index],
389 TempHeaderFields[Index].FieldName,
390 TempHeaderFields[Index].FieldValue
391 );
392 if (EFI_ERROR (Status)){
393 goto ON_EXIT;
394 }
395 }
396
397 NewFieldCount = TempFieldCount;
398
399 for (Index = 0; Index < AppendCount; Index++) {
400 HttpHeader = FindHttpHeader(NewHeaderFields, NewFieldCount, AppendList[Index]->FieldName);
401 if(HttpHeader != NULL) {
402 Status = SetFieldNameAndValue(
403 HttpHeader,
404 AppendList[Index]->FieldName,
405 AppendList[Index]->FieldValue
406 );
407 if (EFI_ERROR (Status)){
408 goto ON_EXIT;
409 }
410 } else {
411 Status = SetFieldNameAndValue
412 (&NewHeaderFields[NewFieldCount],
413 AppendList[Index]->FieldName,
414 AppendList[Index]->FieldValue
415 );
416 if (EFI_ERROR (Status)){
417 goto ON_EXIT;
418 }
419 NewFieldCount++;
420 }
421 }
422
423 //
424 // Calculate NewMessageSize, then build NewMessage
425 //
426 for (Index = 0; Index < NewFieldCount; Index++) {
427 HttpHeader = &NewHeaderFields[Index];
428
429 StrLength = AsciiStrLen (HttpHeader->FieldName);
430 *NewMessageSize += StrLength;
431
432 StrLength = sizeof(": ") - 1;
433 *NewMessageSize += StrLength;
434
435 StrLength = AsciiStrLen (HttpHeader->FieldValue);
436 *NewMessageSize += StrLength;
437
438 StrLength = sizeof(HTTP_CRLF_STR) - 1;
439 *NewMessageSize += StrLength;
440 }
441 StrLength = sizeof(HTTP_CRLF_STR) - 1;
442 *NewMessageSize += StrLength;
443 //
444 // Final 0 for end flag.
445 //
446 *NewMessageSize += 1;
447
448 *NewMessage = AllocateZeroPool (*NewMessageSize);
449 if (*NewMessage == NULL) {
450 Status = EFI_OUT_OF_RESOURCES;
451 goto ON_EXIT;
452 }
453
454 NewMessagePtr = (UINT8 *)(*NewMessage);
455
456 for (Index = 0; Index < NewFieldCount; Index++) {
457 HttpHeader = &NewHeaderFields[Index];
458
459 StrLength = AsciiStrLen (HttpHeader->FieldName);
460 CopyMem (NewMessagePtr, HttpHeader->FieldName, StrLength);
461 NewMessagePtr += StrLength;
462
463 StrLength = sizeof(": ") - 1;
464 CopyMem (NewMessagePtr, ": ", StrLength);
465 NewMessagePtr += StrLength;
466
467 StrLength = AsciiStrLen (HttpHeader->FieldValue);
468 CopyMem (NewMessagePtr, HttpHeader->FieldValue, StrLength);
469 NewMessagePtr += StrLength;
470
471 StrLength = sizeof(HTTP_CRLF_STR) - 1;
472 CopyMem (NewMessagePtr, HTTP_CRLF_STR, StrLength);
473 NewMessagePtr += StrLength;
474 }
475 StrLength = sizeof(HTTP_CRLF_STR) - 1;
476 CopyMem (NewMessagePtr, HTTP_CRLF_STR, StrLength);
477 NewMessagePtr += StrLength;
478
479 *NewMessagePtr = 0;
480
481 ASSERT (*NewMessageSize == (UINTN) NewMessagePtr - (UINTN) (*NewMessage) + 1);
482
483 //
484 // Free allocated buffer
485 //
486 ON_EXIT:
487 if(SeedHeaderFields != NULL) {
488 FreeHeaderFields(SeedHeaderFields, SeedFieldCount);
489 }
490
491 if(TempHeaderFields != NULL) {
492 FreeHeaderFields(TempHeaderFields, TempFieldCount);
493 }
494
495 if(NewHeaderFields != NULL) {
496 FreeHeaderFields(NewHeaderFields, NewFieldCount);
497 }
498
499 return Status;
500 }
501
502 /**
503 This function is used to transform data stored in HttpMessage into a list of fields
504 paired with their corresponding values.
505
506 @param[in] HttpMessage Contains raw unformatted HTTP header string. The buffer for this string will
507 be allocated and released by the caller.
508 @param[in] HttpMessageSize Size in bytes of raw unformatted HTTP header.
509 @param[out] HeaderFields Array of key/value header pairs. The storage for all header pairs is allocated
510 by the driver publishing this protocol, and must be freed by the caller.
511 @param[out] FieldCount Number of headers in HeaderFields.
512
513 @retval EFI_SUCCESS Parse HTTP header into array of key/value pairs succeeded.
514 @retval EFI_OUT_OF_RESOURCES Could not allocate memory for NewMessage.
515 @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
516 HttpMessage is NULL.
517 HeaderFields is NULL.
518 FieldCount is NULL.
519
520 **/
521 EFI_STATUS
522 HttpUtilitiesParse(
523 IN CHAR8 *HttpMessage,
524 IN UINTN HttpMessageSize,
525 OUT EFI_HTTP_HEADER **HeaderFields,
526 OUT UINTN *FieldCount
527 )
528 {
529 EFI_STATUS Status;
530 CHAR8 *TempHttpMessage;
531 CHAR8 *Token;
532 CHAR8 *NextToken;
533 CHAR8 *FieldName;
534 CHAR8 *FieldValue;
535 UINTN Index;
536
537 if (HttpMessage == NULL || HeaderFields == NULL || FieldCount == NULL) {
538 return EFI_INVALID_PARAMETER;
539 }
540
541 Status = EFI_SUCCESS;
542 TempHttpMessage = NULL;
543 *FieldCount = 0;
544 Token = NULL;
545 NextToken = NULL;
546 FieldName = NULL;
547 FieldValue = NULL;
548 Index = 0;
549
550 TempHttpMessage = AllocateZeroPool (HttpMessageSize);
551 if (TempHttpMessage == NULL) {
552 return EFI_OUT_OF_RESOURCES;
553 }
554
555 CopyMem (TempHttpMessage, HttpMessage, HttpMessageSize);
556
557 //
558 // Get header number
559 //
560 Token = TempHttpMessage;
561 while (TRUE) {
562 FieldName = NULL;
563 FieldValue = NULL;
564 NextToken = GetFieldNameAndValue (Token, &FieldName, &FieldValue);
565 Token = NextToken;
566 if (FieldName == NULL || FieldValue == NULL) {
567 break;
568 }
569
570 (*FieldCount)++;
571 }
572
573 if(*FieldCount == 0) {
574 Status = EFI_INVALID_PARAMETER;
575 goto ON_EXIT;
576 }
577
578 //
579 // Allocate buffer for header
580 //
581 *HeaderFields = AllocateZeroPool ((*FieldCount) * sizeof(EFI_HTTP_HEADER));
582 if (*HeaderFields == NULL) {
583 *FieldCount = 0;
584 Status = EFI_OUT_OF_RESOURCES;
585 goto ON_EXIT;
586 }
587
588 CopyMem (TempHttpMessage, HttpMessage, HttpMessageSize);
589
590 //
591 // Set Field and Value to each header
592 //
593 Token = TempHttpMessage;
594 while (Index < *FieldCount) {
595 FieldName = NULL;
596 FieldValue = NULL;
597 NextToken = GetFieldNameAndValue (Token, &FieldName, &FieldValue);
598 Token = NextToken;
599 if (FieldName == NULL || FieldValue == NULL) {
600 break;
601 }
602
603 Status = SetFieldNameAndValue(&(*HeaderFields)[Index], FieldName, FieldValue);
604 if(EFI_ERROR(Status)){
605 *FieldCount = 0;
606 FreeHeaderFields (*HeaderFields, Index);
607 goto ON_EXIT;
608 }
609
610 Index++;
611 }
612
613 //
614 // Free allocated buffer
615 //
616 ON_EXIT:
617 if (TempHttpMessage != NULL) {
618 FreePool(TempHttpMessage);
619 }
620
621 return Status;
622 }