aws_24.0.0_2b75fe6d/src/core/aws-status-set.adb

  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
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
------------------------------------------------------------------------------
--                              Ada Web Server                              --
--                                                                          --
--                     Copyright (C) 2000-2021, AdaCore                     --
--                                                                          --
--  This library is free software;  you can redistribute it and/or modify   --
--  it under terms of the  GNU General Public License  as published by the  --
--  Free Software  Foundation;  either version 3,  or (at your  option) any --
--  later version. This library is distributed in the hope that it will be  --
--  useful, but WITHOUT ANY WARRANTY;  without even the implied warranty of --
--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                    --
--                                                                          --
--  As a special exception under Section 7 of GPL version 3, you are        --
--  granted additional permissions described in the GCC Runtime Library     --
--  Exception, version 3.1, as published by the Free Software Foundation.   --
--                                                                          --
--  You should have received a copy of the GNU General Public License and   --
--  a copy of the GCC Runtime Library Exception along with this program;    --
--  see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see   --
--  <http://www.gnu.org/licenses/>.                                         --
--                                                                          --
--  As a special exception, if other files instantiate generics from this   --
--  unit, or you link this unit with other files to produce an executable,  --
--  this  unit  does not  by itself cause  the resulting executable to be   --
--  covered by the GNU General Public License. This exception does not      --
--  however invalidate any other reasons why the executable file  might be  --
--  covered by the  GNU Public License.                                     --
------------------------------------------------------------------------------

with Ada.Characters.Handling;
with Ada.Strings.Fixed;
with Ada.Unchecked_Deallocation;

with GNAT.SHA256;

with AWS.Headers;
with AWS.Headers.Values;
with AWS.Messages;
with AWS.Net.Buffered;
with AWS.Parameters;
with AWS.Resources.Streams.Memory.ZLib;
with AWS.Server;
with AWS.Translator;
with AWS.URL.Set;
with ZLib;

