Since version 6.d of Perl 6, you can use port 0 to ask the interpreter to find a port to bind for you:
my $socket = IO::Socket::Async.listen($SOCKET_ADDR, 0);
However, $socket is a Supply with no information on the low-level socket it's using. What is the way of finding which port is it binding to?
When you tap the $socket variable you get back a (currently undocumented) IO::Socket::Async::ListenSocket object. This has a couple of methods socket-port and socket-host which are Promises then when they resolve have the correct values.
We can probably tidy up the docs to indicate this.
Example :
my $s = IO::Socket::Async.listen("127.0.0.1",0);
my $t = $s.tap;
my $p = await $t.socket-port;
$p.say;
Related
I have a variable as BASE_URL. When I am using the localhost, I am composing it with one more variable PORT that reflects the port on which the service is running on my local. These two variables composed as {{BASE_URL}}:{{PORT}} defines the complete base URL for my APIs.
But when testing the deployed version of my API, my base URL is just https://www.xyzapi.com without any port declared explicitly.
I am using {{BASE_URL}}/rule-service/v1/find-by-txn format in the request URL. I am using environments to switch between local and remote.
How can I utilize the same request format for both cases? I have multiple microservices running on different ports.
This code did the job!
let baseUrl = pm.environment.get("BASE_URL");
if(baseUrl.includes('localhost')){
let port = pm.collectionVariables.get("PORT");
baseUrl = baseUrl.split(':')[0];
baseUrl = `${baseUrl}:${port}`;
}
pm.environment.set("BASE_URL", baseUrl);
Try this. It introduces a variabe FINAL_BASE_URL which is to be used to build your URLs (eg {{FINAL_BASE_URL}}/rule-service/v1/find-by-txn) and it includes the PORT or not, depending if an environment variable PORT is set.
if (pm.environment.get("PORT")) {
let FINAL_BASE_URL = pm.environment.get("BASE_URL") + ":" + pm.environment.get("PORT")
} else {
let FINAL_BASE_URL = pm.environment.get("BASE_URL")
}
Created a websocket server with "cro sub".
Wrote this client:
use v6;
use Cro::WebSocket::Client;
constant WS-PORT = '20000';
constant WS-ADDRESS = 'localhost';
constant WS-PATH = 'chat';
constant WS-URL = 'ws://' ~ WS-ADDRESS ~ ':' ~ WS-PORT ~ '/' ~ WS-PATH;
constant TIMEOUT-TO-CONNECT = 5; # seconds
my $timeout;
my $connection-attempt;
await Promise.anyof(
$connection-attempt = Cro::WebSocket::Client.connect(WS-URL),
$timeout = Promise.in(TIMEOUT-TO-CONNECT));
if $timeout.status == Kept
{
say "* could not connect to server in ', TIMEOUT-TO-CONNECT, ' seconds";
exit 1;
}
if $connection-attempt.status != Kept
{
say "* error ", $connection-attempt.cause,
" when trying to connect to server";
exit 1;
}
my $connection = $connection-attempt.result;
my $peer = WS-ADDRESS ~ ':' ~ WS-PORT;
say '* connected with ', $peer;
my $counter = 0;
my $message-supplier = Supplier::Preserving.new;
my $has-message-to-send = $message-supplier.Supply;
$message-supplier.emit(1);
react
{
whenever $has-message-to-send
{
$counter++;
$connection.send($counter);
say "* ok, sent message ", $counter, " to server";
}
whenever $connection.messages -> $reply
{
say '* received reply=[' ~ $reply ~ '] from server';
$message-supplier.emit(1);
}
} # react
I see with tcpdump the response code 101 (switching protocols) from the server, but I don't see the message sent from the client to the server.
So, what am I doing wrong ?
Another question, shoudn't "$connection.send" return a Promise or something ? What if there's an error when sending ?
And another question: it seems the server only understands IPV6 addresses...how to make it understand IPV4 addresses ?
That's it, for now.
UPDATE
As per Takao's advice, changing
$connection.send($counter)
to
$connection.send($counter.Str)
solves the problem (though I tried it on another program, not this one).
Let's resolve this piece by piece.
Firstly, your code looks correct to me, except for a couple of tiny bits.
When I reproduced your code, it indeed did not work, so I tried it with cro trace . instead of cro run .. You can find info about that mode in official docs.
The alternative way is to just set CRO_TRACE=1 environment variable.
So during debug I saw this error:
[TRACE(anon 1)] Cro::HTTP::ResponseParser QUIT No applicable body serializer could be found for this message
As it says, the body you sent could not be serialized. So I looked into what are you sending: $counter. $counter in your code is Int, so we need to make it Str before, doing simple $counter.Str makes your example work.
Also, note that you are sending a message on every reply, and echo server (default one you created using cro stub) also sends a reply for every incoming message, so your example sends messages endlessly. To prevent that you may consider adding a condition under which you will no longer send things, but well, it is a test example anyway, so up to you.
As for your other questions:
Another question, shoudn't "$connection.send" return a Promise or something?
It should not, I'll write out some cro's architecture details to explain it next. As you may know from reading docs, cro pipeline is basically just a bunch of Cro::Transform-wrapped supplies. Inside of Cro::Websocket::Client::Connection, send method just sends a thing directly into Cro::Source of the whole pipeline, you cannot go wrong with a simple $supplier.emit($message)(the real implementation of this method looks very close to this line). The thing you bumped into occurred further in the pipeline. I am sure it is not a nice user experience to hide exceptions of such cases, so I'll consider making a patch to propagate the exception, so it'd be easier to catch(although you always can use debug mode).
it seems the server only understands IPV6 addresses...how to make it understand IPV4 addresses ?
I am not sure about that, please open a new question.
When trying to run the following in Redis using booksleeve.
using (var conn = new RedisConnection(server, port, -1, password))
{
var result = conn.Server.FlushDb(0);
result.Wait();
}
I get an error saying:
This command is not available unless the connection is created with
admin-commands enabled"
I am not sure how do i execute commands as admin? Do I need to create an a/c in db with admin access and login with that?
Updated answer for StackExchange.Redis:
var conn = ConnectionMultiplexer.Connect("localhost,allowAdmin=true");
Note also that the object created here should be created once per application and shared as a global singleton, per Marc:
Because the ConnectionMultiplexer does a lot, it is designed to be
shared and reused between callers. You should not create a
ConnectionMultiplexer per operation. It is fully thread-safe and ready
for this usage.
Basically, the dangerous commands that you don't need in routine operations, but which can cause lots of problems if used inappropriately (i.e. the equivalent of drop database in tsql, since your example is FlushDb) are protected by a "yes, I meant to do that..." flag:
using (var conn = new RedisConnection(server, port, -1, password,
allowAdmin: true)) <==== here
I will improve the error message to make this very clear and explicit.
You can also set this in C# when you're creating your multiplexer - set AllowAdmin = true
private ConnectionMultiplexer GetConnectionMultiplexer()
{
var options = ConfigurationOptions.Parse("localhost:6379");
options.ConnectRetry = 5;
options.AllowAdmin = true;
return ConnectionMultiplexer.Connect(options);
}
For those who like me faced the error:
StackExchange.Redis.RedisCommandException: This operation is not
available unless admin mode is enabled: ROLE
after upgrading StackExchange.Redis to version 2.2.4 with Sentinel connection: it's a known bug, the workaround was either to downgrade the client back or to add allowAdmin=true to the connection string and wait for the fix.
Starting from 2.2.50 public release the issue is fixed.
I have mod_perl2 running on a virtual host and I'm trying to make my mysql connection persistent between requests to handle server load. I have read all of the documentation and a book on the topic and I still have no idea why this bare-bones implementation of a mod_perl2 web application replies with "It's broken!".
package Test;
use strict;
use warnings;
use Apache2::Const;
use Carp qw{croak};
use DBI;
our $mysql_handle;
sub handler {
print "Content-Type: text/plain\n\n";
print (defined $mysql_handle ? "It's defined!" : "It's broken!");
return Apache2::Const::OK;
}
sub child_init {
my ($db, $host, $port, $user, $pass)
= qw{app_db localhost 3306 app_user app_pass};
$mysql_handle
= DBI->connect("dbi:mysql:database=$db;host=$host;port=$port", $user, $pass)
or croak("Failed to establish a connection with mysqld: $DBI::errstr");
return Apache2::Const::OK;
}
1;
This is very strange and makes no sense at all to me. It's as if $mysql_handle is lexically-scoped -- when it's not! Please, can some one explain this to me?
You should look at Apache::DBI for mysql connection persistance in mod_perl. It overloads DBI's connect and disconnect which allows you to use DBI->connect(...) normally, with the added benefit of the code working in or out of a mod perl environment.
As far as the scoping issue, I'd need a little more feedback on your mp setup. I would try use vars '$mysql_handle' or even $Test::mysql_handle = DBI->connect(...) and see if you don't get the results you are looking for.
I have an app on my iPhone called iSeismometer which reads the iPhone's accelerometers and acts as a server which streams this data via UDP (I can set the IP address and port number). The question is how to read this data stream with Mathematica? Apparently, Dreeves has been looking into this 12 years ago, so I imagine something must have happened in the meantime.
Update
I got two great answers so far; one from WReach and one from Mark McClure. Both are using JLink to get at the data. This seems like a fine approach. However, I was reminded of some work I did on the WII balance board. Using a few free programs (GlovePIE and PPJoy) I got this bluetooth peripheral to appear as a joystick to Windows, and therefore also to Mathematica (via ControllerState). Of course, bluetooth and UDP are quite different, but could something along the same lines be made to work too?
JLink is definitely the way to go. I prefer to keep my Java code and my Mathematica code separate by compiling a Java programwhich I then call from Mathematica. I set up a Notebook and companion Java program that you can grab here:
http://facstaff.unca.edu/mcmcclur/UDPFiles.tar.gz
Here is the essential Mathematica code:
Needs["JLink`"];
InstallJava[];
AddToClassPath[NotebookDirectory[]];
udpReader = JavaNew["myClient"];
i = 0;
While[True && i++ < 100,
Print[udpReader#udpReadOne[10552]]]
The updReader class is defined by the following Java code.
// A simple UDP client to read from iseismometer:
// http://www.iseismometer.com/
// You can run this from the command line via "java myClient"
// to check that your iseismometer setup is correct or you can
// call the the udpReadOne method from another program.
import java.io.*;
import java.net.*;
import java.util.*;
public class myClient {
public static void main() throws IOException {
DatagramSocket socket = new DatagramSocket(10552);
byte[] buffer = new byte[500];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
while(true) {
socket.receive(packet);
String received = new String(packet.getData(), 0, packet.getLength());
System.out.println(received);
}
}
public static String udpReadOne(int port) throws IOException {
DatagramSocket socket = new DatagramSocket(port);
byte[] buffer = new byte[100];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
socket.receive(packet);
String received = new String(packet.getData(), 0, packet.getLength());
socket.close();
return received;
}
}
Note that you can use the main method of the myClient class to check that your setup is working without Mathematica, essentially taking one potential issue out of the loop.
Assuming the set-up discussed in a blog entry on the iSeismometer web site, a couple of options come to mind.
Import
The first option would be to use an external program to capture the packets, and then use Import to bring in the results, e.g.
Import["!someexternalprog", "Lines"]
Alas, the Python program mentioned in the blog post will not work well here since it runs in an endless loop that must be manually terminated. The Import approach would only work if that program were modified to stop after a fixed number of packets or a time limit or something.
JLink
An alternate approach can be implemented without leaving the comfy Mathematica environment by using JLink. Well, perhaps it is a stretch to say that we are staying within Mathematica since a fair amount of funny-looking Java code is mixed in with the Mathematica code. Nevertheless, it does illustrate the utility of the built-in Java distribution that ships with every copy of Mathematica:
Needs["JLink`"]
LoadJavaClass["java.util.Arrays"];
ClearAll#ListenToISeismometer
ListenToISeismometer[port_] :=
JavaBlock#Module[{socket, packet, listen, record = Null, listening = True}
, packet = JavaNew["java.net.DatagramPacket", JavaNew["[B", 1024], 1024]
; listen[] :=
If[$Failed =!= Quiet[socket#receive[packet], Java::excptn]
, record =
JavaNew[
"java.lang.String"
, java`util`Arrays`copyOfRange ## packet /# {getData[], getOffset[], getLength[]}
]#toString[] // Sow
]
; Row[{Button["Stop", listening = False], Dynamic[record]}, " "] // PrintTemporary
; AbortProtect[
socket = JavaNew["java.net.DatagramSocket", port]
; socket#setSoTimeout[1000]
; Reap[While[listening, listen[]]; socket#close[]][[2, 1]]
]
]
Some shortcuts have been taken with respect to exception handling, packet decoding and the like in order to keep this example at a manageable length.
ListenToISeismometer needs to be given the UDP port number to listen upon. Let's use the same port as in the blog post, 10552:
In[33]:= data = ListenToISeismometer[10552];
The function will listen to all UDP events on that port until told to stop. A button is presented for this purpose, with each packet flashing by along side as received.
When the button is pressed, the function returns a list of the packets received:
In[34]:= data // Column
Out[34]= 1,83575.099,0.029,0.044,0.094
1,83575.781,0.056,0.033,0.099
1,83575.924,0.047,0.054,0.094
1,83575.613,0.096,0.092,0.057
1,83575.748,0.073,0.049,0.061
1,83575.577,0.008,0.089,0.020
...
JLink makes this possible, but there is no escaping the fact that the use of JLink requires a working knowledge of Java.