{"id":20315247,"url":"https://github.com/sysread/ion","last_synced_at":"2025-09-12T00:13:18.504Z","repository":{"id":56836662,"uuid":"112376201","full_name":"sysread/Ion","owner":"sysread","description":"A clear and concise API for writing TCP servers and clients in Perl","archived":false,"fork":false,"pushed_at":"2018-09-06T19:30:36.000Z","size":99,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-02-24T05:58:44.855Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Perl","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sysread.png","metadata":{"files":{"readme":"README.pod","changelog":"Changes","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-11-28T18:57:19.000Z","updated_at":"2018-09-06T19:30:37.000Z","dependencies_parsed_at":"2022-08-29T07:40:49.111Z","dependency_job_id":null,"html_url":"https://github.com/sysread/Ion","commit_stats":null,"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sysread%2FIon","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sysread%2FIon/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sysread%2FIon/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sysread%2FIon/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sysread","download_url":"https://codeload.github.com/sysread/Ion/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241818872,"owners_count":20025210,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-14T18:18:30.819Z","updated_at":"2025-03-04T08:46:08.376Z","avatar_url":"https://github.com/sysread.png","language":"Perl","funding_links":[],"categories":[],"sub_categories":[],"readme":"=pod\n\n=encoding UTF-8\n\n=head1 NAME\n\nIon - A clear and concise API for writing TCP servers and clients\n\n=head1 VERSION\n\nversion 0.08\n\n=head1 SYNOPSIS\n\n  use Ion;\n\n  # A simple echo server\n  my $echo = Listen 7777;\n\n  while (my $conn = \u003c$echo\u003e) {\n      while (my $line = \u003c$conn\u003e) {\n          $conn-\u003e(\"you said: $line\");\n      }\n  }\n\n\n  # Or separate the protocol from the listener and let Ion handle the rest\n  Service { return \"you said: $_[0]\" } $echo;\n\n\n  # Client connections\n  my $conn = Connect localhost =\u003e 7777; # Connect to the server\n  $conn-\u003e('hello world');               # Send a message\n  my $reply = \u003c$conn\u003e;                  # Get it back\n\n=head1 DESCRIPTION\n\nIon is intended as an easy to use, concise interface for building TCP\nnetworking applications in Perl using L\u003cCoro\u003e. Although it is not strictly\nnecessary, it is recommended that one have at least a passing familiarity with\nL\u003cCoro\u003e when building services with this module.\n\n=head1 EXPORTED SUBROUTINES\n\n=head2 Listen\n\nC\u003cListen\u003e builds and initializes a TCP listening socket, returning an\nL\u003cIon::Server\u003e.\n\nIncoming L\u003cconnections|Ion::Conn\u003e are accepted using the readline operator\n(\u003c\u003e). This will cede to the event loop until a new connection is ready.\n\nIncoming data from the client connection is read similarly. The client is\noverloaded to send response data when called as a function.\n\nBecause each of these operations has the potential to cede control to the event\nloop, it is recommended that the client response loop be called using\nL\u003casync|Coro\u003e. That allows other incoming connections to be accepted without\nwaiting for the original client to disconnect.\n\n  my $server = Listen 1234, 'localhost';\n\n  while (my $conn = \u003c$server\u003e) {        # cedes until $conn is ready\n      async {\n          while (my $line = \u003c$conn\u003e) {  # cedes until $line is ready\n              $conn-\u003e(do_something_with($line));\n          }\n      };\n  }\n\nThe port number and host interface are optional. If left undefined, these will\nbe assigned by the operating system and are accessible via the C\u003cport\u003e and\nC\u003chost\u003e methods of the server.\n\n  my $server = Listen;\n  my $port   = $server-\u003eport;\n  my $host   = $server-\u003ehost;\n\n=head2 Service\n\nBegins serving requests on the specified listening service by calling the\nsupplied code block for each line received from a client connection. The\nservice may be specified as the server object itself (the return value of\nL\u003c/Listen\u003e) or by passing the port and host name, which are passed unchanged to\nL\u003c/Listen\u003e.\n\nThe handler block will be called for each incoming line of data and\nadditionally is passed the L\u003cclient connection|Ion::Conn\u003e and the\nL\u003cserver|Ion::Server\u003e objects.\n\n  Service {\n      my ($line, $conn, $server) = @_;\n      return do_something_with_line($line);\n  } 7777, '127.0.0.1';\n\nThe return value of the handler function is then returned to the client. If the\nhandler function returns a false value, the client will be disconnected.\nAlternately, the handler function may return any defined, false value (e.g., 0)\nto retain the client connection but handle the response itself.\n\n  Service {\n      my ($line, $conn, $server) = @_;\n      $conn-\u003e(do_something_with_line($line));\n      return 0;\n  };\n\n=head2 Connect\n\nOpens a connection to a remote host and returns an L\u003cIon::Conn\u003e object. The\nconnection object is overloaded to send data when called as a function and read\nthe next line of data using the readline operator (\u003c\u003e). The connection is\nsafely closed by calling the C\u003cclose\u003e method.\n\n  my $conn = Connect 'localhost', 7777;\n  $conn-\u003e('ping');\n  my $pong = \u003c$conn\u003e;\n  $conn-\u003eclose;\n\nThe readline operator will block until a complete message arrives. To continue\nthread execution while waiting for the message, use an L\u003casync|Coro\u003e block and\nC\u003cjoin\u003e with it later.\n\n  my $pending  = async { \u003c$conn\u003e };\n  my $response = $pending-\u003ejoin;\n\nC\u003cConnect\u003e may also be used to build an L\u003cIon::Conn\u003e out of an existing\nL\u003cCoro::Handle\u003e. The handle may then be used like any other Ion connection.\nThis is particularly useful for serializing data across a non-blocking pipe.\n\n  use Coro::Handle 'unblock';\n  use AnyEvent::Util 'portable_pipe';\n  use JSON::XS qw(encode_json decode_json);\n  use Ion;\n\n  my ($r, $w) = portable_pipe;\n  my $in  = Connect(unblock($r)) \u003c\u003c \\\u0026decode_json;\n  my $out = Connect(unblock($w)) \u003e\u003e \\\u0026encode_json;\n\n=head1 MESSAGE FORMATS\n\nSending a raw line of text is sometimes desireable, but complex applications\nwill want to use an established protocol when transmitting complex data. Rather\nthan fill request handling logic with the details of encoding and decoding data\nfor transmission, the routines used for translating structured data into line\ndata and vice versa may be specified using the \u003e\u003e and \u003c\u003c operators,\nrespectively.\n\nThe syntax is identical for both servers and client connections. When applied\nto a server instance, client connections accepted on the server side inherit\nthe server's configuration (note: the connecting client code will need to be\nsimilarly configured).\n\nMultiple routines may be chained together as a single expression or multiple\nstatements with the assignment version of each operator (\u003e\u003e= and \u003c\u003c=). When\nmore than one routine is specified, they will each be called in turn on the\nresult of the previous routine, with the first routine receiving the raw line\ndata.\n\n=head2 EXAMPLE: JSON\n\n  use Ion;\n  use JSON::XS;\n\n  my $server = Listen;\n  $server \u003c\u003c= sub{ decode_json(shift) };\n  $server \u003e\u003e= sub{ encode_json(shift) };\n\n  while (my $conn = \u003c$server\u003e) {\n    while (my $data = \u003c$conn\u003e) {              # $data is perl data\n      $conn-\u003e({foo =\u003e 'bar', baz =\u003e 'bat'});  # $conn is sent json: \"{'foo': 'bar', 'baz': 'bat'}\"\n    }\n  }\n\n=head2 EXAMPLE: CHAINING\n\n  use Ion;\n  use Data::Dumper;\n  use MIME::Base64 qw(encode_base64 decode_base64);\n\n  my $client = Connect somehost =\u003e 4242;\n\n  # Compound expression\n  $client \u003c\u003c sub{ decode_base64(shift) }                      # decode line format\n          \u003c\u003c sub{ my $msg = eval shift; $@ \u0026\u0026 die $@; $msg }; # eval perl string\n\n  # Individual statements\n  $client \u003e\u003e= sub{ Dumper(shift) };                           # serialize with Dumper\n  $client \u003e\u003e= sub{ encode_base64(shift, '') };                # single line of base64\n\n=head1 ENDLINES\n\nAs one would expect using the \u003c\u003e operator, the value of C\u003c$/\u003e controls the\ncharacter or string used to match the end of a line of input from the socket.\nIt is also appended to all output.\n\n  local $/ = \"\\n\\n\";\n  my $http_request = \u003c$conn\u003e;\n\n=head1 PRECEDENCE OF BITWISE OPERATORS\n\nThe bit shift operators overloaded to simplify data serialization and\ndeserialization have a higher precedence than the comma operator, which can\ncause unexpected problems.\n\nFor example, the following code:\n\n  my $client = Connect 'somehost', 4242\n    \u003c\u003c sub{ decode_json(shift) }\n    \u003e\u003e sub{ encode_json(shift) };\n\n...is equivalent to the parenthesized statement:\n\n  my $client = Connect 'somehost', ((4242\n    \u003c\u003c sub{ decode_json(shift) })\n    \u003e\u003e sub{ encode_json(shift) });\n\n...and would be more correctly written to avoid errors as:\n\n  my $client = Connect('somehost', 4242)\n    \u003c\u003c sub{ decode_json(shift) }\n    \u003e\u003e sub{ encode_json(shift) };\n\n=head1 AUTHOR\n\nJeff Ober \u003csysread@fastmail.fm\u003e\n\n=head1 COPYRIGHT AND LICENSE\n\nThis software is copyright (c) 2018 by Jeff Ober.\n\nThis is free software; you can redistribute it and/or modify it under\nthe same terms as the Perl 5 programming language system itself.\n\n=cut\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsysread%2Fion","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsysread%2Fion","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsysread%2Fion/lists"}