stcarrez / ada-util

Ada Utility Library - Composing streams, processes, logs, serialization, encoders and more
Apache License 2.0
69 stars 14 forks source link

No HTTP manager is defined (even though one should be) #30

Closed ethindp closed 2 years ago

ethindp commented 2 years ago

@stcarrez So, I'm writing a parser for the Google API Discovery Service. I'm using CURL since I don't want to have to deal with AWS's license problems, and my program builds fine. However, when running it I get:

No HTTP manager was defined

raised PROGRAM_ERROR : No HTTP manager was defined.
[./bin/discovery-main]
0x55b8c9de1e8f Util.Http.Clients.Initialize at util-http-clients.adb:226
0x55b8c9dd703b Discovery.Parser at discovery-parser.adb:23
0x55b8c9db03c7 Adainit at b__discovery-main.adb:719
0x55b8c9db042b Main at b__discovery-main.adb:750
[/usr/lib/libc.so.6]
0x7f461489d28e
0x7f461489d348
[./bin/discovery-main]
0x55b8c9daf603 _start at start.S:115
0xfffffffffffffffe

I am calling Util.HTTP.Clients.CURL.Register at the top of my code:

with Discovery.Parser;
with Util.HTTP.Clients.Curl;
procedure Discovery.Main is
begin
   Util.HTTP.Clients.Curl.Register;
   Discovery.Parser.Parse_Discovery_Document;
end Discovery.Main;

Is this a bug?

ethindp commented 2 years ago

I should note that I'm using version 2.5.0 of the library as is in the Alire index.

stcarrez commented 2 years ago

I'm using curl for the same reasons :-) It works fine for me with. I've tried again with:

alr init --bin wget
cd wget
alr with utilada_curl
cp ./alire/cache/dependencies/utilada_2.5.0_f65f9ba9/samples/wget.adb src/
alr build
bin/wget https://www.adacore.com

How do you initialize the Util.Http.Clients.Client instance ?

The instance must be created after the call to Curl.Register.

ethindp commented 2 years ago

@stcarrez That's... Really really weird. My code is a bit long, but here you go (sorry about the lack of indentation, I can't seem to build libadalang-tools right now). The source code isn't exactly like my copy on disk but its close enough:

pragma Ada_2022;

with Util.HTTP.Clients;
with Util.HTTP.Clients.CURL;
with Util.Log.Loggers;
with JSON.Types;
with JSON.Parsers;
with ada.Containers; use Ada.Containers;
with Ada.Containers.Indefinite_Hashed_Maps;
with Ada.Assertions; use Ada.Assertions;
with Ada.Command_Line; use Ada.Command_Line;
with Ada.Strings.Fixed.Equal_Case_Insensitive;

package Discovery.Parser is
procedure Parse_Discovery_Document;
end Discovery.Parser;

package body Discovery.Parser is
package UHC renames Util.HTTP.Clients;
package ULL renames Util.Log.Loggers;
function Compute_Hash (Key: String) return Hash_Type;
package Types is new JSON.Types(Integer_Type => Long_Long_Long_Integer, Float_Type => Long_Long_Float);
use Types;
package Parsers is new JSON.Parsers (Types, 500, true);
package API_Maps is new Ada.Containers.Indefinite_Hashed_Maps(String, JSON_Value, Compute_Hash, Ada.Strings.Fixed.Equal_Case_Insensitive);
Logger: ULL.Logger := ULL.Create("Discovery");
Client: UHC.Client;
Response: UHC.Response;
Root: Types.JSON_Value := Create_Null;
APIs: API_Maps.Map;

procedure Parse_Discovery_Document is
begin
-- code
end Parse_Discovery_Document;

function Compute_Hash (Key: String) return Hash_Type is
hash: Hash_Type := 16#811c_9dc5#;
begin
for char of Key loop
hash := @ xor Character'Pos(char);
Hash := @ * 16#0100_0193#;
end loop;
return hash;
end Compute_Hash;

end Discovery.Parser;

And my main file that runs it all:

with Discovery.Parser;
with Util.HTTP.Clients.Curl;
procedure Discovery.Main is
begin
Util.HTTP.Clients.Curl.Register;
Discovery.Parser.Parse_Discovery_Document;
end Discovery.Main;

I just started it yesterday so it doesn't do all that I intend it to do for right now. I used JSON-Ada primarily because I think that utilada's serialization documentation needs a bit of improvement; I found it quite confusing and not all that clear on what I needed to do to read formats. I like it: it lets me strongly type serialized/deserialized data. But I think it needs better descriptions and steps (it refers to a Java library but I don't think people should have to know how that library works just to use the serialization system). Criticism aside, I think I'm doing the same as the wget example, but it still doesn't work for me for some reason.

stcarrez commented 2 years ago

Ok, your issue is that the following declaration:

Client: UHC.Client;

is at the package level and the Curl.Register is executed from the main, hence after the Client global is initialized. The type has an Initialize procedure that configures according to the default HTTP manager.

The best solution is to avoid declaring any UHC.Client instance at the global package level but within some procedure scope.

If you can't do that, you should probably move the Curl.Register in the body a package that your Discovery.Parser depends.

It could be the body of theDiscovery package or another package that the spec Discovery.Parser depends on. However, by doing so you loose the flexibility of choosing dynamically which HTTP manager you want to use.

I try to improve the documentation as well as the API of Ada Util but it takes time. I agree with you the deserialization is somehow difficult to use and needs improvement on both documentation and APIs.

You may also have a look at the OpenAPI Ada library which uses the Ada Util serialization/deserialization framework. Again, there is not much documentation but I try to make some progress here and there.

ethindp commented 2 years ago

@stcarrez Thanks, that worked. I'll look into swagger-ada since it looks like json-ada doesn't support unicode escapes and I need that for the discovery API (apparently).