| OOP
(that's a fancy way to say "I told you so!" :-)
By investigating the duality between APIs and network protocols, we re-discover the design of the web, and the relationship between mobile/distributed objects and knowledge sharing/exchange. For background, be sure to see "Toward A formalism for communication on the World Wide Web" posted to www-talk back in Feb 94.
The WWW/OOP Thesis: the hypermedia architecture of the web is isomorphic to--or perhaps a special case of--modern distributed object systems. URLs are symbols in the intertwingled GEB sense.
of HTTP into an RPC system:
procedure GET(path: String): ByteSequence;
interface Document { GET(): ByteSequence; } interface Server { docNamed(path: String): Document; };
We know GET can and should be cached. How do we tell the RPC infrastructure?
interface Document { functional GET(): ByteSequence; }
This looks like HTTP 0.9! What about error codes? Accept: headers for format negotiation?
interface Document { GET(version: String, fields: FieldList): struct { version: String; status: Integer; header: FieldList; body: ByteSequence; } }
We don't want to think about versios at this level. Can we make the version implicit in the interface by using RPC facilities?
Can we exploit RPC exception facilities to avoid manually checking for error codes?
interface Document version "HTTP/1.0" { GET(path: String, version: String, fields: FieldList): struct { header: FieldList; body: ByteSequence; } raise(Partial, ClientError, ServerError) } exception Partial, ClientError, ServerError { status: [0..99]; message: String; header: FieldList; body: ByteSequence; };
Can we express more of the structure and semantics of the header fields?
in stead of string syntax: (ref 1.1 spec)
interface Document{ GET( Accept-Charset: CharSetMetrics, Accept-Encoding: Encodings, Accept-Language: LanguageMetrics, Authorization: Credentials, From: String, Host: String, If-Modified-Since: Date, If-Match: String, If-None-Match: String[], If-Range: If-Unmodified-Since: Date, Max-Forwards: int, Proxy-Authorization: Credentials Range: record start, end: int end, Referer: Document, User-Agent: Product) : Response; type Response = struct struct { Age : Duration, | Location: Document, | Proxy-Authenticate: Challenge, | Public: MethodList, | Retry-After: Delay, | Server: Product, | Vary: VariantMap, | Warning: String | WWW-Authenticate: Challenge, body: ByteSequence; } raise(Partial, ClientError, ServerError)
in stead of Authorization argument.
interface Document{ GET( Accept-Charset: CharSetMetrics, Accept-Encoding: Encodings, Accept-Language: LanguageMetrics, From: String, If-Modified-Since: Date, If-Match: String, If-None-Match: String[], If-Range: If-Unmodified-Since: Date, Max-Forwards: int, Range: record start, end: int end, Referer: Document, User-Agent: Product) : Response; type Response = struct struct { Age : Duration, | Location: Document, | Proxy-Authenticate: Challenge, | Public: MethodList, | Retry-After: Delay, | Server: Product, | Vary: VariantMap, | Warning: String | WWW-Authenticate: Challenge, body: ByteSequence; } raise(Partial, ClientError, ServerError)
Why do semantically void information such as From: and User-Agent: and Server show up at this level?
interface Document{ GET( Accept-Charset: CharSetMetrics, Accept-Encoding: Encodings, Accept-Language: LanguageMetrics, Host: String, If-Modified-Since: Date, If-Match: String, If-None-Match: String[], If-Range: If-Unmodified-Since: Date, Max-Forwards: int, Range: record start, end: int end, Referer: Document : Response; type Response = struct struct { Age : Duration, | Location: Document, | Public: MethodList, | Retry-After: Delay, | Vary: VariantMap, | Warning: String body: ByteSequence; } raise(Partial, ClientError, ServerError)
Does RPC marshalling facility require that the whole body be in memory?
interface Document extends IPersistStream{ load(a: Anchor){ bc = new BindingContext() // set up my bc with persistence formats that I know about loadFrom(a.openRead(bc)); } displayOn(w: Window); } interface IPersisStream{ loadFrom(s: InputStream); saveTo(s: OutputStream); } interface BindingContext{ @@ captures semantics of following headers: // data understandin capabilities Accept-Charset: CharSetMetrics, Accept-Encoding: Encodings, Accept-Language: LanguageMetrics, // freshness info If-Modified-Since: Date, If-Match: String, If-None-Match: String[], If-Range: If-Unmodified-Since: Date, // latency requirements Max-Forwards: int, } interface Anchor { /* ala IMoniker, CosNaming::NamingContext */ openRead(bc: BindingContext, referer: Anchor): InputStream; (* GET *) openWrite(bc: BindingContext, referer: Anchor): OutputStream; (* POST *) openAppend(bc: BincingContext, referer: Anchor): OutputStream; (* PUT *) delete(bc: BindingContext, referer: Anchor); (* DELETE *) @@ mkdir? 9fs? DLS (link management)? @@type Response = struct struct { Age : Duration, | Location: Document, | Public: MethodList, | Retry-After: Delay, | Vary: VariantMap, | Warning: String body: InputStream; } raise(Partial, ClientError, ServerError) }
interface Entity: InputStream { contentId(): URI; (* URN *) location(): URI; (* URL *) lastModified(): Date; validator(): ETag; md5(); volume(); Age : Duration, } interface BindingContext{ attribute CacheControl; maxForwards(): CARDINAL; If-Modified-Since: Date, If-Match: String, If-None-Match: String[], If-Range: If-Unmodified-Since: Date, Max-Forwards: int, Accept-Charset: CharSetMetrics, Accept-Encoding: Encodings, Accept-Language: LanguageMetrics, Range: record start, end: int end, | Warning: String } interface Anchor : IMoniker{ bindToStorage(bc: BindingContext, referer: Anchor): Stream @@Hmm.... what about... | Public: MethodList, | Vary: VariantMap, raise(Partial, ClientError, ServerError) } interface Document : IPersistStream{ load(data: InputEntity); saveTo(data: OutputEntity); } GET( type Response = struct struct { | Location: Document, body: InputStream; }
interface SignedEntity { prove(antecedent: Formulas, conclusion Formula) raise Undecidable (f: Formulas); } interface EncryptedEntity { decrypt(k: Key): Stream; }
interface Bank{ open(customerName: String): Account; }; interface Account{ deposit(amount: Currency); withdraw(amount: Currency); balance(): Currency; };
HTTP mapping:
OPEN /bank HTTP/1.new CustomerName: Fred Jones 200 OK Content-Type: application/url http://server/account/1