package body handlers is task body handler is socket : Socket_Type; sel : access Gnat.Sockets.Selector_Type := new Gnat.Sockets.Selector_Type; working : Boolean := True; subtype Socket_Index is Integer range 1 .. 1000; root_path : constant String := "/var/www"; package Socket_Vector is new Ada.Containers.Vectors ( Element_Type => Socket_Type, Index_Type => Socket_Index ); v : Socket_Vector.Vector; task watcher is entry start; end watcher; task body watcher is R : Socket_Set_Type; W : Socket_Set_Type; E : Socket_Set_Type; status : Selector_Status; active : Socket_Type; unparsed: Unbounded_String; begin accept start; Put_Line("start watcher"); Create_Selector (sel.all); loop Put_Line("Watch loop start"); Empty(R); Empty(W); Empty(E); Put("There is "); Put(v.Last_Index, Width => 0); Put_Line(" sockets to watch"); Put_Line("setting to read sockets"); for id in 1..v.Last_Index loop set(R, v.Element(id)); end loop; Put_Line("start of check_selector"); Check_Selector( sel.all, R, W, E, status, Gnat.Sockets.Forever); Put_Line("end of check_selector"); -- wychodzimy jedynie wtedy, gdy zmienna working -- jest False. Inaczej to jedynie przeładowanie -- socketów np. dodano nam socket do obserwacji exit when status = Gnat.Sockets.Aborted and not working; if status /= Gnat.Sockets.Aborted then Put_Line("Sending command to client handler"); Get(R, active); Handler.command(active); end if; Put_Line("Watch loop end"); end loop; Close_Selector (sel.all); Put_Line("exit watcher"); end watcher; procedure Handle_File(s: Socket_Type; path: String; finished: out Boolean) is sz : Natural := Natural(Size(path)); subtype Content_Type is String (1 .. sz); package dio is new Ada.Direct_IO(Content_Type); File : dio.File_Type; contents : Content_Type; channel : Stream_Access := Stream(s); begin Put_Line("Handle_File start"); Put_Line("Path: " & path); Put_Line("File size: " & Natural'Image(sz)); dio.Open( File => File, Mode => dio.In_File, Name => path); Put_Line("File opened"); String'Write(channel, "HTTP/1.0 200 OK" & newline); String'Write(channel, "Server: Ada Server 0.1" & newline); Put_Line("Root path: " & root_path & path); Put_Line("File size: " & Natural'Image(sz)); dio.Read (File, Item => contents); Put_Line("Contents: " & contents & newline); String'Write(channel, newline); String'Write(channel, contents); dio.Close(File); Put_Line("Closed file"); Close_Socket(s); Put_Line("Closed socket"); finished := True; end Handle_File; procedure Handle_Socket(s: Socket_Type; finished: out Boolean) is subtype Line_Type is String (1 .. 4096); channel : Stream_Access := Stream (s); line : Line_Type; elem_line : Stream_Element_Array (1 .. 4096); last : Stream_Element_Offset; package af renames Ada.Strings.Fixed; function Convert is new Ada.Unchecked_Conversion ( Source => Stream_Element_Array, Target => Line_Type ); begin Put_Line("Got command to execute"); channel := Stream (s); Receive_Socket(s, elem_line, last); line := Convert(elem_line); Put("Odczytano: "); Put(Integer(last), Width => 0); Put_Line(" znakow"); if last >= 4 and line (1 .. 4) = "GET " then Put_Line("GET Command: " & line); declare EOL : Integer := af.Index(line, newline); HTTP_Version_Offset : Integer := af.Index(line, "HTTP/1."); path : String := line (5..HTTP_Version_Offset - 2); begin if Exists (root_path & path) then Put_Line("Kind: " & File_Kind'Image(Kind(root_path & path))); if Kind(root_path & path) = Ordinary_File then Put_Line("Full path: '" & root_path & path & "'"); handle_file(s, root_path & path, finished); return; else -- pewnie katalog if Exists(root_path & path & "index.html") then handle_file(s, root_path & path & "index.html", finished); return; end if; if Exists(root_path & path & "index.htm") then handle_file(s, root_path & path & "index.htm", finished); return; end if; String'Write(channel, "HTTP/1.0 200 OK" & newline); String'Write(channel, "Server: Ada Server 0.1" & newline & newline); String'Write(channel, "" & "directory" & newline); end if; else String'Write (channel, "HTTP/1.0 404 NOT FOUND" & newline); String'Write (channel, "Server: Ada Server 0.1" & newline & newline); String'Write (channel, "" & "404 Error"); end if; end; else String'Write(channel, "Unknown" & newline); end if; Put_Line("Closing socket"); Close_Socket(s); finished := True; end Handle_Socket; idx : Integer; begin accept start(id: Integer) do idx := id; end start; Watcher.Start; Put("start client handler with id: "); Put(idx, Width => 0); New_Line; loop select accept handle(s: Socket_Type) do socket := s; v.Append(s); end handle; Abort_Selector(sel.all); Put_Line("Socket consumed by client handler"); or accept command(s: Socket_Type) do declare finished: Boolean; begin Handle_Socket(s, finished); if finished then v.Delete(v.Find_Index(s)); end if; end; end command; or accept stop do Put_Line("Got command to stop"); working := False; Abort_Selector(sel.all); end stop; exit; or terminate; end select; end loop; Put_Line("end client handler"); end handler; end handlers;