package body AWS.Status.Set is

   use Ada.Strings;

   procedure Authorization (D : in out Data);
   --  Parse the Authorization parameters from the Authorization header value

   procedure Create_Stream (D : in out Data)
     with Pre  => D.Binary_Data = null,
          Post => D.Binary_Data /= null;
   --  Create the in-memory stream used to store the message data. This stream
   --  is either standard or compressed to support GZip content-encoding.

   function Create_Private_Hash
     (SID : AWS.Session.Id) return GNAT.SHA256.Message_Digest with Inline;
   --  Returns the binary private digest for the SID

   -------------------
   -- Add_Parameter --
   -------------------

   procedure Add_Parameter
     (D           : in out Data;
      Name, Value : String;
      Decode      : Boolean := True;
      Replace     : Boolean := False) is
   begin
      Add_Parameter
        (D       => D,
         Name    => To_Unbounded_String (Name),
         Value   => To_Unbounded_String (Value),
         Decode  => Decode,
         Replace => Replace);
   end Add_Parameter;

   procedure Add_Parameter
     (D           : in out Data;
      Name, Value : Unbounded_String;
      Decode      : Boolean := True;
      Replace     : Boolean := False) is
   begin
      if Replace then
         AWS.URL.Set.Parameters (D.URI'Access).all.Update
           (Name, Value, Decode);
      else
         AWS.URL.Set.Parameters (D.URI'Access).all.Add (Name, Value, Decode);
      end if;
   end Add_Parameter;

   --------------------
   -- Add_Parameters --
   --------------------

   procedure Add_Parameters (D : in out Data; Parameters : String) is
   begin
      AWS.URL.Set.Parameters (D.URI'Access).all.Add (Parameters);
   end Add_Parameters;

   -----------------
   -- Append_Body --
   -----------------

   procedure Append_Body
     (D      : in out Data;
      Buffer : Stream_Element_Array;
      Trim   : Boolean := False) is
   begin
      if D.Binary_Data = null then
         Create_Stream (D);
      end if;

      D.Binary_Data.Append (Buffer, Trim);
   end Append_Body;

   -----------------
   -- Attachments --
   -----------------

   procedure Attachments
     (D : in out Data; Attachments : AWS.Attachments.List) is
   begin
      D.Attachments := Attachments;
   end Attachments;

   ------------------
   -- Authenticate --
   ------------------

   procedure Authenticate
     (D                      : in out Data;
      Authorization_Mode     : Authorization_Type;
      Authorization_Name     : String;
      Authorization_Password : String) is
   begin
      D.Auth_Mode     := Authorization_Mode;
      D.Auth_Name     := To_Unbounded_String (Authorization_Name);
      D.Auth_Password := To_Unbounded_String (Authorization_Password);
   end Authenticate;

   -------------------
   -- Authorization --
   -------------------

   procedure Authorization (D : in out Data) is

      Header_Value : constant String :=
                       AWS.Headers.Get
                         (D.Header, Messages.Authorization_Token);

      procedure Named_Value (Name, Value : String; Quit : in out Boolean);

      procedure Value (Item : String; Quit : in out Boolean);

      -----------------
      -- Named_Value --
      -----------------

      procedure Named_Value (Name, Value : String; Quit : in out Boolean) is

         type Digest_Attribute
            is (Username, Realm, Nonce, NC, CNonce,
                QOP, URI, Response, Algorithm);
         --  The enumeration type is using to be able to
         --  use the name in the case statement.
         --  The case statement has usially faster implementation.

         Attribute : Digest_Attribute;

         function "+" (Item : String) return Unbounded_String
           renames To_Unbounded_String;

      begin
         begin
            Attribute := Digest_Attribute'Value (Name);
         exception
            when Constraint_Error =>
               --  Ignoring unrecognized attribute
               return;
         end;

         --  Check if the attributes is for the Digest authenticatio schema.
         --  AWS does not support other authentication schemas with attributes
         --  now.

         if D.Auth_Mode /= Digest then
            Quit := True;
         end if;

         case Attribute is
            when Username  => D.Auth_Name     := +Value;
            when Realm     => D.Auth_Realm    := +Value;
            when NC        => D.Auth_NC       := +Value;
            when CNonce    => D.Auth_CNonce   := +Value;
            when QOP       => D.Auth_QOP      := +Value;
            when Nonce     => D.Auth_Nonce    := +Value;
            when Response  => D.Auth_Response := +Value;
            when URI       => D.Auth_URI      := +Value;
            when Algorithm =>
               if Value /= "MD5" then
                  raise Constraint_Error
                    with "Only MD5 algorithm is supported.";
               end if;
         end case;
      end Named_Value;

      -----------
      -- Value --
      -----------

      procedure Value (Item : String; Quit : in out Boolean) is
         Upper_Item : constant String :=
                        Characters.Handling.To_Upper (Item);
      begin
         if Upper_Item = "BASIC" then

            D.Auth_Mode := Basic;

            Quit := True;

            --  We could not continue to parse Basic authentication
            --  by the regular way, because next value is Base64 encoded
            --  "username:password", it is possibe to have symbol '=' there,
            --  our parser could think that it is name/value delimiter.

            declare
               Auth_Str : constant String :=
                            Translator.To_String
                              (Translator.Base64_Decode
                                 (Header_Value
                                    (Item'Length + 2 .. Header_Value'Last)));

               Delimit  : constant Natural := Fixed.Index (Auth_Str, ":");
            begin
               if Delimit = 0 then
                  D.Auth_Name := To_Unbounded_String (Auth_Str);

               else
                  D.Auth_Name :=
                    To_Unbounded_String (Auth_Str (1 .. Delimit - 1));
                  D.Auth_Password :=
                    To_Unbounded_String
                      (Auth_Str (Delimit + 1 .. Auth_Str'Last));
               end if;
            end;

         elsif Upper_Item = "DIGEST" then
            D.Auth_Mode := Digest;
         end if;
      end Value;

      procedure Parse is new AWS.Headers.Values.Parse (Value, Named_Value);

   begin
      Parse (Header_Value);
   end Authorization;

   ------------
   -- Binary --
   ------------

   procedure Binary (D : in out Data; Parameter : Stream_Element_Array) is
   begin
      if D.Binary_Data = null then
         Create_Stream (D);

      else
         --  Clear previous data if exists

         D.Binary_Data.Clear;
      end if;

      --  "Trim => True" mean don't remain allocated space at the end of
      --  internal buffer for next data because this routine designed to put
      --  all data at once.

      D.Binary_Data.Append (Parameter, Trim => True);
   end Binary;

   -------------------------------
   -- Case_Sensitive_Parameters --
   -------------------------------

   procedure Case_Sensitive_Parameters (D : in out Data; Mode : Boolean) is
   begin
      AWS.URL.Set.Parameters (D.URI'Access).all.Case_Sensitive (Mode);
   end Case_Sensitive_Parameters;

   ---------------------
   -- Connection_Data --
   ---------------------

   procedure Connection_Data
     (D        : in out Data;
      Host     : String;
      Port     : Positive;
      Security : Boolean) is
   begin
      AWS.URL.Set.Connection_Data (D.URI, Host, Port, Security);
   end Connection_Data;

   -------------------------
   -- Create_Private_Hash --
   -------------------------

   function Create_Private_Hash
     (SID : AWS.Session.Id) return GNAT.SHA256.Message_Digest
   is
      P_Key   : constant String  := AWS.Session.Private_Key (SID);
      L_SID   : constant String  := AWS.Session.Image (SID);
      Context : SHA256.Context   := SHA256.HMAC_Initial_Context (P_Key);
   begin
      SHA256.Update (Context, L_SID);
      return SHA256.Digest (Context);
   end Create_Private_Hash;

   -------------------
   -- Create_Stream --
   -------------------

   procedure Create_Stream (D : in out Data) is
   begin
      if AWS.Headers.Get
           (D.Header, Messages.Content_Encoding_Token) = "gzip"
      then
         D.Binary_Data := new Resources.Streams.Memory.ZLib.Stream_Type;
         Resources.Streams.Memory.ZLib.Stream_Type
           (D.Binary_Data.all).Inflate_Initialize (Header => ZLib.GZip);

      else
         D.Binary_Data := new Resources.Streams.Memory.Stream_Type;
      end if;
   end Create_Stream;

   -------------------------
   -- Delete_Idle_Session --
   -------------------------

   procedure Delete_Idle_Session (D : in out Data) is
   begin
      if D.Session_Created
        and then AWS.Session.Delete_If_Empty (D.Session_Id)
      then
         D.Session_Created := False;
      end if;
   end Delete_Idle_Session;

   ----------
   -- Free --
   ----------

   procedure Free (D : in out Data) is
      procedure Unchecked_Free is new Ada.Unchecked_Deallocation
        (Resources.Streams.Memory.Stream_Type'Class, Memory_Stream_Access);
   begin
      if D.Binary_Data /= null then
         D.Binary_Data.Close;
         Unchecked_Free (D.Binary_Data);
      end if;

      AWS.Attachments.Reset (D.Attachments, Delete_Files => True);
   end Free;

   -------------
   -- Headers --
   -------------

   procedure Headers (D : in out Data; Headers : AWS.Headers.List) is
   begin
      D.Header := Headers;
   end Headers;

   ----------------
   -- Keep_Alive --
   ----------------

   procedure Keep_Alive (D : in out Data; Flag : Boolean) is
   begin
      D.Keep_Alive := Flag;
   end Keep_Alive;

   ----------------
   -- Parameters --
   ----------------

   procedure Parameters (D : in out Data; Set : AWS.Parameters.List) is
   begin
      AWS.URL.Set.Parameters (D.URI, Set);
   end Parameters;

   --------------------------
   -- Parameters_From_Body --
   --------------------------

   procedure Parameters_From_Body
     (D : in out Data; Boundary : String := "")
   is
      use AWS.Resources.Streams;

      R : constant not null access Memory.Stream_Type'Class :=
            D.Binary_Data;
   begin
      D.Binary_Data.Reset;

      if Boundary /= "" then
         Look_Boundary : loop
            declare
               Line : constant String := R.Get_Line;
            begin
               if Line = Boundary then
                  --  Boundary found, skip headers until empty line found
                  loop
                     declare
                        Line : constant String := R.Get_Line;
                     begin
                        exit Look_Boundary when Line = "";
                     end;
                  end loop;
               end if;
            end;
         end loop Look_Boundary;
      end if;

      --  Now parse body to set parameters

      AWS.URL.Set.Parameters (D.URI'Access).all.Add (R.all);
   end Parameters_From_Body;

   --------------
   -- Protocol --
   --------------

   procedure Protocol (D : in out Data; State : Protocol_State) is
   begin
      D.Protocol := State;
   end Protocol;

   -----------
   -- Query --
   -----------

   procedure Query (D : in out Data; Parameters : String) is
      P : constant not null access AWS.Parameters.List :=
            AWS.URL.Set.Parameters (D.URI'Access);
   begin
      if P.Count > 0 then
         raise Program_Error with "Could not set HTTP Query twice";
      end if;

      D.Query := To_Unbounded_String (Parameters);

      P.Add (Parameters);
   end Query;

   ---------------
   -- Read_Body --
   ---------------

   procedure Read_Body
     (Socket   : Net.Socket_Type'Class;
      D        : in out Data;
      Boundary : String := "")
   is
      procedure Read_Whole_Body;
      --  Read the whole body (Content_Length octets)

      ---------------------
      -- Read_Whole_Body --
      ---------------------

      procedure Read_Whole_Body is

         procedure Read_Chunk (Size : Stream_Element_Offset);
         --  Read a chunk of data of the given Size, the corresponding data is
         --  added into the Binary_Data.

         ----------------
         -- Read_Chunk --
         ----------------

         procedure Read_Chunk (Size : Stream_Element_Offset) is
            Buffer : Stream_Element_Array (1 .. 4096);
            Rest   : Stream_Element_Offset := Size;
         begin
            while Rest > Buffer'Length loop
               Rest := Rest - Buffer'Length;
               Net.Buffered.Read (Socket, Buffer);
               D.Binary_Data.Append (Buffer);
            end loop;

            Net.Buffered.Read (Socket, Buffer (1 .. Rest));
            D.Binary_Data.Append (Buffer (1 .. Rest), Trim => True);
         end Read_Chunk;

         TE : constant String  :=
                AWS.Headers.Get (D.Header, Messages.Transfer_Encoding_Token);
      begin
         if TE = "chunked" then
            --  A chuncked message is written on the stream as list of data
            --  chunk. Each chunk has the following format:
            --
            --  <N : the chunk size in hexadecimal> CRLF
            --  <N * BYTES : the data> CRLF
            --
            --  The termination chunk is:
            --
            --  0 CRLF
            --  CRLF
            --

            Read_Chunks : loop
               declare
                  C_Size : constant Stream_Element_Offset :=
                             Stream_Element_Offset'Value
                               ("16#" & Net.Buffered.Get_Line (Socket) & '#');
                  CRLF   : Stream_Element_Array (1 .. 2);
               begin
                  if C_Size = 0 then
                     --  We reached the end of the chunked message, read
                     --  terminating CRLF.
                     Net.Buffered.Read (Socket, CRLF);
                     exit Read_Chunks;
                  end if;

                  Read_Chunk (Size => C_Size);
                  Net.Buffered.Read (Socket, CRLF);
               end;
            end loop Read_Chunks;

         else
            Read_Chunk (Size => Stream_Element_Offset (D.Content_Length));
         end if;
      end Read_Whole_Body;

   begin
      if D.Binary_Data = null then
         Create_Stream (D);
      end if;

      if Boundary = "" then
         Read_Whole_Body;

      else
         declare
            Content : constant Stream_Element_Array :=
                        Net.Buffered.Read_Until
                          (Socket,
                           Translator.To_Stream_Element_Array (Boundary));
         begin
            if Content'Length > Boundary'Length + 2 then
               D.Binary_Data.Append
                 (Content (Content'First
                    .. Content'Last - Boundary'Length - 2),
                  Trim => True);
               --  Boundary'Length - 2 to remove the boundary and also the CRLF
               --  (before the boundary) which is not part of the body.
            end if;
         end;
      end if;
   end Read_Body;

   -----------------
   -- Read_Header --
   -----------------

   procedure Read_Header (Socket : Net.Socket_Type'Class; D : in out Data) is
   begin
      D.Header.Read (Socket);
      Update_Data_From_Header (D);
   end Read_Header;

   -------------
   -- Request --
   -------------

   procedure Request
     (D            : in out Data;
      Method       : String;
      URI          : String;
      HTTP_Version : String) is
   begin
      D.Calendar_Time  := Ada.Calendar.Clock;
      D.Monotonic_Time := Real_Time.Clock;

      --  Method is case sensitive

      if Method = Messages.Options_Token then
         D.Method := Status.OPTIONS;

      elsif Method = Messages.Get_Token then
         D.Method := Status.GET;

      elsif Method = Messages.Head_Token then
         D.Method := Status.HEAD;

      elsif Method = Messages.Post_Token then
         D.Method := Status.POST;

      elsif Method = Messages.Put_Token then
         D.Method := Status.PUT;

      elsif Method = Messages.Delete_Token then
         D.Method := Status.DELETE;

      elsif Method = Messages.Trace_Token then
         D.Method := Status.TRACE;

      elsif Method = Messages.Connect_Token then
         D.Method := Status.CONNECT;

      else
         D.Method := Status.EXTENSION_METHOD;
      end if;

      D.Method_String := To_Unbounded_String (Method);
      D.HTTP_Version  := To_Unbounded_String (HTTP_Version);

      if HTTP_Version in HTTP_10 | HTTP_11 then
         D.Protocol := HTTP_1;
      elsif HTTP_Version = HTTP_2 then
         D.Protocol := H2;
      end if;

      --  Parse URI and keep parameters case sensitivity flag

      AWS.URL.Set.Parse (D.URI, URI, False, False);
   end Request;

   -----------
   -- Reset --
   -----------

   procedure Reset (D : in out Data) is
   begin
      Free (D);

      D.Socket            := null;
      D.Peername          := Null_Unbounded_String;
      D.Protocol          := HTTP_1;
      D.Method            := GET;
      D.Method_String     := Null_Unbounded_String;
      D.Query             := Null_Unbounded_String;
      D.HTTP_Version      := Null_Unbounded_String;
      D.Content_Length    := 0;
      D.Auth_Mode         := None;
      D.Auth_Name         := Null_Unbounded_String;
      D.Auth_Password     := Null_Unbounded_String;
      D.Auth_Realm        := Null_Unbounded_String;
      D.Auth_Nonce        := Null_Unbounded_String;
      D.Auth_NC           := Null_Unbounded_String;
      D.Auth_CNonce       := Null_Unbounded_String;
      D.Auth_QOP          := Null_Unbounded_String;
      D.Auth_URI          := Null_Unbounded_String;
      D.Auth_Response     := Null_Unbounded_String;
      D.Session_Id        := AWS.Session.No_Session;
      D.Session_Private   := No_Session_Private;
      D.Session_Created   := False;
      D.Session_Timed_Out := False;
      D.SOAP_Action       := False;
      D.Uploaded          := False;
      D.Monotonic_Time    := Ada.Real_Time.Time_First;

      D.Header.Reset;
      AWS.URL.Set.Parameters (D.URI'Access).all.Reset;
   end Reset;

   -------------
   -- Session --
   -------------

   procedure Session (D : in out Data) is
   begin
      D.Session_Id      := AWS.Session.Create;

      --  Create the session's private key

      D.Session_Private := Create_Private_Hash (D.Session_Id);

      D.Session_Created := True;
   end Session;

   -----------------------
   -- Session_Timed_Out --
   -----------------------

   procedure Session_Timed_Out (D : in out Data; Timed_Out : Boolean) is
   begin
      D.Session_Timed_Out := Timed_Out;
   end Session_Timed_Out;

   ------------
   -- Socket --
   ------------

   procedure Socket (D : in out Data; Sock : Net.Socket_Access) is
   begin
      D.Socket   := Sock;
      D.Peername := To_Unbounded_String (Net.Peer_Addr (Sock.all));
   end Socket;

   -----------------------------
   -- Update_Data_From_Header --
   -----------------------------

   procedure Update_Data_From_Header (D : in out Data) is
      AWS_Session_Name      : constant String := Server.Session_Name;
      AWS_Session_Priv_Name : constant String := Server.Session_Private_Name;
   begin
      Authorization (D);

      declare
         Content_Length : constant String :=
                            AWS.Headers.Get
                              (D.Header, Messages.Content_Length_Token);
      begin
         if Content_Length /= "" then
            D.Content_Length := Stream_Element_Count'Value (Content_Length);

         --  Special case for the websockets draft76: even though there is no
         --  Content-Length header, the message contains 8 bytes that are part
         --  of the challenge and must be available to AWS.

         elsif AWS.Headers.Exist
           (D.Header, Messages.Sec_WebSocket_Key1_Token)
         then
            D.Content_Length := 8;
         end if;
      end;

      D.SOAP_Action := AWS.Headers.Exist (D.Header, Messages.SOAPAction_Token);

      declare
         use AWS.Headers;

         Cookies_Set : constant VString_Array :=
                         Get_Values (D.Header, Messages.Cookie_Token);
      begin
         for Idx in Cookies_Set'Range loop
            declare
               --  The expected Cookie line is:
               --  Cookie: ... AWS=<cookieID>[,;] AWS_Private=<key>[,;] ...

               use type AWS.Session.Id;

               procedure Value
                 (Item : String;
                  Quit : in out Boolean) is null;
               --  Called for every un-named value read from the header value

               procedure Named_Value
                 (Name  : String;
                  Value : String;
                  Quit  : in out Boolean);
               --  Called for every named value read from the header value

               SID_Found   : Boolean := False;
               P_SID_Found : Boolean := False;

               -----------------
               -- Named_Value --
               -----------------

               procedure Named_Value
                 (Name  : String;
                  Value : String;
                  Quit  : in out Boolean) is
               begin
                  --  Check if it is current process Cookie

                  if Name = AWS_Session_Name then
                     D.Session_Id := AWS.Session.Value (Value);

                     --  Check if the cookie value was correct

                     if D.Session_Id = AWS.Session.No_Session then
                        return;
                     end if;

                     --  Check if cookie exists in the server

                     if not AWS.Session.Exist (D.Session_Id) then
                        --  Reset to empty cookie if session does not exist.
                        --  This is a case where a session has timed out.

                        D.Session_Id        := AWS.Session.No_Session;
                        D.Session_Private   := No_Session_Private;
                        D.Session_Timed_Out := True;

                        return;
                     end if;

                     --  Check if the session has expired, even though it
                     --  hasn't been deleted yet by the cleaner task.

                     if AWS.Session.Has_Expired (D.Session_Id) then
                        AWS.Session.Delete (D.Session_Id);
                        D.Session_Id        := AWS.Session.No_Session;
                        D.Session_Private   := No_Session_Private;
                        D.Session_Timed_Out := True;
                        return;
                     end if;

                     SID_Found := True;

                  elsif Name = AWS_Session_Priv_Name then
                     if Value'Length = D.Session_Private'Length then
                        D.Session_Private := Value;
                        P_SID_Found := True;
                     end if;
                  end if;

                  Quit := SID_Found and then P_SID_Found;
               end Named_Value;

               -----------
               -- Parse --
               -----------

               procedure Parse is
                 new AWS.Headers.Values.Parse (Value, Named_Value);

            begin
               Parse (To_String (Cookies_Set (Idx)));

               --  Exit when we have found the session cookies

               exit when D.Session_Id /= AWS.Session.No_Session
                 and then D.Session_Private /= Null_Unbounded_String;
            end;
         end loop;

         if AWS.Headers.Get (D.Header, Messages.Upgrade_Token) =
           Messages.H2C_Token
         then
            D.Protocol := Upgrade_To_H2C;
         end if;

         --  Now double check that the session id is valid and has not been
         --  compromised.

         if D.Session_Private /= Create_Private_Hash (SID => D.Session_Id) then
            --  There is mismatch, could be a corrupted session id
            D.Session_Id := AWS.Session.No_Session;
         end if;
      end;
   end Update_Data_From_Header;

   --------------
   -- Uploaded --
   --------------

   procedure Uploaded (D : in out Data) is
   begin
      D.Uploaded := True;
   end Uploaded;

end AWS.Status.Set;