Package: simple_client_server
A server for applying a user-supplied function to data from clientsThis code makes it easy to write simple clients and servers for
retrieving information from server-side files, with further assistance
if those files are CSV formatted.  It defines a client and server pair
with a given port number, allowing use of both TCP and UDP, and reads
the data files for you, re-reading them if they have been modified
since the previous query.For now, it has no provision for writing modified data back to the
files, and it has encryption but only implicit authentication, and
there is no logging.  These may all change in the future.As I couldn't find all the information I needed for writing a very
simple client-server system all in one place, I've tried to make this
code suitable for use as an example or as a base for your own code.  I
hope it may be a useful example for:socketsencryptionthreadsCSV readingHowever, it's years since I previously did any network programming,
and this is my first venture into encryption and decryption, so until
I get some feedback from people with more experience of these areas,
it might not be very good example code.You can call the main functions of this code from your own program, or
use it as a pair of command-line programs in its own right (as
explained in the Examples section of this document).Languages noteI started writing this in Python, and that's the only mature version
for now.  I'm revising and learning other languages, and starting to
write versions in them, but that's in occasional moments rather than a
concentrated effort.  I won't be offended if you pick up on them and
teach me how to do them in other languages.How to use it from your codeThe main entry points are a function to run TCP and UDP servers,
applying a function you supply and with data files you specify:run_servers(host, port, getter, files)and a function for sending a query and getting a response:get_response(query, host, port, tcp=False)and a main-like function:client_server_main(getter, files)which sets upargparsefor the main options and then runs as either
a client or a server using the functions above.The data definitionThe argument mentioned asfilesabove is a dictionary binding
filenames to descriptions of how to read the files.  Each description
may be:a function taking two arguments, the name of a file to read and a
key (the key is not used); the function should return the data as
its result, in whatever form your query function wantsa tuple of a function and a key; the function is called with the
filename and the key; the function should return the data as
its result, in whatever form your query function wantsa string, which is passed to a function (provided by this package)
that usescsv.DictReaderto parse the file, and constructs a
dictionary binding the field of each row named by that string, to
the row as a wholean integer, which is passed to a function (provided by this
package) that usescsv.readerto parse the file, and constructs a
dictionary binding the column of each row indexed by that integer,
to the row as a wholea tuple of a string or an integer (treated as above) and a
function, which is applied to each row as it is read, and the
return value of which is stored in the resulting dictionary;
the indexing string or number refers to the modified row,
allowing indexing on a value constructed from the file data in
the case of the raw data not having a suitable field for
indexing it byThe user functionThe user-supplied function is called with two arguments:a string containing the querya dictionary binding the basenames of the filenames to the results
of the readers described above.It should return the string which is to be sent back to the client.Optional encryptionFor encryption, there are some further arguments you can supply torun_serversandget_response:run_servers(host, port, getter, files,
            query_key=None,
            reply_key=None)

get_response(query, host, port, tcp=False,
             encryption_scheme=ord('H'),
             representation_scheme=ord('a'),
             query_key=None, reply_key=None)Theencryption_schememay be:pNo encryption (sent as plaintext)HHybrid encryption, sent with base64 encodingIfencryption_schemeis notp,query_keyandreply_keyshould
be the results of calls toRSA.importKey, or equivalent.  The
functionsread_keyandread_keys_from_filesare provided to help
with this.Therepresentation_schememay be:'t' The input must be a text string, which is sent as such'j' The input must be encodable as JSON'p' The input can be any Python data that can be pickled'a' Chooses one of the above to suit the dataPrerequisitesIf you don't already have them, you should install (probably withpip3):pycryptopython-decoupleExamplesA very simple example is provided at the end of the source file, that
looks things up in a CSV file/var/local/demo/demo-main.csv, using
the first column as a key.  (A sample data file is provided in this
directory.)To run the example server, copydemo-main.csvinto place, and use:./client_server.py --server --query-key querykey --reply-key replykey.puband to run the client, use./client_server.py --query-key querykey.pub --reply-key replykey spinachThe program I started writing it as a wrapper for is for looking up
where I have stored things at home:https://github.com/hillwithsmallfields/coimealtaWhen I get round to learning to write Android apps, the idea is to
have a phone or tablet use this to ask my home server where something
is, and, if I've added a writeback facility, to record where I've put
things.DevelopmentI wrote this partly as an exercise for reminding myself about socket
programming, and then went on to use it for learning about using
Python's encryption libraries.  I may later use it as an example for
some future learning projects:Python's logging facilitiesDigital signatureI may also add compatible programs in other languages as I learn them (with Haskell and Clojure on my queue, and C and golang on my backlog).