-- This package 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. It 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 . -- -- Copyright (C) 2003-2022, Simon Wright -- Copyright (C) 2022, Stephane Carrez pragma Ada_2012; with Ada.Exceptions; with Ada.IO_Exceptions; with Ada.Streams; with GNAT.Sockets; private with Ada.Finalization; private with Ada.Strings.Maps; private with EWS.Reference_Counted_Pointers_G; package EWS.HTTP is -- Can't Elaborate_Body (body withs EWS.Static, EWS.Dynamic) -------------------------- -- Request management -- -------------------------- type Request is limited private; type Request_P is not null access all Request; procedure Initialize (R : out Request; From : GNAT.Sockets.Socket_Type; Terminated : out Boolean); subtype Method is String; -- GET|POST|HEAD|PUT|DELETE|OPTIONS|PATCH subtype Version is String; -- The HTTP version in use; 1.1 etc subtype URL is String; -- The file part of the request (eg, /index.html) subtype Property is String; -- The value of a parameter of the request (eg, in -- "/index.cgi?name=foo", "foo") -- -- or -- -- The value of a header field (eg, in "Content-Length: 1024", -- "1024") function Get_Method (From : Request) return Method; function Get_Version (From : Request) return Version; function Get_URL (From : Request) return URL; function Get_Property (Named : String; From : Request) return Property; -- Get the value of the named parameter of the query. function Get_Field (Named : String; From : Request) return Property; -- Get the value of the named header field of the query. function Keep_Alive_After_Responding (The_Request : Request) return Boolean; -- Returns True if the connection is to be left open after a -- normal completion (it's always closed after error). --------------------- -- Debug support -- --------------------- function Get_Head (From : Request) return String; function Get_Body (From : Request) return String; ------------------------------------- -- Content/attachment management -- ------------------------------------- type Attachments is private; -- The indexable collection of parts of a request body. In the -- case of a multipart message, as in file uploads, there will be -- several parts, the first (or only) part being index 1. function Get_Attachments (From : Request) return Attachments; procedure Clear (The_Attachments : in out Attachments); -- Attachments are organised using smart (reference counting) -- pointers. Use Clear to null out this particular reference after -- it's finished with. function Get_Field (Named : String; From : Attachments; Index : Positive := 1) return Property; -- Get the value of the named header field of the Index'th part of -- the attachments. -- String content ------------------ type Contents is access constant String; -- The body of an attachment, not including any request parameters -- or header fields. type Content_Kind is (Binary, Text); function Get_Content (From : Attachments; Index : Positive := 1) return Contents; -- Get the contents of the Index'th part of the attachment. function Get_Content_Kind (From : Contents) return Content_Kind; -- Text content ---------------- -- Cursors are an analogue of Ada.Text_IO.File_Type, allowing -- reading lines from attachments. type Cursor is limited private; -- The following exceptions are propagated when the appropriate -- conditions occur. Status_Error : exception renames Ada.IO_Exceptions.Status_Error; Name_Error : exception renames Ada.IO_Exceptions.Name_Error; End_Error : exception renames Ada.IO_Exceptions.End_Error; procedure Open (C : in out Cursor; From : Attachments; Index : Positive := 1); -- Open a Cursor on the Index'th part of the attachments. -- Propagates Status_Error if the Cursor is already open. -- Propagates Name_Error if Index doesn't denote a part of the -- attachments. procedure Open (C : in out Cursor; From : Contents); -- Open a Cursor on From. -- Propagates Status_Error if the Cursor is already open. procedure Close (C : in out Cursor); -- Close a Cursor. -- Propagates Status_Error if the Cursor is already closed. function End_Of_File (C : Cursor) return Boolean; -- Return True if the Cursor has reached the end of its attachment. -- Propagates Status_Error if the Cursor is closed. procedure Get_Line (C : in out Cursor; Line : out String; Last : out Natural); -- Obtain the next line from the Cursor's attachment. -- Propagates Status_Error if the Cursor is closed. -- Propagates End_Error if the Cursor is already at the end. -- XML content --------------- -- The unit Input_Sources.EWS_Attachments is provided for use with -- XMLAda to process an attachment as XML. -- -- In turn, it depends on the child unit -- EWS.HTTP.EWS_Attachments_Friend. --------------------------- -- Response management -- --------------------------- type Response (To : Request_P) is abstract tagged private; -- You do not need to override Content_Length and Write_Content -- provided you have overridden Content. function Response_Kind (This : Response) return String; -- default "200 OK" function Cacheable (This : Response) return Boolean; -- default True function Content_Type (This : Response) return String; -- default "text/plain" function Content_Length (This : Response) return Integer; -- default 0 function Content (This : Response) return String; -- default "". function Headers (This : Response) return String; -- Content-Type followed by Content-Length headers. procedure Write_Content (This : Response; To : not null access Ada.Streams.Root_Stream_Type'Class); procedure Respond (This : Response'Class; To : GNAT.Sockets.Socket_Type); -- Factory function to create a Response function Find (For_Request : not null access Request) return Response'Class; -- Utilities for standard abnormal responses function Not_Found (R : not null access Request) return Response'Class; function Not_Implemented (R : not null access Request) return Response'Class; function Exception_Response (E : Ada.Exceptions.Exception_Occurrence; R : access Request) return Response'Class; private -- Finalizable containment for strings used to hold a Request's -- head and content. type String_P is access String; package Smart_Strings is new Reference_Counted_Pointers_G (String, String_P); -- A Request isn't actually limited. type Request is record Head : Smart_Strings.Pointer; Content : Smart_Strings.Pointer; end record; type Attachments is new Request; procedure Locate_Whole_Body_Part (Within : Attachments; Index : Positive := 1; Start : out Positive; Finish : out Natural); -- Find the bounds in the Contents of Within of the Index'th -- part. The bounds include any leading properties. type Response (To : Request_P) is abstract tagged null record; type Line_Ending_Style is (Unknown, Unterminated, Unix, Windows); type Cursor is limited record Open : Boolean := False; Line_Ending : Line_Ending_Style := Unknown; Data : Contents; Start : Positive; Last : Natural; Next : Positive; end record; function Index (Source : String; Pattern : String; Going : Ada.Strings.Direction := Ada.Strings.Forward; Mapping : Ada.Strings.Maps.Character_Mapping := Ada.Strings.Maps.Identity) return Natural; -- A replacement for the GNAT Ada05 Ada.Strings.Search.Index. -------------------------------- -- Unbounded Memory Streams -- -------------------------------- -- These are Streams held in memory. -- -- The stream contents are held in chunks, allocated as required -- on Write. -- -- There has to be a Read procedure, of course, but it's not -- intended to be used and will propagate Program_Error if called. -- -- A Copy procedure is provided to copy the contents of the stream -- to a socket. Copy uses GNAT.Sockets.Send_Vector to send all the -- chunks to the socket in one call. -- -- The chunks are freed when the Stream is finalized. subtype Stream_Chunk_Elements is Ada.Streams.Stream_Element_Array (0 .. 511); type Stream_Chunk; type Stream_Chunk_P is access Stream_Chunk; type Stream_Chunk is record Next : Stream_Chunk_P; Elements : aliased Stream_Chunk_Elements; end record; type Unbounded_Memory_Stream; type Unbounded_Memory_Stream_Finalizer (UMS : not null access Unbounded_Memory_Stream) is new Ada.Finalization.Limited_Controlled with null record; overriding procedure Finalize (UMSF : in out Unbounded_Memory_Stream_Finalizer); type Unbounded_Memory_Stream is new Ada.Streams.Root_Stream_Type with record Finalizer : Unbounded_Memory_Stream_Finalizer (Unbounded_Memory_Stream'Access); Length : Ada.Streams.Stream_Element_Offset := 0; Head : Stream_Chunk_P; Tail : Stream_Chunk_P; end record; not overriding procedure Copy (Stream : Unbounded_Memory_Stream; To : GNAT.Sockets.Socket_Type); -- Read isn't meant to be called; output contents via Copy. overriding procedure Read (Stream : in out Unbounded_Memory_Stream; Item : out Ada.Streams.Stream_Element_Array; Last : out Ada.Streams.Stream_Element_Offset); overriding procedure Write (Stream : in out Unbounded_Memory_Stream; Item : Ada.Streams.Stream_Element_Array); end EWS.HTTP;