http://qs1969.pair.com?node_id=11134663

brachism has asked for the wisdom of the Perl Monks concerning the following question:

Probably simple, but I have just started dabbling in Perl. Working on a personal project for the home, I am trying to find a simple, quick, and lightweight method of integrating HTTP input into one of my projects. Most of my similar efforts have utilized NodeMCU’s. Years of using these seem to be clouding my judgement for an effective solution in Perl, a language I am learning.

My existing host’s core code is essentially an endless loop managing a series of host events. The client should be able to send occasional requests to either ‘set something’ or ‘inquire about something.’ The host responses in simple text, either 'ERR', 'OK' or with a short string. The reply to the client will be almost immediate, as the requests just set or return variables. Growing up with dial-up modems, my commands are AT-styled.

For example, to start execution, the follow client request results in $runflag=1
http://host:8080/CMD=ATES

I’ve looked at a few packages, such as HTTP::Server::Simple::CGI. While trying to incorporate the associated examples into my code, all block my main program while waiting for a client. The solution I need, is one where I can frequently poll for a client request. My main loop fire about 5 times a second.

Other notes:
- Single client, via HTTP (no SSL), on local network, no authentication
- Client send a command and wait for a response. E.g. http://ava/cmd=ATEP# (0=pause,1=un-pause)
- Host returns a simple text reply “OK”, “ERR”, or a short line of text
- Once reply sent, communication is done; until if/when client send another request
- Response timing is not critical (as in seconds, not milliseconds)

# DEVICE (HOST) – PSEUDO-CODE httpd_init(8081); while (1) { # Manage a request if ($client.request) { # Parse and validate, if invalid client.send('ERR'); # If 'set something': set variables; client.send('OK'); # Elsif a ‘request something': return client(short_string); # clear/cleanup//connection done } # Main program – do things … # Be nice my_sleep(250); }
Any suggestions? Thanks in advance.

Replies are listed 'Best First'.
Re: Adding simple HTTP controls to existing code
by Corion (Patriarch) on Jul 05, 2021 at 11:40 UTC

    For example, the two web frameworks Mojolicious and Dancer (or Dancer2), or maybe even the raw Plack interface allow you to do that, and they each come with a built-in web server as well.

    Your program could look like the following, adapted from the Mojolicious::Lite synopsis:

    # Automatically enables "strict", "warnings", "utf8" and Perl 5.16 f +eatures use Mojolicious::Lite -signatures; # Route with placeholder get '/' => sub ($c) { my $foo = $c->param('foo'); $c->render(text => "Hello from $foo."); run_main_program(); }; # Start the Mojolicious command system app->start;
      Thanks, I will look those up, though I’d hoped to avoid adding dependencies on any large packages or frameworks for something seemingly trivial. As my core code repeats multiple times a second, I wonder if it might be easier just to open a socket, and managed it directly, polling for data, and deal with traffic. Thanks again.
        Then Mojo is your thing. It only has two dependencies, both of which are Core.


        holli

        You can lead your users to water, but alas, you cannot drown them.
Re: Adding simple HTTP controls to existing code
by bliako (Monsignor) on Jul 05, 2021 at 12:09 UTC

    Are you looking for modules enabling asynchronous programming? For example, Net::Async::HTTP. Or plain-and-simple: Net::Curl::Simple which basically fetches a url and when is done it executes a callback.

      Oversimplifying my use case, imagine a Perl program running on a headless Linux computer. A simple Jukebox playing random tracks from a play list, managed by parameters, endlessly running. Occasionally it also periodically plays announcements, such as song metadata, commercials, weather, etc between tracks.

      I want to be able to send simple HTTP commands to the headless server to, for example, change playlists, advance to next, repeat current, reshuffle, or manage the frequency of announcements, etc.

      These commands from a web browser (wget, curl, etc) via cell phone, computer, etc would take the form of http://host:port/CMD=XXXX. Where XXXX might be ATPN = play-next, ATRC = repeat-current; ATLP3 = load playlist #3; ATPP = pause playback; ATRP = resume playback, ATATF60 = adjust time announcement frequency to 60 minutes, etc. These command generally just modify variable in the host.

      I do this often on Arduino/NodeMCU’s using the ESP8266WiFi.h library. In setup() I initialize the server. In loop() I simply check, validate, process, and reply to any client request each iteration.

        I misunderstood your use-case. You need something which listens+possibly requests, asynchronously and not something which does just requests asynchronously. So, above will be useful in replying to your clients.

Re: Adding simple HTTP controls to existing code
by Bod (Parson) on Jul 05, 2021 at 20:54 UTC

    For my Controlling USB on Raspberry Pi project I set up a tunnel using ngrok and had an HTTP listener on the Raspberry Pi using HTTP::Server::Simple. It works very well...

    Here is the bit that handles the HTTP requests...

    package Bod::Curtains::Server; VERSION = 1.0 use HTTP::Server::Simple::CGI; use base qw(HTTP::Server::Simple::CGI); use Bod::Curtains::Control; use HTTP::Tiny; use Sys::Hostname; use strict; my $control = Bod::Curtains::Control->new; my $host = hostname; sub handle_request { my ($self, $request) = @_; print "HTTP/1.0 200 OK\r\n"; print $request->header; print "SUCCESS\n"; }