I would like to use gRPC, which is an RPC framework made by google, to use ruby for the server and PHP for the client, and use API communication between different languages with a common I/F.
The fact that different languages are used is more like a conditional reflection on the reality that PHP cannot build a gRPC server, rather than wanting to enjoy the real pleasure of sharing I/F by IDL definition, which is the merit of gRPC. As a result, the real thrill will come, so I would like to taste it.
When using PHP for the client, there are many places where the server side is introduced with go, so I will use ruby here (yes, go do not know confirmed).
It's not necessary to write an overview here, so let's get started. Here, as an API, I will write that if you specify a domain, the expiration date will be fetched.
INPUT: FQDN OUTPUT: yyyy/mm/dd hh:ii:ss
API (gRPC): Prepare common I/F with defined I/O Client (PHP): Request FQDN from API through server Server (ruby): Checks the expiration date of the SSL certificate from the FQDN requested by the client and responds the date via API.
pecl install grpc
pecl install protobuf
vi (Abbreviation)/php.ini
extension=grpc.so
extension=protobuf.so
gem install grpc
gem install grpc-tools
mkdir ~/projects/grpc_trial/
cd ~/projects/grpc_trial/
composer require grpc/grpc
composer require google/protobuf
mkdir {protos,php,ruby}
vi protos/checkexpires.proto
(See below)
grpc_tools_ruby_protoc -I ./protos --ruby_out=./ruby --grpc_out=./ruby ./protos/checkexpires.proto
ls ruby/ | grep checkexpires
checkexpires_pb.rb
checkexpires_services_pb.rb
protoc --proto_path=./protos --php_out=./php --grpc_out=./php ./protos/checkexpires.proto --plugin=protoc-gen-grpc=/usr/local/bin/grpc_php_plugin
ls php/Checkexpires/
CheckReply.php CheckRequest.php GetSslClient.php
vi checkexpires.rb
(See below)
vi checkexpires.php
(See below)
ruby ./checkexpires.rb &
php ./checkexpires.php
php checkexpires.php example.com
2021/12/25 23:59:59
checkexpires.proto
syntax = "proto3";
package checkexpires;
import "google/protobuf/timestamp.proto";
service GetSsl {
rpc getExpire (CheckRequest) returns (CheckReply) {}
}
message CheckRequest {
string fqdn = 1;
}
message CheckReply {
google.protobuf.Timestamp timestamp = 1;
}
checkexpires.rb
this_dir = File.expand_path(File.dirname(__FILE__))
lib_dir = File.join(this_dir, 'ruby')
$LOAD_PATH.unshift(lib_dir) unless $LOAD_PATH.include?(lib_dir)
require 'time'
require 'grpc'
require 'checkexpires_services_pb'
class CheckexpiresServer < Checkexpires::GetSsl::Service
def get_expire(check_req, _unused_call)
end_at = `openssl s_client -connect #{check_req.fqdn}:443 </dev/null 2>/dev/null|openssl x509 -text | grep "Not After"`
end_at = end_at.split(' : ').pop
end_at = end_at.gsub("\n", '')
end_at = Time.parse(end_at).to_i
end_at = Time.at(end_at)
Checkexpires::CheckReply.new(timestamp: end_at)
end
end
def main
s = GRPC::RpcServer.new
s.add_http2_port('localhost:50051', :this_port_is_insecure)
s.handle(CheckexpiresServer)
s.run_till_terminated_or_interrupted([1, 'int', 'SIGQUIT'])
end
main
checkexpires.php
<?php
require dirname(__FILE__).'/vendor/autoload.php';
require dirname(__FILE__).'/php/Checkexpires/GetSslClient.php';
require dirname(__FILE__).'/php/Checkexpires/CheckReply.php';
require dirname(__FILE__).'/php/Checkexpires/CheckRequest.php';
require dirname(__FILE__).'/php/GPBMetadata/Checkexpires.php';
$server = 'localhost:50051'; //checkexpires.rb
if (is_null($argv[1] ?? null)) {
echo "need to input fqdn\n";
exit(1);
}
try {
$client = new Checkexpires\GetSslClient($server, [
'credentials' => Grpc\ChannelCredentials::createInsecure(),
]);
$request = new Checkexpires\CheckRequest();
$request->setFqdn($argv[1]);
list($reply, $status) = $client->getExpire($request)->wait();
if (($status->code ?? null) === 0) {
$ts = $reply->getTimestamp()->getSeconds();
echo date('Y/m/d H:i:s', $ts);
exit(0);
}
} catch (Exception $e) {
echo $e->getMessage();
exit(1);
}
Since the common I/F is defined in different languages, let's standardize each compiler as well.
vi ~/.bashrc
function protoc2() {
grpc_tools_ruby_protoc -I ./protos --ruby_out=./ruby --grpc_out=./ruby ./protos/$@ && protoc --proto_path=./protos --php_out=./php --grpc_out=./php ./protos/$@ --plugin=protoc-gen-grpc=/usr/local/bin/grpc_php_plugin
}
. ~/.bashrc
cd ~/projects/grpc_trial/
protoc2 checkexpires.proto
Now you have a common I/F for ruby and PHP in one proto file. After that, it feels like writing unique processing on the client side and server side respectively. I think I can do my best.
Recommended Posts