Skip to content

Commit fd9b020

Browse files
committed
Rework AWS client's API to avoid stressing too much the stack.
When sending large payload over a socket with AWS.Client.Post we ensure that no copy of the data is made on the stack to avoid a stack overflow. This is especially important when a call is made from a task where the stack may be limited. Add corresponding regression test. Fixes T527-014.
1 parent 6668504 commit fd9b020

File tree

6 files changed

+155
-17
lines changed

6 files changed

+155
-17
lines changed

regtests/0326_big_post/big_post.adb

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
------------------------------------------------------------------------------
2+
-- Ada Web Server --
3+
-- --
4+
-- Copyright (C) 2020, AdaCore --
5+
-- --
6+
-- This is free software; you can redistribute it and/or modify it --
7+
-- under terms of the GNU General Public License as published by the --
8+
-- Free Software Foundation; either version 3, or (at your option) any --
9+
-- later version. This software is distributed in the hope that it will --
10+
-- be useful, but WITHOUT ANY WARRANTY; without even the implied warranty --
11+
-- of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU --
12+
-- General Public License for more details. --
13+
-- --
14+
-- You should have received a copy of the GNU General Public License --
15+
-- distributed with this software; see file COPYING3. If not, go --
16+
-- to http://www.gnu.org/licenses for a complete copy of the license. --
17+
------------------------------------------------------------------------------
18+
19+
with Ada.Streams;
20+
with Ada.Text_IO;
21+
22+
with AWS.Client;
23+
with AWS.Messages;
24+
with AWS.MIME;
25+
with AWS.Net.Log;
26+
with AWS.Parameters;
27+
with AWS.Response;
28+
with AWS.Server.Status;
29+
with AWS.Status;
30+
with AWS.Translator;
31+
with AWS.Utils;
32+
33+
procedure Big_Post is
34+
35+
use Ada;
36+
use Ada.Streams;
37+
use AWS;
38+
39+
type String_Access is access String;
40+
41+
--------
42+
-- CB --
43+
--------
44+
45+
function CB (Request : Status.Data) return Response.Data is
46+
begin
47+
Text_IO.Put_Line
48+
("Length payload: " & Status.Content_Length (Request)'Img);
49+
return Response.Build (MIME.Text_HTML, "ok");
50+
end CB;
51+
52+
WS : Server.HTTP;
53+
M_Body : String_Access;
54+
R : Response.Data;
55+
56+
begin
57+
Server.Start (WS, "Big Post", CB'Unrestricted_Access, Port => 0);
58+
59+
Text_IO.Put_Line ("started");
60+
Text_IO.Flush;
61+
62+
M_Body := new String'(1 .. 16_000_000 => 'a');
63+
64+
R := Client.Post
65+
(Server.Status.Local_URL (WS) & "/big_post",
66+
M_Body.all,
67+
Content_Type => MIME.Text_HTML);
68+
69+
Text_IO.Put_Line (Response.Message_Body (R));
70+
71+
Server.Shutdown (WS);
72+
Text_IO.Put_Line ("shutdown");
73+
end Big_Post;

regtests/0326_big_post/big_post.gpr

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
------------------------------------------------------------------------------
2+
-- Ada Web Server --
3+
-- --
4+
-- Copyright (C) 2020, AdaCore --
5+
-- --
6+
-- This is free software; you can redistribute it and/or modify it --
7+
-- under terms of the GNU General Public License as published by the --
8+
-- Free Software Foundation; either version 3, or (at your option) any --
9+
-- later version. This software is distributed in the hope that it will --
10+
-- be useful, but WITHOUT ANY WARRANTY; without even the implied warranty --
11+
-- of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU --
12+
-- General Public License for more details. --
13+
-- --
14+
-- You should have received a copy of the GNU General Public License --
15+
-- distributed with this software; see file COPYING3. If not, go --
16+
-- to http://www.gnu.org/licenses for a complete copy of the license. --
17+
------------------------------------------------------------------------------
18+
19+
with "aws";
20+
21+
project Big_Post is
22+
for Main use ("big_post.adb");
23+
end Big_Post;

regtests/0326_big_post/test.out

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
started
2+
Length payload: 16000000
3+
ok
4+
shutdown

regtests/0326_big_post/test.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from test_support import *
2+
3+
build_and_run('big_post');

src/core/aws-client.adb

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -678,17 +678,47 @@ package body AWS.Client is
678678
Attachments : Attachment_List := Empty_Attachment_List;
679679
Headers : Header_List := Empty_Header_List) is
680680
begin
681-
Internal_Post
682-
(Connection,
683-
Result,
684-
Translator.To_Stream_Element_Array (Data),
685-
URI,
686-
SOAPAction => No_Data,
687-
Content_Type => (if Content_Type = No_Data
688-
then MIME.Application_Form_Data
689-
else Content_Type),
690-
Attachments => Attachments,
691-
Headers => Headers);
681+
-- For message larger than 20kb the conversion to Stream_Element_Array
682+
-- may create a stack overflow.
683+
684+
if Data'Length > 20 * 1024 then
685+
declare
686+
SEA : Utils.Stream_Element_Array_Access :=
687+
Translator.To_Stream_Element_Array (Data);
688+
begin
689+
Internal_Post
690+
(Connection,
691+
Result,
692+
SEA.all,
693+
URI,
694+
SOAPAction => No_Data,
695+
Content_Type => (if Content_Type = No_Data
696+
then MIME.Application_Form_Data
697+
else Content_Type),
698+
Attachments => Attachments,
699+
Headers => Headers);
700+
701+
Utils.Unchecked_Free (SEA);
702+
703+
exception
704+
when others =>
705+
Utils.Unchecked_Free (SEA);
706+
raise;
707+
end;
708+
709+
else
710+
Internal_Post
711+
(Connection,
712+
Result,
713+
Translator.To_Stream_Element_Array (Data),
714+
URI,
715+
SOAPAction => No_Data,
716+
Content_Type => (if Content_Type = No_Data
717+
then MIME.Application_Form_Data
718+
else Content_Type),
719+
Attachments => Attachments,
720+
Headers => Headers);
721+
end if;
692722
end Post;
693723

694724
---------

src/core/aws-net.adb

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
------------------------------------------------------------------------------
22
-- Ada Web Server --
33
-- --
4-
-- Copyright (C) 2000-2019, AdaCore --
4+
-- Copyright (C) 2000-2020, AdaCore --
55
-- --
66
-- This library is free software; you can redistribute it and/or modify --
77
-- it under terms of the GNU General Public License as published by the --
@@ -442,10 +442,12 @@ package body AWS.Net is
442442
(Socket : Socket_Type'Class; Data : Stream_Element_Array)
443443
is
444444
use Ada.Real_Time;
445-
Save : constant Boolean := Socket.C.Can_Wait;
446-
First : Stream_Element_Offset := Data'First;
447-
Last : Stream_Element_Offset;
448-
Stamp : Time;
445+
Chunk_Size : constant := 100 * 1_024;
446+
Save : constant Boolean := Socket.C.Can_Wait;
447+
First : Stream_Element_Offset := Data'First;
448+
Chunk_Last : Stream_Element_Offset := Data'Last;
449+
Last : Stream_Element_Offset;
450+
Stamp : Time;
449451
begin
450452
Socket.C.Can_Wait := True;
451453

@@ -466,7 +468,10 @@ package body AWS.Net is
466468
end if;
467469

468470
loop
469-
Send (Socket, Data (First .. Data'Last), Last);
471+
Chunk_Last :=
472+
Stream_Element_Offset'Min (Data'Last, First + Chunk_Size - 1);
473+
474+
Send (Socket, Data (First .. Chunk_Last), Last);
470475

471476
exit when Last = Data'Last;
472477

0 commit comments

Comments
 (0)