Compare commits
1 commit
| Author | SHA1 | Date | |
|---|---|---|---|
| 7ceee609b2 |
3 changed files with 13 additions and 156 deletions
55
README.md
55
README.md
|
|
@ -21,24 +21,18 @@ If you don't really now what a middleware is, be at ease, nobody really knows.
|
||||||
Nowadays, you may have eared of their latest avatar: *web services*.
|
Nowadays, you may have eared of their latest avatar: *web services*.
|
||||||
As our programmer is going to realize, one now have *two* problems.
|
As our programmer is going to realize, one now have *two* problems.
|
||||||
The burden of writing, using, and maintaining code using middleware is always huge.
|
The burden of writing, using, and maintaining code using middleware is always huge.
|
||||||
Because they are made to handle a **tremendous** number of features and complex situations
|
Because they are made to handle a **tremendous** number of complex situations,
|
||||||
—most of which involve adversary users, users being bots, or both.
|
most of which involve adversary users, users being bots, or both.
|
||||||
|
|
||||||
But most of the time, the actual problem does not really involve these situations.
|
But most of the time, the actual problem does not really involve these situations.
|
||||||
At least not at the beginning (… which means probably never).
|
At least not at the beginning (which means probably never).
|
||||||
People familiar with middleware history would argue that their key feature
|
If you are building up (firsts versions of) communicating programs
|
||||||
is not just *messages passing* but *remote call*, which involves *object serialization*.
|
|
||||||
But most of the time, the messages are pretty simple anyway, and using a middleware
|
|
||||||
to implement a serialization of a list of instances having three members of fundamental types
|
|
||||||
is not a good use of your time.
|
|
||||||
|
|
||||||
If you are building up (firsts versions of) two communicating programs
|
|
||||||
that will run on a (safe) local network,
|
that will run on a (safe) local network,
|
||||||
and for which the exchanged messages are known and simple,
|
and for which the exchanged messages are known,
|
||||||
then I have good news:
|
then I have good news:
|
||||||
**you don't have to use web services** (or any kind of middleware).
|
**you don't have to use web services** (or any kind of middleware).
|
||||||
|
|
||||||
**YOU JUST NEED TO KNOW HOW TO READ/WRITE FROM/TO (SPECIAL) FILES**.
|
**You just need to know how to read/write from/to (special) files**.
|
||||||
|
|
||||||
|
|
||||||
### Overview
|
### Overview
|
||||||
|
|
@ -55,7 +49,7 @@ in which you read/write.
|
||||||
|
|
||||||
Once you made your service on top of named pipes,
|
Once you made your service on top of named pipes,
|
||||||
it is easy to wrap it within an interface made with other languages/tools.
|
it is easy to wrap it within an interface made with other languages/tools.
|
||||||
For instance, it is very easy to expose it on the network using common tools like `socat` (see below).
|
For instance, it is very easy to expose it on the network using common tools like `socat`.
|
||||||
|
|
||||||
Be warned that this is not secure, though, you should only use this for testing
|
Be warned that this is not secure, though, you should only use this for testing
|
||||||
purpose in a secured local network.
|
purpose in a secured local network.
|
||||||
|
|
@ -73,13 +67,13 @@ The theoretical principle can be represented by this UML sequence diagram:
|
||||||
│ │ │ │
|
│ │ │ │
|
||||||
│ │ │┌──────╢
|
│ │ │┌──────╢
|
||||||
│ │ block││ wait ║
|
│ │ block││ wait ║
|
||||||
│ask │ │└─────>║
|
│ask │ │└─────→║
|
||||||
├─────────────>│ │
|
├─────────────→│ │
|
||||||
╟─────┐│ ├──────>│
|
╟─────┐│ ├──────→│
|
||||||
║wait ││block │ ║process
|
║wait ││block │ ║process
|
||||||
║<────┘│ │ ║
|
║←────┘│ │ ║
|
||||||
│ │<──────────────┤
|
│ │←──────────────┤
|
||||||
│<─────┤ │ tell│
|
│←─────┤ │ tell│
|
||||||
│ │ │ │
|
│ │ │ │
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -90,29 +84,6 @@ Notes:
|
||||||
for the sake of simplicity, but you may just as well use only one.
|
for the sake of simplicity, but you may just as well use only one.
|
||||||
|
|
||||||
|
|
||||||
### When NOT to use named pipes
|
|
||||||
|
|
||||||
To be completely honest, here are a list of cases that —**if they are all true**—
|
|
||||||
may lead you to consider that maybe it would be a good idea
|
|
||||||
to think about how you may eventually end up
|
|
||||||
looking for a solution that might be something that's close to a middleware:
|
|
||||||
|
|
||||||
- ☒ your service takes time to compute something,
|
|
||||||
- ☒ you have one service, but an unknown (large) number of clients,
|
|
||||||
- ☒ all clients expect the same interface,
|
|
||||||
- ☒ which involves answering to the server,
|
|
||||||
- ☒ with *complex* data structures,
|
|
||||||
- ☒ you absolutely need to serve them all as fast as possible,
|
|
||||||
- ☒ over the internet,
|
|
||||||
- ☒ and you are *certain* that no one will want *another* middleware in the next project.
|
|
||||||
|
|
||||||
I your use case don't match all of this checklist but you still want to
|
|
||||||
use a middleware, maybe you should just consider making a side software
|
|
||||||
that will expose/transliterate the data going through the named pipe.
|
|
||||||
That way, your service stays simple and you can easily
|
|
||||||
exchange one middleware for another without even touching it.
|
|
||||||
|
|
||||||
|
|
||||||
Build and run
|
Build and run
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
|
|
|
||||||
21
service1.py
21
service1.py
|
|
@ -1,21 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
import sys
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
print("Start server")
|
|
||||||
|
|
||||||
while True:
|
|
||||||
with open(sys.argv[1]) as fin:
|
|
||||||
datas = fin.readline()
|
|
||||||
|
|
||||||
data = datas.strip()
|
|
||||||
print("Received: <",data,">", file=sys.stderr)
|
|
||||||
|
|
||||||
with open(sys.argv[2], 'w') as fout:
|
|
||||||
fout.write(data)
|
|
||||||
|
|
||||||
if data == "exit":
|
|
||||||
break
|
|
||||||
|
|
||||||
print("Stop server", file=sys.stderr)
|
|
||||||
93
service2.py
93
service2.py
|
|
@ -1,93 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
from enum import Enum
|
|
||||||
import threading
|
|
||||||
import stat
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
|
|
||||||
class ERROR(Enum):
|
|
||||||
NOT_FIFO = 1
|
|
||||||
|
|
||||||
class Service:
|
|
||||||
def __init__(self, context: str, data: str, out: str) -> None:
|
|
||||||
self._has_current_context: bool = False
|
|
||||||
self._mutex = threading.Lock()
|
|
||||||
self._file_current_context: str = context
|
|
||||||
self._file_data: str = data
|
|
||||||
self._out: str = out
|
|
||||||
self._current_context: str = ""
|
|
||||||
|
|
||||||
def get_has_current_context(self) -> bool:
|
|
||||||
return self._has_current_context
|
|
||||||
|
|
||||||
def set_has_current_context(self, flag: bool) -> None:
|
|
||||||
self._mutex.acquire()
|
|
||||||
self._has_current_context = flag
|
|
||||||
self._mutex.release()
|
|
||||||
|
|
||||||
def update_current_context(self) -> None:
|
|
||||||
while True:
|
|
||||||
print("Wait for context...", file = sys.stderr)
|
|
||||||
has_error: bool = False
|
|
||||||
try:
|
|
||||||
with open(self._file_current_context) as if_current_context:
|
|
||||||
self._current_context: str = if_current_context.readline().strip()
|
|
||||||
except:
|
|
||||||
has_error = True
|
|
||||||
|
|
||||||
if not has_error:
|
|
||||||
self.set_has_current_context(True)
|
|
||||||
print("\tReceived context:", self._current_context, file=sys.stderr)
|
|
||||||
|
|
||||||
def handle_data(self) -> None:
|
|
||||||
while True:
|
|
||||||
if self.get_has_current_context():
|
|
||||||
print("Wait for data...", file=sys.stderr)
|
|
||||||
has_error: bool = False
|
|
||||||
try:
|
|
||||||
with open(self._file_data) as if_data:
|
|
||||||
data: str = if_data.readline().strip()
|
|
||||||
except:
|
|
||||||
has_error = True
|
|
||||||
|
|
||||||
if not has_error:
|
|
||||||
print("\tReceived data:",data, file=sys.stderr)
|
|
||||||
|
|
||||||
print("Do stuff...", file=sys.stderr)
|
|
||||||
result: str = self._current_context + ":" + data
|
|
||||||
print("\tdone", file=sys.stderr)
|
|
||||||
|
|
||||||
print("Output...", file=sys.stderr)
|
|
||||||
with open(self._out, 'w') as out:
|
|
||||||
out.write(result)
|
|
||||||
|
|
||||||
print("\tdone", file=sys.stderr)
|
|
||||||
|
|
||||||
def is_named_pipe_fifo(filename: str):
|
|
||||||
st = os.stat(filename)
|
|
||||||
return stat.S_ISFIFO(st.st_mode)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
|
|
||||||
assert(len(sys.argv) == 4)
|
|
||||||
|
|
||||||
for i in range(1,4):
|
|
||||||
if not is_named_pipe_fifo(sys.argv[i]):
|
|
||||||
print("ERROR:", sys.argv[i], "is not a named pipe FIFO", file=sys.stderr)
|
|
||||||
sys.exit(ERROR.NO_FIFO)
|
|
||||||
|
|
||||||
print("Start server", file=sys.stderr, flush=True)
|
|
||||||
server = Service(sys.argv[1], sys.argv[2], sys.argv[3])
|
|
||||||
|
|
||||||
do_current_context = threading.Thread( target = server.update_current_context )
|
|
||||||
do_tasks = threading.Thread( target = server.handle_data )
|
|
||||||
|
|
||||||
do_current_context.start()
|
|
||||||
do_tasks.start()
|
|
||||||
|
|
||||||
do_current_context.join()
|
|
||||||
do_tasks.join()
|
|
||||||
|
|
||||||
print("End", file=sys.stderr)
|
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue