]> git.proxmox.com Git - mirror_edk2.git/blob - NetworkPkg/HttpDxe/HttpProto.c
NetworkPkg: Avoid memory allocation for each HTTP message exchange.
[mirror_edk2.git] / NetworkPkg / HttpDxe / HttpProto.c
1 /** @file
2 Miscellaneous routines for HttpDxe driver.
3
4 Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13 **/
14
15 #include "HttpDriver.h"
16
17 /**
18 The common notify function used in HTTP driver.
19
20 @param[in] Event The event signaled.
21 @param[in] Context The context.
22
23 **/
24 VOID
25 EFIAPI
26 HttpCommonNotify (
27 IN EFI_EVENT Event,
28 IN VOID *Context
29 )
30 {
31 if ((Event == NULL) || (Context == NULL)) {
32 return ;
33 }
34
35 *((BOOLEAN *) Context) = TRUE;
36 }
37
38 /**
39 The notify function associated with TxToken for Tcp4->Transmit().
40
41 @param[in] Event The event signaled.
42 @param[in] Context The context.
43
44 **/
45 VOID
46 EFIAPI
47 HttpTcpTransmitNotify (
48 IN EFI_EVENT Event,
49 IN VOID *Context
50 )
51 {
52 HTTP_TOKEN_WRAP *Wrap;
53
54 if ((Event == NULL) || (Context == NULL)) {
55 return ;
56 }
57
58 Wrap = (HTTP_TOKEN_WRAP *) Context;
59 Wrap->HttpToken->Status = Wrap->TcpWrap.TxToken.CompletionToken.Status;
60 gBS->SignalEvent (Wrap->HttpToken->Event);
61
62 //
63 // Free resources.
64 //
65 if (Wrap->TcpWrap.TxToken.Packet.TxData->FragmentTable[0].FragmentBuffer != NULL) {
66 FreePool (Wrap->TcpWrap.TxToken.Packet.TxData->FragmentTable[0].FragmentBuffer);
67 }
68
69 if (Wrap->TcpWrap.TxToken.CompletionToken.Event != NULL) {
70 gBS->CloseEvent (Wrap->TcpWrap.TxToken.CompletionToken.Event);
71 }
72
73 Wrap->TcpWrap.IsTxDone = TRUE;
74
75 //
76 // Check pending TxTokens and sent out.
77 //
78 NetMapIterate (&Wrap->HttpInstance->TxTokens, HttpTcpTransmit, NULL);
79
80 }
81
82 /**
83 The notify function associated with RxToken for Tcp4->Receive ().
84
85 @param[in] Event The event signaled.
86 @param[in] Context The context.
87
88 **/
89 VOID
90 EFIAPI
91 HttpTcpReceiveNotify (
92 IN EFI_EVENT Event,
93 IN VOID *Context
94 )
95 {
96 HTTP_TOKEN_WRAP *Wrap;
97 NET_MAP_ITEM *Item;
98 UINTN Length;
99 EFI_STATUS Status;
100 HTTP_PROTOCOL *HttpInstance;
101
102 if ((Event == NULL) || (Context == NULL)) {
103 return ;
104 }
105
106 Wrap = (HTTP_TOKEN_WRAP *) Context;
107 if (EFI_ERROR (Wrap->TcpWrap.RxToken.CompletionToken.Status)) {
108 return ;
109 }
110
111 HttpInstance = Wrap->HttpInstance;
112
113 //
114 // Check whether we receive a complete HTTP message.
115 //
116 ASSERT (HttpInstance->MsgParser != NULL);
117
118 Length = (UINTN) Wrap->TcpWrap.RxData.FragmentTable[0].FragmentLength;
119 Status = HttpParseMessageBody (
120 HttpInstance->MsgParser,
121 Length,
122 Wrap->HttpToken->Message->Body
123 );
124 if (EFI_ERROR (Status)) {
125 return ;
126 }
127
128 if (HttpIsMessageComplete (HttpInstance->MsgParser)) {
129 //
130 // Free the MsgParse since we already have a full HTTP message.
131 //
132 HttpFreeMsgParser (HttpInstance->MsgParser);
133 HttpInstance->MsgParser = NULL;
134 }
135
136 Wrap->HttpToken->Message->BodyLength = Length;
137 ASSERT (HttpInstance->CacheBody == NULL);
138 //
139 // We receive part of header of next HTTP msg.
140 //
141 if (HttpInstance->NextMsg != NULL) {
142 Wrap->HttpToken->Message->BodyLength = HttpInstance->NextMsg -
143 (CHAR8 *) Wrap->HttpToken->Message->Body;
144 HttpInstance->CacheLen = Length - Wrap->HttpToken->Message->BodyLength;
145 if (HttpInstance->CacheLen != 0) {
146 HttpInstance->CacheBody = AllocateZeroPool (HttpInstance->CacheLen);
147 if (HttpInstance->CacheBody == NULL) {
148 return ;
149 }
150 CopyMem (HttpInstance->CacheBody, HttpInstance->NextMsg, HttpInstance->CacheLen);
151 HttpInstance->NextMsg = HttpInstance->CacheBody;
152 HttpInstance->CacheOffset = 0;
153 }
154 }
155
156 Item = NetMapFindKey (&Wrap->HttpInstance->RxTokens, Wrap->HttpToken);
157 if (Item != NULL) {
158 NetMapRemoveItem (&Wrap->HttpInstance->RxTokens, Item, NULL);
159 }
160
161
162 Wrap->TcpWrap.IsRxDone = TRUE;
163 Wrap->HttpToken->Status = Wrap->TcpWrap.RxToken.CompletionToken.Status;
164
165 gBS->SignalEvent (Wrap->HttpToken->Event);
166
167 //
168 // Check pending RxTokens and receive the HTTP message.
169 //
170 NetMapIterate (&Wrap->HttpInstance->RxTokens, HttpTcpReceive, NULL);
171
172 FreePool (Wrap);
173 }
174
175 /**
176 Create events for the TCP4 connection token and TCP4 close token.
177
178 @param[in] HttpInstance Pointer to HTTP_PROTOCOL structure.
179
180 @retval EFI_SUCCESS The events are created successfully.
181 @retval others Other error as indicated.
182
183 **/
184 EFI_STATUS
185 HttpCreateTcp4ConnCloseEvent (
186 IN HTTP_PROTOCOL *HttpInstance
187 )
188 {
189 EFI_STATUS Status;
190 //
191 // Create events for variuos asynchronous operations.
192 //
193 Status = gBS->CreateEvent (
194 EVT_NOTIFY_SIGNAL,
195 TPL_NOTIFY,
196 HttpCommonNotify,
197 &HttpInstance->IsConnDone,
198 &HttpInstance->ConnToken.CompletionToken.Event
199 );
200 if (EFI_ERROR (Status)) {
201 goto ERROR;
202 }
203
204 //
205 // Initialize CloseToken
206 //
207 Status = gBS->CreateEvent (
208 EVT_NOTIFY_SIGNAL,
209 TPL_NOTIFY,
210 HttpCommonNotify,
211 &HttpInstance->IsCloseDone,
212 &HttpInstance->CloseToken.CompletionToken.Event
213 );
214 if (EFI_ERROR (Status)) {
215 goto ERROR;
216 }
217
218
219 return EFI_SUCCESS;
220
221 ERROR:
222 //
223 // Error handling
224 //
225 HttpCloseTcp4ConnCloseEvent (HttpInstance);
226
227 return Status;
228 }
229
230
231 /**
232 Close events in the TCP4 connection token and TCP4 close token.
233
234 @param[in] HttpInstance Pointer to HTTP_PROTOCOL structure.
235
236 **/
237 VOID
238 HttpCloseTcp4ConnCloseEvent (
239 IN HTTP_PROTOCOL *HttpInstance
240 )
241 {
242 ASSERT (HttpInstance != NULL);
243
244 if (NULL != HttpInstance->ConnToken.CompletionToken.Event) {
245 gBS->CloseEvent (HttpInstance->ConnToken.CompletionToken.Event);
246 HttpInstance->ConnToken.CompletionToken.Event = NULL;
247 }
248
249 if (NULL != HttpInstance->CloseToken.CompletionToken.Event) {
250 gBS->CloseEvent(HttpInstance->CloseToken.CompletionToken.Event);
251 HttpInstance->CloseToken.CompletionToken.Event = NULL;
252 }
253 }
254
255 /**
256 Create event for the TCP4 transmit token.
257
258 @param[in] Wrap Point to HTTP token's wrap data.
259
260 @retval EFI_SUCCESS The events is created successfully.
261 @retval others Other error as indicated.
262
263 **/
264 EFI_STATUS
265 HttpCreateTcp4TxEvent (
266 IN HTTP_TOKEN_WRAP *Wrap
267 )
268 {
269 EFI_STATUS Status;
270 HTTP_PROTOCOL *HttpInstance;
271 HTTP_TCP_TOKEN_WRAP *TcpWrap;
272
273 HttpInstance = Wrap->HttpInstance;
274 TcpWrap = &Wrap->TcpWrap;
275
276 Status = gBS->CreateEvent (
277 EVT_NOTIFY_SIGNAL,
278 TPL_NOTIFY,
279 HttpTcpTransmitNotify,
280 Wrap,
281 &TcpWrap->TxToken.CompletionToken.Event
282 );
283 if (EFI_ERROR (Status)) {
284 return Status;
285 }
286
287 TcpWrap->TxData.Push = TRUE;
288 TcpWrap->TxData.Urgent = FALSE;
289 TcpWrap->TxData.FragmentCount = 1;
290 TcpWrap->TxToken.Packet.TxData = &Wrap->TcpWrap.TxData;
291 TcpWrap->TxToken.CompletionToken.Status = EFI_NOT_READY;
292
293 return EFI_SUCCESS;
294 }
295
296 /**
297 Create event for the TCP4 receive token which is used to receive HTTP header.
298
299 @param[in] HttpInstance Pointer to HTTP_PROTOCOL structure.
300
301 @retval EFI_SUCCESS The events is created successfully.
302 @retval others Other error as indicated.
303
304 **/
305 EFI_STATUS
306 HttpCreateTcp4RxEventForHeader (
307 IN HTTP_PROTOCOL *HttpInstance
308 )
309 {
310 EFI_STATUS Status;
311
312
313 Status = gBS->CreateEvent (
314 EVT_NOTIFY_SIGNAL,
315 TPL_NOTIFY,
316 HttpCommonNotify,
317 &HttpInstance->IsRxDone,
318 &HttpInstance->RxToken.CompletionToken.Event
319 );
320 if (EFI_ERROR (Status)) {
321 return Status;
322 }
323
324 HttpInstance->RxData.FragmentCount = 1;
325 HttpInstance->RxToken.Packet.RxData = &HttpInstance->RxData;
326 HttpInstance->RxToken.CompletionToken.Status = EFI_NOT_READY;
327
328 return EFI_SUCCESS;
329 }
330
331 /**
332 Create event for the TCP4 receive token which is used to receive HTTP body.
333
334 @param[in] Wrap Point to HTTP token's wrap data.
335
336 @retval EFI_SUCCESS The events is created successfully.
337 @retval others Other error as indicated.
338
339 **/
340 EFI_STATUS
341 HttpCreateTcp4RxEvent (
342 IN HTTP_TOKEN_WRAP *Wrap
343 )
344 {
345 EFI_STATUS Status;
346 HTTP_PROTOCOL *HttpInstance;
347 HTTP_TCP_TOKEN_WRAP *TcpWrap;
348
349 HttpInstance = Wrap->HttpInstance;
350 TcpWrap = &Wrap->TcpWrap;
351
352 Status = gBS->CreateEvent (
353 EVT_NOTIFY_SIGNAL,
354 TPL_NOTIFY,
355 HttpTcpReceiveNotify,
356 Wrap,
357 &TcpWrap->RxToken.CompletionToken.Event
358 );
359 if (EFI_ERROR (Status)) {
360 return Status;
361 }
362
363 TcpWrap->RxData.FragmentCount = 1;
364 TcpWrap->RxToken.Packet.RxData = &Wrap->TcpWrap.RxData;
365 TcpWrap->RxToken.CompletionToken.Status = EFI_NOT_READY;
366
367 return EFI_SUCCESS;
368 }
369
370 /**
371 Intiialize the HTTP_PROTOCOL structure to the unconfigured state.
372
373 @param[in] HttpSb The HTTP service private instance.
374 @param[in, out] HttpInstance Pointer to HTTP_PROTOCOL structure.
375
376 @retval EFI_SUCCESS HTTP_PROTOCOL structure is initialized successfully.
377 @retval Others Other error as indicated.
378
379 **/
380 EFI_STATUS
381 HttpInitProtocol (
382 IN HTTP_SERVICE *HttpSb,
383 IN OUT HTTP_PROTOCOL *HttpInstance
384 )
385 {
386 EFI_STATUS Status;
387 VOID *Interface;
388
389 ASSERT ((HttpSb != NULL) && (HttpInstance != NULL));
390
391 HttpInstance->Signature = HTTP_PROTOCOL_SIGNATURE;
392 CopyMem (&HttpInstance->Http, &mEfiHttpTemplate, sizeof (HttpInstance->Http));
393 HttpInstance->Service = HttpSb;
394
395 //
396 // Create TCP child.
397 //
398 Status = NetLibCreateServiceChild (
399 HttpInstance->Service->ControllerHandle,
400 HttpInstance->Service->ImageHandle,
401 &gEfiTcp4ServiceBindingProtocolGuid,
402 &HttpInstance->TcpChildHandle
403 );
404
405 if (EFI_ERROR (Status)) {
406 goto ON_ERROR;
407 }
408
409 Status = gBS->OpenProtocol (
410 HttpInstance->TcpChildHandle,
411 &gEfiTcp4ProtocolGuid,
412 (VOID **) &Interface,
413 HttpInstance->Service->ImageHandle,
414 HttpInstance->Service->ControllerHandle,
415 EFI_OPEN_PROTOCOL_BY_DRIVER
416 );
417
418 if (EFI_ERROR (Status)) {
419 goto ON_ERROR;
420 }
421
422 Status = gBS->OpenProtocol (
423 HttpInstance->TcpChildHandle,
424 &gEfiTcp4ProtocolGuid,
425 (VOID **) &HttpInstance->Tcp4,
426 HttpInstance->Service->ImageHandle,
427 HttpInstance->Handle,
428 EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
429 );
430 if (EFI_ERROR(Status)) {
431 goto ON_ERROR;
432 }
433
434 HttpInstance->Url = AllocateZeroPool (HTTP_URL_BUFFER_LEN);
435 if (HttpInstance->Url == NULL) {
436 Status = EFI_OUT_OF_RESOURCES;
437 goto ON_ERROR;
438 }
439
440 NetMapInit (&HttpInstance->TxTokens);
441 NetMapInit (&HttpInstance->RxTokens);
442
443 return EFI_SUCCESS;
444
445 ON_ERROR:
446
447 if (HttpInstance->TcpChildHandle != NULL) {
448 gBS->CloseProtocol (
449 HttpInstance->TcpChildHandle,
450 &gEfiTcp4ProtocolGuid,
451 HttpInstance->Service->ImageHandle,
452 HttpInstance->Service->ControllerHandle
453 );
454
455 gBS->CloseProtocol (
456 HttpInstance->TcpChildHandle,
457 &gEfiTcp4ProtocolGuid,
458 HttpInstance->Service->ImageHandle,
459 HttpInstance->Handle
460 );
461
462 NetLibDestroyServiceChild (
463 HttpInstance->Service->ControllerHandle,
464 HttpInstance->Service->ImageHandle,
465 &gEfiTcp4ServiceBindingProtocolGuid,
466 HttpInstance->TcpChildHandle
467 );
468 }
469
470 return Status;
471
472 }
473
474 /**
475 Clean up the HTTP child, release all the resources used by it.
476
477 @param[in] HttpInstance The HTTP child to clean up.
478
479 **/
480 VOID
481 HttpCleanProtocol (
482 IN HTTP_PROTOCOL *HttpInstance
483 )
484 {
485 HttpCloseConnection (HttpInstance);
486
487 HttpCloseTcp4ConnCloseEvent (HttpInstance);
488
489 if (HttpInstance->CacheBody != NULL) {
490 FreePool (HttpInstance->CacheBody);
491 HttpInstance->CacheBody = NULL;
492 HttpInstance->NextMsg = NULL;
493 }
494
495 if (HttpInstance->RemoteHost != NULL) {
496 FreePool (HttpInstance->RemoteHost);
497 HttpInstance->RemoteHost = NULL;
498 }
499
500 if (HttpInstance->MsgParser != NULL) {
501 HttpFreeMsgParser (HttpInstance->MsgParser);
502 HttpInstance->MsgParser = NULL;
503 }
504
505 if (HttpInstance->Url != NULL) {
506 FreePool (HttpInstance->Url);
507 HttpInstance->Url = NULL;
508 }
509
510 NetMapClean (&HttpInstance->TxTokens);
511 NetMapClean (&HttpInstance->RxTokens);
512
513 if (HttpInstance->TcpChildHandle != NULL) {
514 gBS->CloseProtocol (
515 HttpInstance->TcpChildHandle,
516 &gEfiTcp4ProtocolGuid,
517 HttpInstance->Service->ImageHandle,
518 HttpInstance->Service->ControllerHandle
519 );
520
521 gBS->CloseProtocol (
522 HttpInstance->TcpChildHandle,
523 &gEfiTcp4ProtocolGuid,
524 HttpInstance->Service->ImageHandle,
525 HttpInstance->Handle
526 );
527
528 NetLibDestroyServiceChild (
529 HttpInstance->Service->ControllerHandle,
530 HttpInstance->Service->ImageHandle,
531 &gEfiTcp4ServiceBindingProtocolGuid,
532 HttpInstance->TcpChildHandle
533 );
534 }
535 }
536
537 /**
538 Establish TCP connection with HTTP server.
539
540 @param[in] HttpInstance The HTTP instance private data.
541
542 @retval EFI_SUCCESS The TCP connection is established.
543 @retval Others Other error as indicated.
544
545 **/
546 EFI_STATUS
547 HttpCreateConnection (
548 IN HTTP_PROTOCOL *HttpInstance
549 )
550 {
551 EFI_STATUS Status;
552
553 //
554 // Create events for variuos asynchronous operations.
555 //
556 HttpInstance->IsConnDone = FALSE;
557
558 //
559 // Connect to Http server
560 //
561 HttpInstance->ConnToken.CompletionToken.Status = EFI_NOT_READY;
562 Status = HttpInstance->Tcp4->Connect (HttpInstance->Tcp4, &HttpInstance->ConnToken);
563 if (EFI_ERROR (Status)) {
564 DEBUG ((EFI_D_ERROR, "HttpCreateConnection: Tcp4->Connect() = %r\n", Status));
565 return Status;
566 }
567
568 while (!HttpInstance->IsConnDone) {
569 HttpInstance->Tcp4->Poll (HttpInstance->Tcp4);
570 }
571
572 Status = HttpInstance->ConnToken.CompletionToken.Status;
573
574 if (!EFI_ERROR (Status)) {
575 HttpInstance->State = HTTP_STATE_TCP_CONNECTED;
576 }
577
578 return Status;
579 }
580
581 /**
582 Close existing TCP connection.
583
584 @param[in] HttpInstance The HTTP instance private data.
585
586 @retval EFI_SUCCESS The TCP connection is closed.
587 @retval Others Other error as indicated.
588
589 **/
590 EFI_STATUS
591 HttpCloseConnection (
592 IN HTTP_PROTOCOL *HttpInstance
593 )
594 {
595 EFI_STATUS Status;
596
597 if (HttpInstance->State == HTTP_STATE_TCP_CONNECTED) {
598 HttpInstance->CloseToken.AbortOnClose = TRUE;
599 HttpInstance->IsCloseDone = FALSE;
600
601 Status = HttpInstance->Tcp4->Close (HttpInstance->Tcp4, &HttpInstance->CloseToken);
602 if (EFI_ERROR (Status)) {
603 return Status;
604 }
605
606 while (!HttpInstance->IsCloseDone) {
607 HttpInstance->Tcp4->Poll (HttpInstance->Tcp4);
608 }
609 }
610
611 HttpInstance->State = HTTP_STATE_TCP_CLOSED;
612 return EFI_SUCCESS;
613 }
614
615 /**
616 Configure TCP4 protocol child.
617
618 @param[in] HttpInstance The HTTP instance private data.
619 @param[in] Wrap The HTTP token's wrap data.
620
621 @retval EFI_SUCCESS The TCP4 protocol child is configured.
622 @retval Others Other error as indicated.
623
624 **/
625 EFI_STATUS
626 HttpConfigureTcp4 (
627 IN HTTP_PROTOCOL *HttpInstance,
628 IN HTTP_TOKEN_WRAP *Wrap
629 )
630 {
631 EFI_STATUS Status;
632 EFI_TCP4_CONFIG_DATA *Tcp4CfgData;
633 EFI_TCP4_ACCESS_POINT *Tcp4AP;
634 EFI_TCP4_OPTION *Tcp4Option;
635 HTTP_TCP_TOKEN_WRAP *TcpWrap;
636
637 ASSERT (HttpInstance != NULL);
638 TcpWrap = &Wrap->TcpWrap;
639
640
641 Tcp4CfgData = &HttpInstance->Tcp4CfgData;
642 ZeroMem (Tcp4CfgData, sizeof (EFI_TCP4_CONFIG_DATA));
643
644 Tcp4CfgData->TypeOfService = HTTP_TOS_DEAULT;
645 Tcp4CfgData->TimeToLive = HTTP_TTL_DEAULT;
646 Tcp4CfgData->ControlOption = &HttpInstance->Tcp4Option;
647
648 Tcp4AP = &Tcp4CfgData->AccessPoint;
649 Tcp4AP->UseDefaultAddress = HttpInstance->IPv4Node.UseDefaultAddress;
650 if (!Tcp4AP->UseDefaultAddress) {
651 IP4_COPY_ADDRESS (&Tcp4AP->StationAddress, &HttpInstance->IPv4Node.LocalAddress);
652 IP4_COPY_ADDRESS (&Tcp4AP->SubnetMask, &HttpInstance->IPv4Node.LocalSubnet);
653 }
654
655 Tcp4AP->StationPort = HttpInstance->IPv4Node.LocalPort;
656 Tcp4AP->RemotePort = HttpInstance->RemotePort;
657 Tcp4AP->ActiveFlag = TRUE;
658 IP4_COPY_ADDRESS (&Tcp4AP->RemoteAddress, &HttpInstance->RemoteAddr);
659
660 Tcp4Option = Tcp4CfgData->ControlOption;
661 Tcp4Option->ReceiveBufferSize = HTTP_BUFFER_SIZE_DEAULT;
662 Tcp4Option->SendBufferSize = HTTP_BUFFER_SIZE_DEAULT;
663 Tcp4Option->MaxSynBackLog = HTTP_MAX_SYN_BACK_LOG;
664 Tcp4Option->ConnectionTimeout = HTTP_CONNECTION_TIMEOUT;
665 Tcp4Option->DataRetries = HTTP_DATA_RETRIES;
666 Tcp4Option->FinTimeout = HTTP_FIN_TIMEOUT;
667 Tcp4Option->KeepAliveProbes = HTTP_KEEP_ALIVE_PROBES;
668 Tcp4Option->KeepAliveTime = HTTP_KEEP_ALIVE_TIME;
669 Tcp4Option->KeepAliveInterval = HTTP_KEEP_ALIVE_INTERVAL;
670 Tcp4Option->EnableNagle = TRUE;
671 Tcp4CfgData->ControlOption = Tcp4Option;
672
673 Status = HttpInstance->Tcp4->Configure (HttpInstance->Tcp4, Tcp4CfgData);
674 if (EFI_ERROR (Status)) {
675 DEBUG ((EFI_D_ERROR, "HttpConfigureTcp4 - %r\n", Status));
676 return Status;
677 }
678
679 Status = HttpCreateTcp4ConnCloseEvent (HttpInstance);
680 if (EFI_ERROR (Status)) {
681 return Status;
682 }
683
684 Status = HttpCreateTcp4TxEvent (Wrap);
685 if (EFI_ERROR (Status)) {
686 return Status;
687 }
688
689 HttpInstance->State = HTTP_STATE_TCP_CONFIGED;
690
691 return EFI_SUCCESS;
692 }
693
694 /**
695 Check existing TCP connection, if in error state, receover TCP4 connection.
696
697 @param[in] HttpInstance The HTTP instance private data.
698
699 @retval EFI_SUCCESS The TCP connection is established.
700 @retval EFI_NOT_READY TCP4 protocol child is not created or configured.
701 @retval Others Other error as indicated.
702
703 **/
704 EFI_STATUS
705 HttpConnectTcp4 (
706 IN HTTP_PROTOCOL *HttpInstance
707 )
708 {
709 EFI_STATUS Status;
710 EFI_TCP4_CONNECTION_STATE Tcp4State;
711
712
713 if (HttpInstance->State != HTTP_STATE_TCP_CONFIGED || HttpInstance->Tcp4 == NULL) {
714 return EFI_NOT_READY;
715 }
716
717 Status = HttpInstance->Tcp4->GetModeData(
718 HttpInstance->Tcp4,
719 &Tcp4State,
720 NULL,
721 NULL,
722 NULL,
723 NULL
724 );
725 if (EFI_ERROR(Status)){
726 DEBUG ((EFI_D_ERROR, "Tcp4 GetModeData fail - %x\n", Status));
727 return Status;
728 }
729
730 if (Tcp4State > Tcp4StateEstablished) {
731 HttpCloseConnection(HttpInstance);
732 }
733
734 return HttpCreateConnection (HttpInstance);
735 }
736
737 /**
738 Send the HTTP message through TCP4.
739
740 @param[in] HttpInstance The HTTP instance private data.
741 @param[in] Wrap The HTTP token's wrap data.
742 @param[in] TxString Buffer containing the HTTP message string.
743 @param[in] TxStringLen Length of the HTTP message string in bytes.
744
745 @retval EFI_SUCCESS The HTTP message is queued into TCP transmit queue.
746 @retval Others Other error as indicated.
747
748 **/
749 EFI_STATUS
750 HttpTransmitTcp4 (
751 IN HTTP_PROTOCOL *HttpInstance,
752 IN HTTP_TOKEN_WRAP *Wrap,
753 IN UINT8 *TxString,
754 IN UINTN TxStringLen
755 )
756 {
757 EFI_STATUS Status;
758 EFI_TCP4_IO_TOKEN *TxToken;
759 EFI_TCP4_PROTOCOL *Tcp4;
760
761 Tcp4 = HttpInstance->Tcp4;
762 TxToken = &Wrap->TcpWrap.TxToken;
763
764 TxToken->Packet.TxData->DataLength = (UINT32) TxStringLen;
765 TxToken->Packet.TxData->FragmentTable[0].FragmentLength = (UINT32) TxStringLen;
766 TxToken->Packet.TxData->FragmentTable[0].FragmentBuffer = (VOID *) TxString;
767 TxToken->CompletionToken.Status = EFI_NOT_READY;
768
769 Wrap->TcpWrap.IsTxDone = FALSE;
770 Status = Tcp4->Transmit (Tcp4, TxToken);
771 if (EFI_ERROR (Status)) {
772 DEBUG ((EFI_D_ERROR, "Transmit failed: %r\n", Status));
773 return Status;
774 }
775
776 return Status;
777 }
778
779 /**
780 Translate the status code in HTTP message to EFI_HTTP_STATUS_CODE defined
781 in UEFI 2.5 specification.
782
783 @param[in] StatusCode The status code value in HTTP message.
784
785 @return Value defined in EFI_HTTP_STATUS_CODE .
786
787 **/
788 EFI_HTTP_STATUS_CODE
789 HttpMappingToStatusCode (
790 IN UINTN StatusCode
791 )
792 {
793 switch (StatusCode) {
794 case 100:
795 return HTTP_STATUS_100_CONTINUE;
796 case 101:
797 return HTTP_STATUS_101_SWITCHING_PROTOCOLS;
798 case 200:
799 return HTTP_STATUS_200_OK;
800 case 201:
801 return HTTP_STATUS_201_CREATED;
802 case 202:
803 return HTTP_STATUS_202_ACCEPTED;
804 case 203:
805 return HTTP_STATUS_203_NON_AUTHORITATIVE_INFORMATION;
806 case 204:
807 return HTTP_STATUS_204_NO_CONTENT;
808 case 205:
809 return HTTP_STATUS_205_RESET_CONTENT;
810 case 206:
811 return HTTP_STATUS_206_PARTIAL_CONTENT;
812 case 300:
813 return HTTP_STATUS_300_MULTIPLE_CHIOCES;
814 case 301:
815 return HTTP_STATUS_301_MOVED_PERMANENTLY;
816 case 302:
817 return HTTP_STATUS_302_FOUND;
818 case 303:
819 return HTTP_STATUS_303_SEE_OTHER;
820 case 304:
821 return HTTP_STATUS_304_NOT_MODIFIED;
822 case 305:
823 return HTTP_STATUS_305_USE_PROXY;
824 case 307:
825 return HTTP_STATUS_307_TEMPORARY_REDIRECT;
826 case 400:
827 return HTTP_STATUS_400_BAD_REQUEST;
828 case 401:
829 return HTTP_STATUS_401_UNAUTHORIZED;
830 case 402:
831 return HTTP_STATUS_402_PAYMENT_REQUIRED;
832 case 403:
833 return HTTP_STATUS_403_FORBIDDEN;
834 case 404:
835 return HTTP_STATUS_404_NOT_FOUND;
836 case 405:
837 return HTTP_STATUS_405_METHOD_NOT_ALLOWED;
838 case 406:
839 return HTTP_STATUS_406_NOT_ACCEPTABLE;
840 case 407:
841 return HTTP_STATUS_407_PROXY_AUTHENTICATION_REQUIRED;
842 case 408:
843 return HTTP_STATUS_408_REQUEST_TIME_OUT;
844 case 409:
845 return HTTP_STATUS_409_CONFLICT;
846 case 410:
847 return HTTP_STATUS_410_GONE;
848 case 411:
849 return HTTP_STATUS_411_LENGTH_REQUIRED;
850 case 412:
851 return HTTP_STATUS_412_PRECONDITION_FAILED;
852 case 413:
853 return HTTP_STATUS_413_REQUEST_ENTITY_TOO_LARGE;
854 case 414:
855 return HTTP_STATUS_414_REQUEST_URI_TOO_LARGE;
856 case 415:
857 return HTTP_STATUS_415_UNSUPPORETD_MEDIA_TYPE;
858 case 416:
859 return HTTP_STATUS_416_REQUESTED_RANGE_NOT_SATISFIED;
860 case 417:
861 return HTTP_STATUS_417_EXPECTATION_FAILED;
862 case 500:
863 return HTTP_STATUS_500_INTERNAL_SERVER_ERROR;
864 case 501:
865 return HTTP_STATUS_501_NOT_IMIPLEMENTED;
866 case 502:
867 return HTTP_STATUS_502_BAD_GATEWAY;
868 case 503:
869 return HTTP_STATUS_503_SERVICE_UNAVAILABLE;
870 case 504:
871 return HTTP_STATUS_504_GATEWAY_TIME_OUT;
872 case 505:
873 return HTTP_STATUS_505_HTTP_VERSION_NOT_SUPPORTED;
874
875 default:
876 return HTTP_STATUS_UNSUPPORTED_STATUS;
877 }
878 }
879
880 /**
881 Check whether the user's token or event has already
882 been enqueue on HTTP TxToken or RxToken list.
883
884 @param[in] Map The container of either user's transmit or receive
885 token.
886 @param[in] Item Current item to check against.
887 @param[in] Context The Token to check againist.
888
889 @retval EFI_ACCESS_DENIED The token or event has already been enqueued in IP
890 @retval EFI_SUCCESS The current item isn't the same token/event as the
891 context.
892
893 **/
894 EFI_STATUS
895 EFIAPI
896 HttpTokenExist (
897 IN NET_MAP *Map,
898 IN NET_MAP_ITEM *Item,
899 IN VOID *Context
900 )
901 {
902 EFI_HTTP_TOKEN *Token;
903 EFI_HTTP_TOKEN *TokenInItem;
904
905 Token = (EFI_HTTP_TOKEN *) Context;
906 TokenInItem = (EFI_HTTP_TOKEN *) Item->Key;
907
908 if (Token == TokenInItem || Token->Event == TokenInItem->Event) {
909 return EFI_ACCESS_DENIED;
910 }
911
912 return EFI_SUCCESS;
913 }
914
915 /**
916 Check whether the HTTP message associated with TxToken is already sent out.
917
918 @param[in] Map The container of TxToken.
919 @param[in] Item Current item to check against.
920 @param[in] Context The Token to check againist.
921
922 @retval EFI_NOT_READY The HTTP message is still queued in the list.
923 @retval EFI_SUCCESS The HTTP message has been sent out.
924
925 **/
926 EFI_STATUS
927 EFIAPI
928 HttpTcpNotReady (
929 IN NET_MAP *Map,
930 IN NET_MAP_ITEM *Item,
931 IN VOID *Context
932 )
933 {
934 HTTP_TOKEN_WRAP *ValueInItem;
935
936 ValueInItem = (HTTP_TOKEN_WRAP *) Item->Value;
937
938 if (!ValueInItem->TcpWrap.IsTxDone) {
939 return EFI_NOT_READY;
940 }
941
942 return EFI_SUCCESS;
943 }
944
945 /**
946 Transmit the HTTP mssage by processing the associated HTTP token.
947
948 @param[in] Map The container of TxToken.
949 @param[in] Item Current item to check against.
950 @param[in] Context The Token to check againist.
951
952 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
953 @retval EFI_SUCCESS The HTTP message is queued into TCP transmit
954 queue.
955
956 **/
957 EFI_STATUS
958 EFIAPI
959 HttpTcpTransmit (
960 IN NET_MAP *Map,
961 IN NET_MAP_ITEM *Item,
962 IN VOID *Context
963 )
964 {
965 HTTP_TOKEN_WRAP *ValueInItem;
966 EFI_STATUS Status;
967 CHAR8 *RequestStr;
968 CHAR8 *Url;
969
970 ValueInItem = (HTTP_TOKEN_WRAP *) Item->Value;
971 if (ValueInItem->TcpWrap.IsTxDone) {
972 return EFI_SUCCESS;
973 }
974
975 //
976 // Parse the URI of the remote host.
977 //
978 Url = AllocatePool (StrLen (ValueInItem->HttpToken->Message->Data.Request->Url) + 1);
979 if (Url == NULL) {
980 return EFI_OUT_OF_RESOURCES;
981 }
982
983 UnicodeStrToAsciiStr (ValueInItem->HttpToken->Message->Data.Request->Url, Url);
984
985 //
986 // Create request message.
987 //
988 RequestStr = HttpGenRequestString (
989 ValueInItem->HttpInstance,
990 ValueInItem->HttpToken->Message,
991 Url
992 );
993 FreePool (Url);
994 if (RequestStr == NULL) {
995 return EFI_OUT_OF_RESOURCES;
996 }
997
998 //
999 // Transmit the request message.
1000 //
1001 Status = HttpTransmitTcp4 (
1002 ValueInItem->HttpInstance,
1003 ValueInItem,
1004 (UINT8*) RequestStr,
1005 AsciiStrLen (RequestStr)
1006 );
1007 FreePool (RequestStr);
1008 return Status;
1009 }
1010
1011 /**
1012 Receive the HTTP response by processing the associated HTTP token.
1013
1014 @param[in] Map The container of RxToken.
1015 @param[in] Item Current item to check against.
1016 @param[in] Context The Token to check againist.
1017
1018 @retval EFI_SUCCESS The HTTP response is queued into TCP receive
1019 queue.
1020 @retval Others Other error as indicated.
1021
1022 **/
1023 EFI_STATUS
1024 EFIAPI
1025 HttpTcpReceive (
1026 IN NET_MAP *Map,
1027 IN NET_MAP_ITEM *Item,
1028 IN VOID *Context
1029 )
1030 {
1031 //
1032 // Process the queued HTTP response.
1033 //
1034 return HttpResponseWorker ((HTTP_TOKEN_WRAP *) Item->Value);
1035 }
1036
1037 /**
1038 Generate HTTP request string.
1039
1040 @param[in] HttpInstance Pointer to HTTP_PROTOCOL structure.
1041 @param[in] Message Pointer to storage containing HTTP message data.
1042 @param[in] Url The URL of a remote host.
1043
1044 @return Pointer to the created HTTP request string.
1045 @return NULL if any error occured.
1046
1047 **/
1048 CHAR8 *
1049 HttpGenRequestString (
1050 IN HTTP_PROTOCOL *HttpInstance,
1051 IN EFI_HTTP_MESSAGE *Message,
1052 IN CHAR8 *Url
1053 )
1054 {
1055 EFI_STATUS Status;
1056 UINTN StrLength;
1057 UINT8 *Request;
1058 UINT8 *RequestPtr;
1059 UINTN HttpHdrSize;
1060 UINTN MsgSize;
1061 BOOLEAN Success;
1062 VOID *HttpHdr;
1063 EFI_HTTP_HEADER **AppendList;
1064 UINTN Index;
1065
1066 ASSERT (HttpInstance != NULL);
1067 ASSERT (Message != NULL);
1068
1069 DEBUG ((EFI_D_ERROR, "HttpMethod - %x\n", Message->Data.Request->Method));
1070
1071 Request = NULL;
1072 Success = FALSE;
1073 HttpHdr = NULL;
1074 AppendList = NULL;
1075
1076 //
1077 // Build AppendList
1078 //
1079 AppendList = AllocateZeroPool (sizeof (EFI_HTTP_HEADER *) * (Message->HeaderCount));
1080 if (AppendList == NULL) {
1081 return NULL;
1082 }
1083
1084 for(Index = 0; Index < Message->HeaderCount; Index++){
1085 AppendList[Index] = &Message->Headers[Index];
1086 }
1087
1088 //
1089 // Check whether the EFI_HTTP_UTILITIES_PROTOCOL is available.
1090 //
1091 if (mHttpUtilities == NULL) {
1092 return NULL;
1093 }
1094
1095 //
1096 // Build raw unformatted HTTP headers.
1097 //
1098 Status = mHttpUtilities->Build (
1099 mHttpUtilities,
1100 0,
1101 NULL,
1102 0,
1103 NULL,
1104 Message->HeaderCount,
1105 AppendList,
1106 &HttpHdrSize,
1107 &HttpHdr
1108 );
1109 FreePool (AppendList);
1110 if (EFI_ERROR (Status) || HttpHdr == NULL) {
1111 return NULL;
1112 }
1113
1114 //
1115 // Calculate HTTP message length.
1116 //
1117 MsgSize = Message->BodyLength + HTTP_MAXIMUM_METHOD_LEN + AsciiStrLen (Url) +
1118 AsciiStrLen (HTTP_VERSION_CRLF_STR) + HttpHdrSize;
1119 Request = AllocateZeroPool (MsgSize);
1120 if (Request == NULL) {
1121 goto Exit;
1122 }
1123
1124 RequestPtr = Request;
1125 //
1126 // Construct header request
1127 //
1128 switch (Message->Data.Request->Method) {
1129 case HttpMethodGet:
1130 StrLength = sizeof (HTTP_GET_STR) - 1;
1131 CopyMem (RequestPtr, HTTP_GET_STR, StrLength);
1132 RequestPtr += StrLength;
1133 break;
1134 case HttpMethodHead:
1135 StrLength = sizeof (HTTP_HEAD_STR) - 1;
1136 CopyMem (RequestPtr, HTTP_HEAD_STR, StrLength);
1137 RequestPtr += StrLength;
1138 break;
1139 default:
1140 ASSERT (FALSE);
1141 goto Exit;
1142 }
1143
1144 StrLength = AsciiStrLen (Url);
1145 CopyMem (RequestPtr, Url, StrLength);
1146 RequestPtr += StrLength;
1147
1148 StrLength = sizeof (HTTP_VERSION_CRLF_STR) - 1;
1149 CopyMem (RequestPtr, HTTP_VERSION_CRLF_STR, StrLength);
1150 RequestPtr += StrLength;
1151
1152 //
1153 // Construct header
1154 //
1155 CopyMem (RequestPtr, HttpHdr, HttpHdrSize);
1156 RequestPtr += HttpHdrSize;
1157
1158 //
1159 // Construct body
1160 //
1161 if (Message->Body != NULL) {
1162 CopyMem (RequestPtr, Message->Body, Message->BodyLength);
1163 RequestPtr += Message->BodyLength;
1164 }
1165
1166 //
1167 // Done
1168 //
1169 *RequestPtr = 0;
1170 Success = TRUE;
1171
1172 Exit:
1173
1174 if (!Success) {
1175 if (Request != NULL) {
1176 FreePool (Request);
1177 }
1178
1179 Request = NULL;
1180 }
1181
1182 if (HttpHdr != NULL) {
1183 FreePool (HttpHdr);
1184 }
1185
1186 return (CHAR8*) Request;
1187 }