One thing I would mention is that COM and CORBA have explicit support for object references, which is a concept that disappeared from later protocols.

With these technologies, a server can return a reference to, say, a Person. The client can then do "person.GetName()" or similar. The method calls are implemented as "stubs" that act as proxies that simply send the RPC along with the references to the objects they operate on. The server-side RPC implementation keeps a mapping between references and actual in-memory objects, so that calls to references call the right thing in the server process.

The benefit is that you can work with APIs in ways that feel natural. You can do "persons.GetPerson("123").GetEmployer().GetEmployees()" or whatever — everything feels like you're working with in-memory objects.

This has drawbacks. One is that the cost of method calls is obscured by this "referential transparency", as it's never obvious what is remote or local. Another problem is that the server is required to keep an object around until a client releases it or dies. If the client dies without first releasing, the objects will live until a keepalive timer triggers. But because a malformed client can keep objects around, the system is vulnerable to high memory use (and abuse). In the end you'd often end up holding a whole graph of objects, and nothing would be released until all references were released. Leak can be difficult to find.

My knowledge of COM/CORBA may be incomplete, but never understood why the server couldn't implement these in terms of "locators". For example, if a server has a "GetPerson(string id) -> Person" type method, rather than sending an object reference that points to an in-memory person object, it could return a lightweight, opaque string like "person:123". Any time the client's internal proxy passed this back to the server, the server could look it up; the glue needed to resolve these identifiers back into real objects would be a little more work on the part of the developer, but it would sidestep the whole need to keep objects around. And they could cached quite easily.

Cap'n Web [1] is the first RPC system in a long time (as far as I know) that implements object references. However, it does this in a pretty different way with different pros and cons.

[1] https://blog.cloudflare.com/capnweb-javascript-rpc-library/

> Cap'n Web [1] is the first RPC system in a long time (as far as I know) that implements object references.

Cap'n Proto does it too, and has been around since 2013. We use it extensively it the implementation of Cloudflare Workers. Many people have joined the team, initially thought "what is this weird thing? Why don't we just use gRPC instead?", and then after a few months of using it decided it's actually a superpower. I'm planning to write more about this on the Cloudflare blog in the next couple months, probably.

(Cap'n Proto is itself based on CapTP, the protocol used in the E programming language.)

I never actually used COM nor CORBA, but my impression is there's a few reasons they didn't work where Cap'n Proto does:

1. Excessive complexity. CORBA is a monstrously large standard, covering not just protocol but also system architecture ("Object Resource Brokers").

2. Lack of asynchronous programming. CORBA calls would synchronously block the calling thread until the call completed. But when making calls over a network (rather than locally), it's much more important that you be able to do other things while you wait. CORBA added (extremely complex) asynchronous I/O support late in its life but few people ever used it.

3. Lack of promise pipelining. This sort of follows from #2 (at least, I don't know how you'd express promise pipelining if you don't have promises to start with). Without promise pipelining, it's incredibly hard to design composable interfaces, because they cannot be composed without adding a round trip for every call. So instead you end up pushed towards big batch requests, but those don't play well with object-oriented API design.

4. Poor lifecycle management. An object reference in CORBA was (I am told) "just data", which could be copied anywhere and then used. The server had no real way of being notified when the object reference was no longer needed, unless clients proactively told it so (but this was up to the app). Cap'n Proto ties object lifetime to connections, so when a connection is lost, all the object references held across it are automatically disposed. Cap'n Proto's client libraries are also designed to carefully track the lifecycle of a reference within the client app, so that as soon as it goes out-of-scope (GC'd, destructor runs, etc.), a message can be sent to the server letting it know. This works pretty well.

5. Bad security model. All objects existed in a global namespace and any client could connect to any object. Access control lists had to be maintained to decide which clients were allowed access to which objects. This is a bolted-on security mechanism that sounds simple but in practice is extremely tedious and error-prone, and often people would avoid it by implementing coarse-grained security models. Cap'n Proto implements an object-capability model, aka capability-based security. There is no global namespace of objects. To access one, you have to first receive an object reference from someone who already has one. Passing someone an object reference implies giving them permission to use it. This may at first sound more complicated, but in practice it turns out to map very cleanly to common object-oriented API design patterns.

As a result of all this, in Cap'n Proto (and Cap'n Web), you can pretty much use the exact same API design patterns you'd use in a modern programming language, with lots of composable objects and methods, and it's all safe and efficient.

(I'm the author of Cap'n Proto and Cap'n Web.)

I'm one of the thankfully few people in the world who worked on an implementation of DCOM (in my case, for Wine, although by coincidence I had a university lecturer who did the same for QNX). They're very similar systems. I think some of that is true and mattered, some of it is true and didn't matter much, and there's some aspects missing.

Neither DCOM nor CORBA worked well over the internet. DCOM was designed in an era when Bill Gates was publicly shitting on the internet and saying the future was closed BBS networks like CompuServe, AOL and MSN. Microsoft didn't have a reasonable internet strategy back then, even TCP/IP support was kinda ropey. So DCOM assumed an entirely flat network in which every machine had a unique network address, was up all the time, there were no firewalls anywhere and encryption wasn't needed. As a consequence passing a callback to an object - a very OOP and idiomatic thing to do - meant the server would try to connect back to the client. Every RPC system back then made this mistake, also in the UNIX world with Java's RPC system (RMI), Sun RPC etc. Thus every client in these architectures was also a server. This idea was just about tenable up until the DSL rollout and teenagers started noticing that every Windows XP box on the internet was also a server with a bunch of exposed RPC interfaces, all of which was connected to piles of crappy old C++ riddled with memory safety bugs. After a few megaworms Microsoft pushed updates that added a firewall and closed the entire RPC port by default, instead of providing any kind of finer grained support for firewalling at the object exporter level, and that was the end of any chance of using this 90s generation of RPC.

HTTP, on the other hand, had a very clear notion of what was a server and what was a client. It also had SSL/TLS, developed by Netscape for the web, which was one of the first cryptographic protocols. We take it for granted now but stuff like DCOM had no equivalent and no effort to develop any. After all, objects are exported over wired ethernet ports at your office, right? Why would you need any encryption? The need for it on the web was driven by eCommerce but nobdy used DCOM or CORBA for exporting shops over the network.

Systems like DCOM did have object capabilities and lifecycle handling, but that wasn't terribly useful because the default timeout on pings was absurdly high, like 20 minutes or something, and so in practice the ability to hold references to stateful objects could cause memory leaks accidentally and of course the moment anyone wanted to DoS attack you it was all over.

Thanks for chiming in! I was actually poking through Cap'n Provo's implementation of capabilities the other day, and I noticed that capabilities are 32 bit numbers. And, at least in the rust implementation, capabilities are incrementing numbers. Do you ever worry about enumeration attacks?

Those numbers are indexes into a table that is connection-specific. So the numbers themselves aren't secrets at all, and it doesn't matter if you can guess them -- they won't mean the same thing in the context of some other connection. Sort of like how file descriptor numbers are specific to a process.

Thanks for clarifying! I figured I must've been missing something...