Table of Contents
- Hop
- Server Information
- Responses
- hop.HTTPResponseHop( obj, [option] )
- hop.HTTPResponseXml( obj, [option] )
- hop.HTTPResponseString( string, [option] )
- hop.HTTPResponseJson( object )
- hop.HTTPResponseFile( path, [option] )
- hop.HTTPResponseAuthentication( msg, [request] )
- hop.HTTPResponseError( obj )
- hop.HTTPResponseAsync( sender, req )
- hop.HTTPResponseProxy( obj )
- Broadcast
- hop.broadcast( eventName, value )
- hop.signal()
- Server.addEventListener( eventName, handler [, options] )
- new hop.Server( [ hostname [, port [, authorization [, ssl ] ] ] )
- Web Service
- hop.webService( url )
- WebServiceFrame.post([ success [, fail-or-options]] )
- WebServiceFrame.postSync([ success [, fail-or-option]] )
- Miscellaneous
- hop.charsetConvert( text, source, target )
- hop.encodeURIComponent( string )
- hop.Cons()
- hop.List()
- hop.md5sum( string )
- hop.sha1sum( string )
- hop.compileXML( node [, ofile] [, backend] )
- Sub Modules
Hop
This module contains utilities for getting, controlling, and using the Hop server. The module defines functions to craft service responses, and a broadcast function that lets a server send events to registered remote clients.
The module also defines an API to invoke third party Web Services.
For ease of use, hop is defined as a global object and can be used
directly without require.
Server Information
The server properties defined below are read-only.
hop.port
The port number of the running Hop server. To set the port and protocol for the Hop server, see config.
console.log( "port:", hop.port );hop.hostname
The host name of the running Hop server.
console.log( "hostname:", hop.hostname );hop.version
The Hop version.
console.log( "Hop version:", hop.version );
Responses
Service result values are transformed into Hop responses before being sent to the clients.
hop.HTTPResponseHop( obj, [option] )
This class is used to respond values to client requests.
service getObj() {
return hop.HTTPResponseHop( { key: "foo", value: [ 1,2 3 ] } );Note:
In normal situation, it is not necessary to explicitly build the
HTTPResponseHop object as the runtime system automatically constructs
one when the response of a service is a compound JavaScript object.
The options list is:
startLine: a string denoting the HTTP start line.contentType: thecontent-typeof the response.charset: the charset.header: the full response header, an object.
hop.HTTPResponseXml( obj, [option] )
This class is used to deliver XML documents to client.
service getXml() {
return hop.HTTPResponseXml( <div>a div</div> );The options list is:
backend: the HTML backend (defaults to "HTML5")startLine: a string denoting the HTTP start line.contentType: thecontent-typeof the response.charset: the charset.header: the full response header, an object.
Note:
In normal situation, it is not necessary to explicitly build the
HTTPResponseXml object as the runtime system automatically constructs
one when the response of a service is an XML fragment. It might be
useful to construct an HTTPResponseXML explicitly when a header
is to be associated with the response. Example:
service foo() {
return hop.HTTPResponseXml(
<html>
<button onclick=~{console.log( document.cookie )}>show</button>
</html>,
{ contentType: "text/html", header: { "set-cookie": "a=b; HttpOnly" } } );
}hop.HTTPResponseString( string, [option] )
This class is used to deliver plain character strings to client.
service getXml() {
return hop.HTTPResponseString(
"This resource does not exist here!",
{ startLine: "HTTP/1.0 404 File not found" } ) The options list is:
startLine: a string denoting the HTTP start line.contentType: thecontent-typeof the response.charset: the charset.header: the full response header, an object.
hop.HTTPResponseJson( object )
This convenience function returns an [application/json] value from a
JavaScript object. It is the same as:
hop.HTTPResponseString( JSON.stringify( obj ), { contentType: 'application/json' } )startLine: a string denoting the HTTP start line.contentType: thecontent-typeof the response.charset: the charset.header: the full response header, an object.
hop.HTTPResponseFile( path, [option] )
This class is used to respond files to clients. The argument path is
the full path of a existing file.
contentType: thecontent-typeof the response.charset: the charset.header: the full response header, an object.
Example
This example shows the most efficient way to deliver content file
to client. Using a HTTPResponseFile object deliver much better
performance than reading the file content first and then seding a
buffer or a string the client.
In this example, the file replied to the client is to be interpreted
as a plain text file alhgouth it is a JavaScript program. To tell
the Web browser not to interpret the file, the mime type text/plain
is specified in the response option.
The charset encoding of the file is also provided using the charset
attribute.
file/file.js
service file() {
var pre = <pre/>;
return <html>
~{
var entityMap = {
"&": "&",
"<": "<",
">": ">",
'"': '"',
"'": ''',
"/": '/'
};
function escapeHTML( string ) {
return String( string ).replace(
/[&<>"'\/]/g,
function ( s ) {
return entityMap[s];
} );
}
}
<button onclick=~{
var file = ${fileGet.resource( "file.js" )};
${fileGet}( file )
.post( function( txt ) {
${pre}.innerHTML = escapeHTML( txt )
} );
}>
click me
</button>
${pre}
</html>;
}
service fileGet( path ) {
return hop.HTTPResponseFile( path,
{ contentType: "text/plain",
charset: hop.locale } );
}
console.log( "Go to \"http://%s:%d/hop/file\"", hop.hostname, hop.port );
Note:
HTTPResponseFile is a much faster way to send a file to a client, althought,
the same behaviour can also be implemented combining standard fs operations
and HTTPResponseString values.
hop.HTTPResponseAuthentication( msg, [request] )
This class is used to respond HTTP 401 Unauthorized response to Web
client.
Note:
the class hop.HTTPResponseAuthentication is a convenience class.
The same behavior can be implemented using hop.HTTPResponseString
and passing a startLine value in the optional argument.
Example
This example shows how to use HTTPResponseAuthentication to request
Web browser authentication.
The example counts the number of request (the variable count). Each request
decrements the counter but only passes through when it reaches 0.
authentication/authentication.js
var count = 2;
service authenticationAccept() {
switch( count-- ) {
case 2:
return hop.HTTPResponseAuthentication( "I don't know you", this );
case 1:
return hop.HTTPResponseAuthentication( "Do you really insist?", this );
case 0:
count = 2;
return "Ok for this time";
}
}
service authentication() {
var console = <div/>;
return <html>
<div>
Click 3 times the "click me" button.
Permission granted on the third request.
</div>
<button onclick=~{
${authenticationAccept}()
.post( function( v ) { ${console}.innerHTML = v },
{ fail: function( v ) { ; } } ) }>
click me
</button>
${console}
</html>
}
console.log( "Go to \"http://%s:%d/hop/authentication\"", hop.hostname, hop.port );
hop.HTTPResponseError( obj )
Respond an error value to the client, which either invokes the fail
callback of the post service call, or raises an exception.
hop.HTTPResponseAsync( sender, req )
Asynchronous responses are used when a service cannot returns instantly
a value to a client because it relies on an asynchronous computation.
In that situation, the service must produce a hop.HTTPResponseAsync which
is interpreted by the builtin server as a delayed reply.
- The argument
senderis a function of one argument. This function is automatically invoked by the runtime system with a value that is a function of one parameter. Invoking that function provokes the delivery of the reply to the client. - The argument
reqis either a request object (thethisvalue of the service invokation) or an object containing the optional fields.currentRequest: the request object.contentType: thecontent-typeof the response.charset: the charset.header: the full response header, an object.
Example
This example illustraed service declaration with fixed number of argument and asynchronous responses.
The service foo must call the service bar on the same host.
Calling the serving synchronously would result in a dead lock
as only one thread is in charge of handling services.
The call to bar must then be asynchronous. This is acheived
by applying the post method of the frame computed with
bar( x + 1 ).
The service foo relies on a asynchronous computation. It then cannot
respond immediately to the client. It will be in position to
reply only when the invocation of bar has completed. This is
implemented using a hop.HTTPResponseAsync object. The argument
sendReponse is a function automatically created by the runtime
system. When invoked with sendReponse( e ) the result of the
service bar is replied to the cilent which has called foo.
svc3/svc3.js
service svc3() {
return <html>
<button onclick=~{
${foo}( 1 )
.post( function( r ) {
document.body.appendChild( r );
} )
}>click</button>
</html>;
}
service foo( x ) {
console.log( "in foo x=", x );
return hop.HTTPResponseAsync(
function( sendResponse ) {
bar( x + 1 ).post( function( e ) {
sendResponse( e );
} )
}, this );
}
service bar( x ) {
console.log( "in bar x=", x );
return <div>${ x + 1 }</div>;
}
console.log( "Go to \"http://%s:%d/hop/svc3\"", hop.hostname, hop.port );
Note:
the class hop.HTTPResponseAsync is the base class for
implementing asynchronous reponses. Returning
Promise objects as a similar behavior and is encouraged. The service foo
defined above can be implemented as:
service foo( x ) {
console.log( "in foo x=", x );
return new Promise( function( resolve, reject ) {
bar( x + 1 ).post( resolve );
}
}Invoking the resolve function actually sends the responds to the client.
Invoking the reject as the same effect of responding a HHTPResponseError
value.
hop.HTTPResponseProxy( obj )
The hop.HTTPResponseProxy objects are to be used when a remote resource
can be access othwerwise. For instance, these situations arise because of
the security enforcement of the Web browsers. Some resources have to be
downloaded from the origin server. Using a hop.HTTPResponseProxy object
enables the web page to only use local URLs, that are proxied to the actual
remote resources by the server.
Example
This example illustrates standard client image manipulations and use
of hop.HTTPResponseProxy objects.
Security enforcement of Web browsers prevent manipulation image pixels whose origin differs from the origin of the main page. To workaround this problem, the manipulated image is proxied by the Hop server. The Web browser only sees relative local URLs.
image/image.js
var img_default = "http://t1.gstatic.com/images?q=tbn:ANd9GcRUADxj_7NEl8RAFNM-s6x3Wgp1QIg81QHRMVeOuMHBklr1JWddmQ";
var colors_default = [
{red: 252, green: 27, blue: 0},
{red: 125, green: 167, blue: 129},
{red: 255, green: 122, blue: 10}
];
service imgProxy( url ) {
return hop.HTTPResponseProxy( url );
}
service image( o ) {
var url = o && "url" in o ? o.url : img_default;
return <html>
~{
function drawImage( url, colors ) {
var img = new Image();
var src = document.getElementById( "src" );
var dst = document.getElementById( "dst" );
var ctxsrc = src.getContext( "2d" );
var ctxdst = dst.getContext( "2d" );
img.onload = function( e ) {
src.width = img.width;
src.height = img.height;
dst.width = img.width;
dst.height = img.height;
ctxsrc.drawImage( img, 0, 0 );
var f = ctxsrc.getImageData( 0, 0, img.width, img.height );
ctxdst.putImageData( colorize( f, colors ), 0, 0 );
}
img.src = url;
}
function colorize( frame, colorset ) {
var data = frame.data;
var l = data.length / 4;
for( var i = 0; i < l; i++ ) {
var r = data[ i*4 ];
var g = data[ i*4 + 1 ];
var b = data[ i*4 + 2 ];
var range = Math.floor( (0.65*r + 0.22*g + 0.13*b) / 86 );
var cs = colorset[ range ];
data[ i*4 ] = 0.7*cs.red + 0.3*r;
data[ i*4 + 1 ] = 0.7*cs.green + 0.3*g;
data[ i*4 + 2 ] = 0.7*cs.blue + 0.3*b;
}
return frame;
}
window.addEventListener( "load", function( e ) {
drawImage( ${imgProxy( url )}, ${colors_default} )
} )
}
<img src=${url}/>
<canvas id="src" style="display: none"/>
<canvas id="dst"/>
</html>
}
console.log( "Go to \"http://%s:%d/hop/image\"", hop.hostname, hop.port );
Broadcast
Broadcast is an abstraction on top of webSockets to let a Hop server send events to connected clients (either web browsers or Hop client processes). Connections originate from the client to the server, so broadcast can be used even in the asymetric web topology where clients most often lie behind a NAT router or firewall and would not accept a connection from a remote server (forbidding the remote server to invoke services running on the client process).
hop.broadcast( eventName, value )
Generates an event of type eventName with payload value. The event
is broadcast over the network to all registered clients. eventName
is cast into a String, valuecan be any serializable object,
including JavaScript objects, Hop.js services, and
xml-elements. Clients register to specific broadcast events with the
addEventListenermethod.
hop.broadcast( 'refreshScore', 14 );hop.signal()
This function is similar to broadcast but only one receiver will be
notified of the message.
Server.addEventListener( eventName, handler [, options] )
Use this method on the client side to register to the eventName
server event. The effect of this method is to establish a persistent
connection with the Hop server, register the client for the given
event type, and trigger the handler whenever the event is received by
the client. handler takes one argument, the event. The transmitted
value can be retrieved in the value property of the event.
When used within a web browser, connection is established with the Hop
server serving the current page, the exact syntax is
server.addEventListener( eventName, handler ) where server denotes
the current server (the runtime system automatically binds the
server variable to the current server).
server.addEventListener( 'refreshScore', function( event ) {
var score = event.value;
var scoreElement = this.document.getElementById( 'score' );
// update GUI element with new scoreOn the server side, server objects are instances of the Server class.
new hop.Server( [ hostname [, port [, authorization [, ssl ] ] ] )
the arguments are as follows:
hostname: a string, the name or IP number of the remote host that will emit signals. If omitted, defaults to the running host name.port: the port number of the remote host. If omitted, defaults to the running Hop port.authorization: a string, an optional authorization for accessing the remote host. This has the syntax of the framepostmethod.ssl: a optional boolean. When true, the established channel between the two servers uses SSL.
var srv = new hop.Server( "localhost", 9999 );
srv.addEventListener( 'refreshScore', function( event ) {
var score = event.value;
...
} )
service getScore();
getScore.call( srv, "jean dupont" ).post( v => ... );Web Service
WebService is a set of API that let you invoke third party WebServices the same way you invoke Hop services.
var hop = require( 'hop' );
var mymemory = hop.webService( "http://mymemory.translated.net/api/get" );
mymemory( {q: 'My tailor is rich.', langpair: 'en|fr' } ).post( function( result ) {
console.log( result.responseData );
}, { fail: function( error ) {
console.log( 'failure' );
} });hop.webService( url )
Use this method to declare a remote WebService,that can later be
invoked with named arguments. urlis the url of the WebService.
Call the returned function with an object argument containing the
named arguments you want to send to the WebService. The returned value
is a WebServiceFrame (very similar in use to Service Frames).
WebServiceFrame.post([ success [, fail-or-options]] )
Invokes asynchronously the webService. The optional successargument,
when provided, must be a function of one argument, which is set the
the value returned by the WebService.
if the optional argument fail-or-options is a procedure, it is
invoked if an error occurs during the WebService invocation. If
fail-or-options is an object, it contains optional parameters to the
WebService invocation.
The list of valid options are:
hostname: the remote host.port: the remote host port.authorization: a identification of the formname:password.fail: a failure callback.scheme: the scheme used for the request (defaults tohttp).ssl: a boolean to enable secure connections.timeout: a number of milliseconds.method: the method of the call (e.g.,GETorPOST).header: the complete header of the request. The header is an regular object.body: a string, denoting the body of the request.
Example:
var ws = hop.webService( "http://localhost:1337/api/oauth/token" );
ws()
.postSync(
{ method: "POST",
header: { "content-type": "application/x-www-form-urlencoded" },
body: "grant_type=password&client_id=android&client_secret=SomeRandomCharsAndNumbers&username=myapi&password=abc1234" } );WebServiceFrame.postSync([ success [, fail-or-option]] )
The synchronous version of post. Returns the value returned by the
service. Since postSync blocks the execution of the client process
until the service returns a value, it is strongly advised to use
the asynchronous post when applicable. The options are shared with the
post method.
Miscellaneous
hop.charsetConvert( text, source, target )
Converts the text string from charset source into charset target.
url/url.js
"use hopscript";
var mymemory = hop.webService( "http://mymemory.translated.net/api/get" );
function translateText( text, lang = "en|fr" ) {
var o = mymemory( { q: text, langpair: lang } ).postSync();
if( o.responseStatus === 200 ) {
var t = o.responseData.translatedText;
return hop.charsetConvert( unescape( t ), "UTF-8" );
}
}
service url() {
var output = <div/>;
var input = <input value="toto n'est pas content"/>;
var select = <select>
<option label="fr->en" value="fr|en">fr->en</option>
<option label="en->fr" value="en|fr">en->fr</option>
</select>
var translate = service( text, langpair ) {
return translateText( text, langpair );
};
return <html>
<div>
${select}
${input}
<button onclick=~{
${translate}( ${input}.value, ${select}.value )
.post( function( v ) { ${output}.innerHTML = v; } )}>
translate
</button>
${output}
</div>
</html>;
}
console.log( "Go to \"http://%s:%d/hop/url\"", hop.hostname, hop.port );
hop.encodeURIComponent( string )
Encodes a string into a valid URI component.
hop.encodeURIComponent( 'jean dupont' );
// "jean%20dupont"hop.Cons()
This function is a constructor to create native (Bigloo) objects.
hop.List()
This function is a constructor to create native (Bigloo) objects.
hop.md5sum( string )
Computes the md5sum of a string.
hop.md5sum( 'jean dupont' );
// "b38bed581de7b86dd6fc8355c73cebf2"hop.sha1sum( string )
Computes the sha1 sum of a string.
hop.sha1sum( 'jean dupont' );
// "7461340811509ec24dd1c1a32504a01e24423768"hop.compileXML( node [, ofile] [, backend] )
Compile a XML node into HTML. If no output file is specified,
the product of the compilation is returned in a buffer. The
optional backend argument is a string denoting the HTML version to be
used for the compilation.
var node = <html><div onclick=~{alert( "clicked" )}>click me</div></html>
console.log( hop.compileXML( node, false, "html5" ) );Note:
explicit compilation to HTML using hop.compileXML is unncessary
for service responses. Services can directly return XML objects
in response to HTTP requests.
Sub Modules
The following properties lead to sub modules that can be loaded using
the require function.
var hop = require( 'hop' );
var config = require( hop.config );hop.config
See config.
hop.fontifier
hop.markdown
See markdown.
hop.notepad
hop.security
hop.spage
See spage.
hop.tree
See tree.
hop.user
See user.