You are on page 1of 46

Chat Service Integration in Guru Web Platform

Design Document Version 2.0

Developed for Guru.com August 7th, 2012 Developed by Automated Logical Software Pvt. Ltd.
Atlogys Technical Consulting R-8, Nehru Enclave New Delhi, India 110019
Strictly Confidential

Versions
Date 27/07/2012 07/08/2012 Author Ritika Garga Ritika Garga Change Summary Original Document HLV _ v1.0 Complete detailed Design Doc Version 1 2

References
# Document Name 1 Our Understanding Atlogys Technical Consulting.pdf 2 Updated Chatv1.1.doc (BRS) (From Sharepoint) 3 Updated Chat HTML Mockups (From Sharepoint) 4 Chat UI Mocks Link 5 Guru Architecture High Level (Visio) 6 Guru-Architecture.docx 7 GuruRPGuide.docx 8 ChatBRS_v1.2.docx 9 Updated Chat HTML Mockups (From Sharepoint) Description This lists the R&D done by us till date ad gives you a preliminary look into the technical solution. Updated doc - Describes all features desired from chat service on server and client side Html, css, js for the chat UI can run locally on machine http://dl.dropbox.com/u/26268356/Guru/HTM L/Chat/Chat.html# Guru.com web setup and overall design architecture 2 pager on guru overall code structure from Shrinivas Narayani Information on OpenId usage at guru.com Post Consolidation of all Q&A from Atlogys Post Consolidation of all Q&A from Atlogys enable/disable settings in dropdown added. Author Atlogys Date Shared 11/06/2012

Guru Guru

16/07/2012 16/07/2012

Guru Guru

9/07/2012 28/05/2012

Guru Guru Guru Guru

27/07/2012

3/08/2012 3/08/2012

1. Introduction
The document describes the overall architecture & high level design (for both server and client) suggested for integration of chat component into the existing web platform - Guru.com. In doing so, the document throws light on various components involved, their role & interaction amongst the other components of the proposed design.

2. Terms, Definitions, Acronyms used


XMPP: Extensible Messaging & Presence Protocol also called Jabber MUC: Multi user chat JSJAC: JavaScript Jabber Client API Strophe/ Strophe.js: A JavaScript based Jabber Client API Jetty: An embedded Java web container used by Openfire server BOSH: Bidirectional Streams Over Synchronous HTTP CORS: Cross Origin Resource Sharing AJAX: Asynchronous JavaScript & XML UCL :- User Control (We can place this user control on any aspx page) CPH:- Content place holder (We can put user controls, aspx pages inside content placeholder) Client or Web client: The module comprised of web pages that is interpreted & executed by web browser & which is used by an end-user to login into the Guru.com website as well as chat component.

3. Scope
Integrating the Openfire server with the webserver (ASP.NET also having ColdFusion pages) of the guru.com platform along with providing for a uniform and identical embedding of the html based web chat client in the site. The document excludes the multi-user chat (MUC) & focuses solely on the chat messages exchanged between any two users - one of them freelancer & other an employer. However, if & when the MUC functionality needs to be implanted, the current design will work with little or no change.

3.1 Chat Features The features and functionality to be added in the chat client are as per the updated document #8 (ChatBRS_v1.2)of the References table above. These were also listed in sections 1.1.1 and sections 1.1.2 of document #1 in the References table.

4. Chat Engine
The chat engine consists of the chat server and chat client. 4.1 Chat Server After exploring many options for chat servers (both opensource and commercial), we have decided to go ahead with the Java based OpenSource OpenFire server which is XMPP based. Openfire is a real time collaboration (RTC) server licensed under the Open Source Apache License. It uses the only widely adopted open protocol for instant messaging, XMPP (also called Jabber). The reasons for selection of OpenFire were elaborated in document #1 of references table above. 4.1.1 Features of OpenFire: It supports the following features:

User-friendly web-based installation and administration panel Shared groups for easy roster deploying Plugin interface SSL/TLS support Offline Messages support Server-to-Server connectivity Database connectivity for storing messages and user details (including the embedded HSQL database and support for MySQL, PosgreSQL and other databases) LDAP integration Platform independent (with the installers for different platforms) Integration with Spark IM client Legacy transports support (with the discontinued Kraken plugin, and planned support for Spectrum IM) Connection manager for load balancing Clustering support (current open-source Clustering plugin only works with the commercial Oracle Coherence software) Message archiving-logging Content filtering, packet rules

The proprietary extension to Openfire allows multiple server instances to work together in one clustered environment. This extension is now open source as well (but depends on Oracle Coherence, a commercial product). 4.1.2 Chat Server Hardware and Deployment The OpenFire chat server application will reside on a dedicated Linux machine. The Linux box will be connected to guru.com main server through intranet. It will run on a URL like chat.guru.com. It will use Mysql database. The deployment configuration/stack for Openfire server should be Openfire 3.7.1 (containing embedded Jetty app server/container)

64 bit Java Runtime Environment (preferably Oracle & by default its bundled with Openfire setup)

64 bit Linux server box

X86-64 architecture

The following hardware will suffice: o 8 GB of RAM, o 64 bit processor with 2 cores o If its a dedicated Openfire machine then we can use almost 70-80% of RAM for JVM heap. o OpenFire uses Apache Mina where we can configure the thread pool to handle the desired number of total requests.

4.1.3 Inside Openfire Openfire will have following components: 1. The Jetty is embeddable web container that serves the static content as well as servlet/JSP & which can be spawned from the source code as & when needed. Internally, Openfire does this & spawns 2 instances of Jetty. a. One running on port 9090 over which you access the web based admin UI. It also acts as a container for plug-in deployment. b. Second running on port 7070. The HttpBindSevlet is deployed here & is the server side end point of BOSH.

2. HttpBindServlet: is the servlet that provides the server side BOSH interface. It allows the web clients to connect to Openfire server & exchange IMs. Its deployed in Jetty & binds to port 7070. 4.1.4 Server SSL and Configuring the Binding Ports In OpenFire, the 7443 port allows a BOSH client to connect to HttpBindServlet over SSL. When the XMPP client needs to connect to server, the client opens a stream (indicated by <stream> element) to the server. The client & server negotiate the stream features like Transport Layer Security (TLS). When the client is connecting over port 7070 and TLS is being used, then the channel is encrypted end to end without changing the port. So there is an end-to-end transport layer encrypted channel between client & server on port 7070. Whereas, in case of SSL, the encryption happens over a separate & dedicated port i.e. 7443. 1. In any case, the server needs to establish its identity to the client & prove that it is indeed who it claims to be. So the chat server will need the certificates. Openfire has self-signed certificates which we can use to begin with. However, actual asymmetric RSA (private-public key pair) certificates will need to be purchased from VeriSign etc. for the production deployment of openfire. 2. Sometimes, the users/clients that need to access the Guru chat component are placed behind firewalls (outbound firewall within their network). Their network admin might restrict access to port 7070 or 7443. In such a case, the HttpBindServlet running on OpenFire, and to which web browser will make CORS Ajax requests, can be updated to listen on ports 80 or 443.

4.2 Client Side Client side chat application will communicate with the OpenFire server using XMPP over BOSH. [WebSockets is another desired channel considered as an alternate to BOSH but this is not well supported by many browsers (like only works on IE10 preview edition). Furthermore, the reliability needs to be tested for openfire support of websockets on non 80 ports (i.e. Whether openfire can support the WebSocket handling in stand-alone setup i.e. sans any proxy. The plug-in for enabling WebSocket support is present at: http://code.google.com/p/openfire-websockets/ ). Please refer to the Theory (heading #18 ) the end of this document for clarity on BOSH, webSockets etc. Since we will be sending requests from a web browser, we will use the JavaScript XMPP libraries like sthrope.js or JsJAc.js which provide implementations of XMPP over BOSH. Essentially what they do is that - the XMPP specific XML is wrapped in payload of HTTP POST.

Any one of these two libraries can be used since both are very well documented and widely used libraries for XMPP communication. We prefer using JsJac.js for purposes of this project. The function calls inside the JsJac library will communicate with OpenFire and fetch information like chat contacts list, chat messages, favorite contacts, offline messages etc. which are stored in OpenFire Database. The .NET application (guru.com) will change its presenter code on all .aspx and .cfm pages to output the chat UI and chat related JavaScript which will makes calls to the functions inside the JsJac.js library. The returned XML from JsJac.js will be parsed by the chat related JavaScript on the clients browser to extract the chat messages and information which will then be rendered inside the chat html windows. Such client side code must be added by the .NET server application to all its client side pages. Please note that as per heading # 16 (section 5), this is being added in div 2 via a chat.ascx user control (of the Main.aspx/Main.cfm pages). The web chat client will also fetch some information directly from the guru.com servers like profile images, names and screen names (or company names) of all contacts in the chat list, along with the logged in users chat settings (discussed in detail below).

5. Basic Control Flow


1. Web user logs onto guru.com and creates a session with the guru .net application. 2. Upon successful authentication with guru.com, the web client sends a request to log into openfire directly using JsJac/sthrope.js client JavaScript library. 3. Openfire check for user authentication with guru openId provider. 4. When OpenID confirms the user to be a valid logged in user in the system, and after OpenFire validates the user in its own database, the web client establishes a successful connection with OpenFire and creates a session (XMPP over BOSH socket) with OpenFire. 3. The web client then requests openfire to get its chat contact list, favorite contacts, recent messages, favorite contacts etc. 4. For all contacts received, the web client asks guru.com server to send their respective profile names, images and screen names (or company names). 5. The web chat client asks guru.com to send the chat user settings of the logged in user. 6. The chat UI which includes both the chat related html and JavaScript receives the necessary information from 3,4,5 to render the chat windows as desired.

NOTE: Please note that this Chat UI must be outputted by guru .net application on all its aspx and cfm pages. The chat related JavaScript here uses JsJac.js library to perform step 3 and REST calls to guru.com to do 4. It must know how to parse xml received in 3. 7. On the backend, there is also communication between OpenFire (with HTTPBindServlet in Jetty) and Guru using REST API calls. This uses a mix of push and pull techniques. Openfire till pull the list of all registered users from guru.com. Guru.com will push information to Openfire on trigger events like user addition, user deletion, new message exchange between two users etc (discussed in detail below).

This control flow is shown in the diagram below:

*NOTE: Instead of Json, we will use XML throughout for consistency.

6. Communication between Web Client and OpenFire


In the following text to come, proxy is referred to as a rule based software component that shall pass the HTTP requests originating from web client to Openfire server. This proxy can be a web server like Apache or Nginx or IIS that can run on dedicated server or which can be configured to accommodate the rules for the proxy passes.

6.1 METHOD OF COMMUNICATION Scenario 1: Without HTTP Proxy (Direct communication between web client and OpenFire ) We plan to use CORS to achieve communication between web client hosted on domain:port guru.com:80 and openfire server hosted on domain:port chat.guru.ccom:7070. The requests are directly sent to a HTTP binding servlet that runs in Jetty container listening at port 7070 or 7443 (for SSL). CORS support exists on all browsers that we need to launch this feature on. Precisely, this includes:

Gecko 1.9.1 (Firefox 3.5,[3] SeaMonkey 2.0[4]) and above. WebKit (Initial revision uncertain, Safari 4 and above,[1] Google Chrome 3 and above, possibly earlier)[5] MSHTML/Trident 4.0 (Internet Explorer 8) provides partial support via the XDomainRequest object.[1] Presto-based browsers (Opera) implement CORS as of Opera 12.00[6] and Opera Mobile 12, but not Opera Mini.[7]

Please refer to Theory section at end for more information on CORS (vs. document.domain vs. JsonP) Scenario 2: With Proxy The native XMPP communication is socket or stream based & happens over TCP port no: 5222/5223. But while connecting from the web client, some browsers dont allow to make requests to the above ports. So, we place an HTTP proxy or a connection manager in between client & Openfire server. This proxy receives the request on standard port 80 & passes them to a HTTP binding servlet that runs in Jetty container listening at port 7070/7443. All the chat & other XMPP communication is achieved by wrapping XML stanzas in the payload of HTTP POST requests. This is the implementation for: XEP-0124 & XEP-0206. [ Now achieved by sthrope.js OR JsJac.js ] It will pass the requests originating from web client to the BOSH servlet listening on port 7070. The context path for the concerned servlet is /http-bind/. So, the proxy pass will be made to /http-bind/ from the proxy server. There are 2 possibilities on configuring the proxy server:

Option 1: Using Apache/NginX between web client & Openfire Web client => Apache (or Nginx) as Proxy => Openfire In this case, the requests received on proxys port 80 of the nature: */http-bind/ will be passed to /<IP of Openfire hosted m/c>:7070/http-bind/ The proxy server will run on chat.guru.com:80 and it will talk to server at chat.guru.com:7070 and the client will be on www.guru.com:80 If this approach is employed, then the web chat related files (html+js) have to reside on this proxy to abide by the same-origin policy. But the rest of files are being served from Guru.com web server. So, we wont recommend this approach. Or We Use CORS to move the proxy on chat.guru.com and then communicate with it. In either case, the following has to be considered when deploying a proxy: For scalability & exact number of concurrent requests that can be handled, we need to look on various factors. For e.g. in case of Apache, its dependent on: Max number of processes that can be spawned Max number of threads that can be spawned per process Max time duration for which the connection will be kept open/alive before being closed. Whats being served by the Apache- simple JS, HTML will server more requests than say an app using PHP module. 5. In case of proxy to Openfire, the connection has to remain open till the HttpBindServlet on Openfire processes & sends back the response. Also, as the proxy will be part of BOSH system, it will be sitting on reasonable number of requests/connections so as to push the data immediately on availability. If say N number of users are concurrently logged into the Guru.com & thereby into chat, my guess is- proxy will be sitting on N connections at any time. Its because to achieve BOSH functionality & need to push data on client connection hold by proxy. The memory consumed on each connection to proxy will be minimal (sufficient enough for a single thread stack + socket+ other data structures if any as its the proxy pass & actual content will be served by Openfire.) Typically, Apache will be able to serve few hundred requests. Option 2: Using IIS on Guru.com as proxy Web client => IIS (Proxy+Guru.com web server) => Openfire In this case, IIS needs to be configured to act as proxy & pass */http-bind/ requests to /<IP of Openfire hosted m/c>:7070/http-bind/. The web chat files will reside on this proxy, which is also hosting rest of Guru.com site. If the same origin 1. 2. 3. 4.

policy is to be considered, then option #2 looks feasible. We will recommend this approach. This approach also fits with the current scalability setup of the Guru.com web servers.

Recommended Approach: In Scenario 2, we recommend Option 2. But overall, we recommend Scenario 1. Hence, we will go ahead with Scenario 1 Without Proxy. 6.1.1 Implementing CORS 1. Modify HttpBindServlet When the browsers have capability to send the cross domain Ajax requests, they need the support from the server to which they need to make the request. In this case, the web page originating from Guru.com needs to send the Ajax request to chat.guru.com. In this case, chat.guru.com needs to trust & accept requests from guru.com. To achieve this, we need to enable CORS for the HttpBindServlet. It needs to be modified with the following code snippets:
response.addHeader("Access-Control-Allow-Origin", "guru.com"); response.addHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS"); response.addHeader("Access-Control-Allow-Headers", "Content-Type"); response.addHeader("Access-Control-Max-Age", "86400");

Here 86400 is the time for which the preflighted requests will be cached by the browser while doing CORS. 2. Enable Pre-flighted requests

The Openfire server needs to be provisioned for handling OPTIONS request and it should be able to set the Access-Control related headers in the response. So add doOptions method in HttpBindServlet and add Access-Control related headers to the response. This is required for chrome and safari. Please read more on Pre-flighted Requests for CORS in heading 18 in this document below.

6.2 MEANS OF COMMUNICATION Using Sthrope.js / JsJac.js To send each request, user needs to create a sTrophe.Builder object to generate formatted XML that will be sent to OpenFire. Each time these requests are sent to retrieve any application data, user must pass a unique ID (called JID jabber ID) as one of the parameters. Below piece of code (pseudo code) can be used for that: Strophe.Builder = function (name, attrs) Create a Strophe.Builder object. The attributes should be passed in object notation. For example var b = new Builder('message', {to: 'emp@domail.com', from: 'freelancer@domain.com', type: set, })

In the below example creating stanza using $iq (where iq will be as root element of the XML generated (i.e. another approach of creating request/response xml) Building XML Request to send var stanza = $iq ( {to: emp@domain.com , from: freelancer@domain.com, type: 'set', id: 'JabberID' }) Building child element into stranza $iq.c('query', {xmlns: 'jabber:iq:register'}) $iq.c('ChatMessage', chatcontrol.Text) $iq.tree(); To send request generated by sTrophe.Builder below piece of code can be used sendIQ: function (elem) con.sendIQ(stanza); Retrieving XML Response var stanza = $iq ( { to: freelancer@domain.com , from: emp@domain.com, type: 'get',

})

id: 'JabberID'

Building child element into stanza $iq.c('query', {xmlns: 'jabber:iq:register'}) $iq.c('ChatMessage', chatcontrol.ResponseText) Also, everytime a request is sent to server sTrophe.Flush function should be called to flus the queue.

7. High level design & architecture


This shows the request flow pattern with Scenario 1 [ From section 6 above ]

chat.guru.com Web browser Web pages from Guru.com Chat UI/ Controls JsJaC or Stropje.js
Login to Openfire, chat, block/unblock contact & exchange all XMPP data over BOSH User logs into Guru.com C A C H E HttpBindServlet

Openfire server Jetty

Plugins
OpenIdAuthPlugin UserServicePlugin Archive Plugin

Details including: OpenID identifier, server to connect to, JID etc are sent back to client

Pull + Push

Guru.com Web server

OpenID Auth Server

MySQL
- User data including JID, roster etc. - Transcripts or Archives of chats

8. Authentication
When the user logs into the guru.com site, the web server will also now send the following additional response params in the current response. 1. JID (Jabber User ID) for user to log in to chat module. The Jabber Id(JID) is the one with which XMPP server identifies the user/client or a server in an XMPP system. Its of the form: [node@]domain[/resource] So, listed below are all the valid JIDs: chat.guru.com freelancer@chat.guru.com employer@chat.guru.com/spark For more details, refer http://tools.ietf.org/html/rfc6122#section-2 2. The domain and/or location of the chat server to connect to This is chat.guru.com Upon receiving this, the web client prepares itself to log into the chat module. To do so, it uses JavaScript based Jabber/XMPP client API (Strophe, JsJac etc.). Please note that as stated earlier, all the messages, presence etc. exchanged between client & Openfire server would be XMPP over BOSH. That will be taken care by JavaScript XMPP library like- Strophe. So, when user logs in, the following XML will be contained in HTTP POST request (shown sans <body/> element): <stream:stream from='juliet@chat.guru.com' // Jabber ID to='chat.guru.com' version='1.0' xml:lang='en' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'> The server will send the response wrapped in HTTP POST like: <stream:stream from='chat.guru.com' id='++TR84Sm6A3hnt3Q065SnAbbk3Y=' to='juliet@chat.guru.com'

// Unique session id (sid)

version='1.0' xml:lang='en' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>

JID and Salt On guru.com, when it gets an authentication request from username shrinivas to login, it will generate the JID as follows and then pass it to the web client. This JID will also be stored on guru.com We will take the username, hash it with a private salt and append @chat.guru.com to create the JID on the fly. The JIDs will not be stored on guru.com. We will use a MD5 (Sha1) algorithm for this. e.g. For username shrinivas, the returned JID will be: sXF45raniva23s@chat.guru.com (where sXF45raniva23s is created on the fly using a secret salt only known to guru.com) The salt is being added to prevent username tampering so that ellen cannot get leos chat data when both ellen and leo are logged into the system at the same time. Please note that the web client is never sending the plain username to openfire. Since all calls are in JavaScript, usernames can be easily tampered with. Hence the client will Only send hashed (obfuscated) Jabber IDs that can only be encrypted/de-crypted by the Openfire and Guru (openID) servers (which maintain the private keys). OpenID Before completing the authentication, the openfire will send the JID to guru openID which must state whether this is a valid logged in user or not. OpenID will map the JID to the username and check if this user is logged in or not at that time. If openID authenticates, then openfire will check if this JID is a valid user in its database and if so, it will also map it with its username for extra authenticity. If both are positive, then a session is created between openfire and web client. Upon successful authentication, two autonomous sessions are created one with Guru and another with OpenFire.

Storage Pre-requisites for Authentication: The Jabber ID and username are stored in Openfire The Jabber ID and username are also stored in guru.com

Authentication Architecture Diagram:

1. Client Signs-in to his/her guru.com account (over http/https). 2. Guru.com authorizes him/her and renders the guru page and chat client as well as sets the userspecific parameters (profile ID, authentication string etc.) for connecting to openfire (over http/https) 3. The chat client using the parameters set by guru.com tries to connect with openfire server (Over XMPP). 4. Guru tries to authenticate the passed credential with the openfire (over http/https.) 5. Gurus OpenID server sends its response for authentication back to openfire (over http/https.) 6. In case of OK response from OpenID server, openfire establishes a XMPP session with the client. Logout 1. When the user explicitly logs out of Guru.com site, then the web client shall first logout the user from Openfire server. Upon finishing this, it shall logout from the Guru.com server.

2. When the user is logged out due to session timeout on Guru.com, Guru.com shall also end the users session on Openfire.

9. OpenFire Plugins
The Openfire server will be having 3 plug-ins: 1. OpenIDAuthPlugin: Pulls from web & OpenId servers It implements the Plugin & PacketInterceptor interfaces. It will intercept the login packet, & then send the login request to OpenId auth server. When the OpenId server returns with AUTHENTICATED response, then the Openfire server checks whether concerned user exists in its database. Upon, successfully authenticating with server, the plug-in can ask the web-server to get the most recent roster for the logged in user. From this roster, it will make the chat contact graph for the user and store it in openfire database. It will be persisted in the Openfires user DB. This might also need partial or complete rewriting of Openfires Authentication Manager because, by default, Openfire has support for database & LDAP based user authentication. The authentication manager takes care of this. In this case, the authentication mechanism needs to query OpenId server. Once OpenId server responds in affirmative, then Openfire will crosscheck the concerned user in its DB. To achieve this functionality, we will need to modify parts of the plugin.

2.UserServicePlugin or UserAdminPlugin: Accepts push as well as pulls from web server with REST calls It implements Plugin interface. The plug-in basically provides the trigger/event based interface for the web server along with the regular admin API exposed with REST calls. On its deployment, the plug-in make the request to web server to get all the users & stores their data into the Openfire DB. The REST API calls are mentioned later in the document. 3.TranscriptsPlugin: Exposes pull to web server & possibly to web client as well It implements the Plugin interface. This plug-in will be doing 3 things: a. Construct & Monitor the cache The plug-in will construct an LRU cache for caching the transcripts exchanged between users in memory. DB will back this cache.

Transcripts (a.k.a Archives) are a bunch of IMs exchanged between two users. The archives will be stored in the MySQL DB of Openfire. When user A & B initiate the chat, a relevant object say- Transcript will be instantiated in cache. Once LRU cache finds this object as stale or exceeding certain max limit put on size of transcript it will be evicted from cache & will be persisted in the DB. The Openfire provides a Monitoring plug-in to persist the transcripts. If it aligns with the requirements then it will be used instead of custom cache. For cache size, since we will use a dedicated machine for Openfire server, we shall be able to keep it at-least of JVM heap size available to Openfire. b. The plug-in will serve REST calls from either web server or client or both to let them query about & retrieve the transcripts shared between two users. c. It will provide all chat messages exchanged between two users over a specific date range. This will also be used to fetch the 50 most recent chats of a user, account for sorting etc. Openfire already takes on the responsibility of storing the offline messages. So we may club the results from two tables with appropriate segregation & send it back to web client.

10. Expectations from the OpenFire Server


10.1 Data to be Persisted Contact list with user status of each member User status of logged in member o o o o Green Online Red Busy Orange Idle; any user not using the application for 15 minutes will be considered as idle Gray Offline. When a user is offline, their entire contact information is displayed as grayed out and disabled.

Ability to set/change status of logged in user Most recent chats (conversations) [ list of recent chats] Chat details for each conversation or of a particular conversation Conversation history Offline chats List of Favorite contacts (ability to mark a contact as favorite) Mark/Unmark a contact as favorite Chat message history grouped by date

Ability to send files, save files. View files sent/saved. Blocked user list

Some major tables will be: ofUser ofRoster ofOffline ofPresence

Roster information is stored as follows in openFire.

Here, sher has 2 contacts in his roster- admin & leo.

10.2 APIs to be exposed 1. Openfire will provide Rest APIs for trigger based events. [ To be invoked by Guru.com server ] Guru.com will make push calls to send information to OpenFire when the following events occur: i. ii. iii. iv. New employer/freelancer account added on guru An employer/freelancer account deleted on guru New contact to be added into the chat-contact list Contact to be deleted from chat-contact list.

1.1 addUser This call is made when a new user signs up on guru.com. Request parameters: username/userId, firstName, lastName, role. Response: The API shall respond back with the status of the operation using an HTTP response code and accordingly exceptions must be handled at gurus end. When the operation succeeds, the user is persisted in the Openfire DB.

1.2 deleteUser This call is made when a user is deleted from the guru.com web platform. Request parameters: username/userId Response: The API shall respond back with the status of the operation using an HTTP response code and accordingly exceptions must be handled at gurus end. 1.3 addChatContact: Invoked when employer exchanges message with a new freelancer or vice-versa (for both send and receive) Request parameters: username/userId, to whose roster the contact should be added, the username/userId of the contact to be added, contactFirstName, contactLastName, role of person being added. Response: The API shall respond back with the status of the operation using an HTTP response code and accordingly exceptions must be handled at gurus end. We will first check if the contact is already in the roster of the user currently logged in. When the operation succeeds, the contact is added to the users roster in Openfire DB. If the user is already logged into chat at that time, we will propagate the new contact to client in real time. The openfire plug-in will send back a XMPP stanza to the concerned users containing the contact details of other. The web client will process this packet & update the roster in real-time. 1.4 deleteChatContact: Invoked when employer deletes all message with a new freelancer or vice-versa (in the guru messaging system). Request parameters: username/userId, from whose roster the contact should be deleted, the username/userId of the contact to be deleted. Response: The API shall respond back with the status of the operation using an HTTP response code and accordingly exceptions must be handled at gurus end. When the operation succeeds, the contact is deleted from the users roster in Openfire DB.

Here we show the sample flowchart for addChatContact

Here we show the sample flowchart for deleteChatContact

2. Openfire will provide API calls to be invoked by web client for doing authentication and creating session with OpenFire. [ To be invoked by web client ] // explained in section on Authentication above.

Sample JS Api call pseudo Code: Call sTrphone.connect function of Strophe.Connection object and pass JID to that function to connect to OpenFire/Bosh server. connect: function ( jid, callback, wait, hold) Code snippet: var con = new Strophe.Connection(jabber-server.com); var conStatus Int; con.connect(JabberID, password, onJabberConnected); function onJabberConnected(status) { if (status === Strophe.Status.CONNECTED) { conStatus = 1; } else if (status === Strophe.Status.DISCONNECTED) { conStatus = 0; } }

3. Must provide apis to provide chat related data directly to the web client. [ To be invoked by the web client ] These will include but not limited to API calls for requests like: a. b. c. d. Filter users by their chat status only give all offline, only show all busy etc. Ability to block/unblock a user View messages with user <X> Show all chat messages for this user grouped by date also mark the new messages (these are the ones which have not been read yet) from 7 days to 2 years e. Get Latest 50 chats f. Get all chat messages for user X which are new (not read yet). g. Save the content of chat on client side

h. Sign out of chat i. etc


e.g. getTranscripts() Request parameters: username1, username2, startDate, endDate Response: The API shall respond back with the list of transcripts exchanged between two concerned users between the time duration mentioned by startDate & endDate. Each Transcript can have following property: timeStamp/date text (actual messages exchanged between the user1 & user2)

10.3 Other Expectations 1. Must make request to OpenId to authenticate user (using plugin as stated above) 2. Chat Roster Management Apis a. Pull from Guru.com by making requests to guru.com i. Populating the initial database on OpenFire side with existing user accounts. ii. Pull from guru.com the chat contact list of the user which has just authenticated with openfire. 3. Designing and implementing database schemas for the openfire usage and storing all information as desired.

11. Expectations from Guru web server


While interacting with the Openfire server, the Guru web server is assumed to take on following responsibilities: 11.1 Data to be persisted: Guru.com server will need to store following things in its DB: 1. The web server must provide the users JID to the web client, which will be later, used for Openfire authentication. The web server may store the JID in its DB. - The chat domain of the Openfire server.

For e.g. if use with username freelancer is logs into the Guru.com web server & chat domain is chat.guru.com then the JID generated for user can be: freelancer@chat.guru.com - For enhanced security & reduced chances of getting tampered, web server will add salt to JID or take a hash of it when sending to web client. 2. Chat related UI settings like: Was user on recent chats OR favorites OR all chats? Sound is off or on? Was the user logged into chat or not ?

11.2 API to be exposed 1. The web server shall provide the REST API to retrieve all Registered Users. [ Invoked by OpenFire on its initial deployment] Web server will send both usernames and JIDs to openfire.
getAllRegisteredUsers() Response: The API shall respond back with the list of all the users. The response can be in the XMl format. When the operation succeeds, all the users are persisted in the Openfire DB.

2. The web server shall provide the API to retrieve the chat configuration settings for a logged in user. [ Invoked by web client ] 3. It will also provide an API to save the updated chat configuration, which will be retrieved for subsequent user visits to the site. [ Invoked by web client ] 4. The web server will support a call that gives chat roster list of each user [ Invoked by Openfire upon authentication with each user ] The API call can look as follows:

getRoster() Request parameters: username/userId for whom to retrieve the roster Response: The API shall respond back with the complete & most recent contact list for the concerned user. The list is constructed from the internal messaging policy employed by Guru.com server. Each contact item from list can have following properties: contactUsername/userId contactFirstName contactLastName If the call is being made first time for this user by Openfire, it will persist the roster in its own table. Else, it will do a sync to check roster and update it if required.

5. The web server shall provide an API to retrieve contact details like image, company/screen name for each contact present in the roster of a logged in user. [ Invoked by web client ] The API call may look like:
getContactInfo() Request parameters: JabberID (received from OpenFire) Response: The API shall respond with all the info pertaining to the concerned user like: firstName lastName contactImageUrl contactRole etc. screen name / company name.

11.3 Other Expectations 1. The web server upon successful authentication of user shall provide all the necessary data to the client to login to openfire (like JID, location of chat server). 2. The web server will invoke the REST API calls exposed by Openfire on trigger based events that happen inside guru.com - like new message exchange, new account created etc. [Explained in section 10.2 above].

12. Expectations from OpenId Module


The OpenId server shall expose a REST API that will accept the JabberID & validate its authenticity. It will map the jabber ID to the userID and then check if the concerned user is valid & currently logged in on Guru.com server. It can return the boolean status along with any extra data queried. Openfire server will act as Relying Party and Guru.com will act as Provider Party. The communication shall take place in conformance with the Guru.com OpenId Relying party guide document. For e.g. if user named freelancer is logging into the Guru.com, then Openfire will send the authentication request to Guru.com (which is Open Id provider) with Open Id as: freelancer@guru.com And the Guru.com will provide the response like: openid.claimed_id: http://qa.guru.com/user.aspx/freelancer@guru.com Upon receiving the affirmative response from OpenId server, Openfire will verify that the concerned user exists in its DB. If the user exists, the user is successfully authenticated on chat.guru.com. Openfire can also map this JabberID back to username and make sure it is the same for added authenticity.

13. Expectations from Web client


1. The web client should be able to communicate with Openfire server using BOSH over XMPP. 2. In essence it should be able to exchange the XML stanzas for XMPP communication wrapped in HTTP or over stream. It shall be able to form the XML stanzas & parse the responses. 3. Using CORS (bot normal and pre-flighted), the web client shall take on the responsibility of directly making calls to the Openfire plug-ins. The call can be like retrieving the transcripts over yesterday etc. 4. The web client shall call guru web server to retrieve the chat configuration saved in users previous visit to site. Upon receiving such data, it shall appropriately render the web pages. 5. Once the web client gets the roster from openfire, it will get contact details of each user in the roster from guru.com. 6. It will get authentication response parameters from guru.com and initiate session with openfire. 7. It will initiate logout from openfire when guru.com logs out.

14. Scalability and Performance


1. Openfire has support for up to 20,000 concurrent users per server. 2. As per the information sent by Guru, they receive at any given time around 400 active concurrent connections on any of their web servers. Since they have 4 servers, they can at-peak have 1600 live active users at any point of time. Besides 1600 active connections, OpenFire will also receive requests from guru (on trigger events) and many requests from web client per active logged in user. We assume a factor of N requests per logged in user where N needs to be determined. The total simultaneous requests thus can be 1600 * N 3. OpenFire uses Apache Mina where we can configure the thread pool to handle the desired number of total requests. 4. We can also deploy multiple server instances of OpenFire for scaling.

15. Experimental Setup Done


a. OpenFire server is running at http://78.47.223.136 b. We have integrated a Chat Web Client Goto http://78.47.223.136/webchat/webclient.html Select HTTP Binding Enter details like: HTTP Base= /http-bind/ Jabber Server = atlogys-openfire The two user accounts created are: ritika / ritika123 rajiv / rajiv123 Please login as each user in 2 separate browser windows and chat with each other. Client uses direct CORS for communication with support for pre-flighted requests enabled on Openfire. Previous Experiment: Also did experiment with proxy The client used BOSH functionality with Apache set as proxy in between client & Openfire server. It has

the version of JsJac library used at that time of development. (Scenario 2, Option 1)

16. UI Integration of Chat on Guru.com Web Pages


This section discusses the various approaches considered for rendering the chat window on guru web pages. The following conditions must be met: 1. The chat window should render identically on all pages whether .aspx or .cfm 2. The chat window should not refresh, and it must stay in the same location when user goes from one .aspx page to the other, from one .cfm page to the other, or from an aspx page to a cfm page or viceversa. 3. The chat window must render as per the shared HTML mocks as referenced in Item # 3 and #9 in the references table above. 16.1 Recommended Solution: Deep Linking The idea is to use Ajax to only refresh the Non Chat related HTML on the page while the chat html remains unchanged. And also use Ajax Crawling techniques like #! to make the Ajax URLs SEO friendly and book-markable. This approach is explained in detail in the following sections.

Section 1:
Currently, a typical page on guru (whether .cfm or .aspx) look as follows.

If we add the chat html in a div somewhere inside the body of this page, our goal is to ensure that it does not refresh as we click on a link on this page (to goto another page). However, clicking on a new link will re-load the entire html as shown above, thereby also refreshing the chat div. We solve the problem as explained below in section2.

Section 2:

The current content of all existing pages (for each page) will be rendered inside div1. Chat html will be rendered in div2. Any links in div 1 (like header, footer links, nav bar links etc) will make Ajax calls to the guru.com web server and only reload the content of div1 (Plus also update head for things like css, js files, meta info etc). We will update the URL using #! To show the ajax click to the browser and user.

Section 3
1. The following url re-writing rules will be added to guru: Original http://www.guru.com/myadmin.cfm http://www.guru.com/PaymentReminder.cfm New http://www.guru.com/Main.cfm/#!myadmin http://www.guru.com/Main.cfm/#!PaymentReminder

http://www.guru.com/xyz.cfm Original http://www.guru.com/Messages.aspx http://www.guru.com/Payments.aspx http://www.guru.com/xyz.aspx 2. SiteMap

http://www.guru.com/Main.cfm/#!xyz New http://www.guru.com/Main.aspx/#!Messages http://www.guru.com/Main.aspx/#!Payments http://www.guru.com/Main.aspx/#!xyz

All new urls under New column of table above will get added to sitemap.xml for crawling purposes. Please see section on crawling and indexing below.

3. URL Handler Rules: The following additional url handler rules need to be added on the IIS server: Ajax requests to #!Messages.aspx get directed to the current (already existing) handler for emp/shared/Messages.aspx Ajax requests to #!PaymentReminder.cfm get directed to the handler for emp/shared/PaymentReminder.cfm And so on // Please note that these url schemas and redirect handlers can be further optimized. This can be done post initial discussion with Guru.

Section 4:
1. Next we will update all existing .aspx and .cfm pages inside the Guru / Websites / marketplace / emp[pro] folders such that they give the headless and bodyless (without the< head> and <body> tags) html content only. We will remove the basicTags like <html>, <head> and <body> . This is because the content returned by them must be rendered inside Div1 only. This is called the reducedHTML. This is just a minor modification.

E.g. Lets consider Messages.aspx . It uses emp/shared/EmployerBase.master to render the basicTags like <html>, <head>, <body>.
MESSAGES.ASPX <%@ Page Language="C#" AutoEventWireup="True" CodeBehind="Messages.aspx.cs" Title="Guru.com - Messages" ValidateRequest="false" Inherits="Guru.Websites.Marketplace.emp.Messages" MasterPageFile="~/emp/Shared/EmployerBase.Master" Culture="auto" Theme="Global" EnableEventValidation="false" %> <%@ MasterType VirtualPath="~/emp/Shared/EmployerBase.Master" %>

The emp/shared/EmployerBase.Master inturn looks like:


<%@ Master Language="C#" AutoEventWireup="true" CodeBehind="EmployerBase.master.cs" Inherits="Guru.Websites.Marketplace.emp.Shared.EmployerBase" %> <!DOCTYPE html> <html> <head runat="server"> <title></title> <asp:ContentPlaceHolder ID="head" runat="server"> <link href="/App_Themes/Global/global.css" rel="stylesheet" type="text/css" /> // SNIPPED // </style> <![endif]--> <script src="/js/localclock_3.25.js" type="text/JavaScript"></script> <asp:ContentPlaceHolder ID="empHeadCnt" runat="server" /> </asp:ContentPlaceHolder> </head> <body> <asp:PlaceHolder ID="topNavigation" runat="server"></asp:PlaceHolder> <form id="form1" runat="server"> // SNIPPED //

</body> <asp:ContentPlaceHolder id="cntJS" runat="server"></asp:ContentPlaceHolder> </html>

This will be changed so it now looks like: <%@ Master Language="C#" AutoEventWireup="true" CodeBehind="EmployerBase.master.cs" Inherits="Guru.Websites.Marketplace.emp.Shared.EmployerBase" %> <!DOCTYPE html> <html> <head runat="server"> <span id=headdata> <title></title> <asp:ContentPlaceHolder ID="head" runat="server"> <link href="/App_Themes/Global/global.css" rel="stylesheet" type="text/css" /> // SNIPPED // </style> <![endif]--> <script src="/js/localclock_3.25.js" type="text/JavaScript"></script> <asp:ContentPlaceHolder ID="empHeadCnt" runat="server" /> </asp:ContentPlaceHolder> </span> </head> <body> <asp:PlaceHolder ID="topNavigation" runat="server"></asp:PlaceHolder> <form id="form1" runat="server"> // SNIPPED // </body> <asp:ContentPlaceHolder id="cntJS" runat="server"></asp:ContentPlaceHolder> </html>

2. Furthermore, we must modify any GET/POST calls from inside the BODY section of these pages (or any direct anchor links) to NOW be in ajax. E.g. If the Messages.aspx page has a link to Payments.aspx page, instead of being a regular request, this will NOW be an ajax request to #!Payments.aspx page (which in turn as per modifications proposed in this section 3 will only give the desired reducedHTML). We will do so by adding tab=rel to all <a href> links present inside <body> of any cfm or aspx page

<a href="/Payments.aspx" rel="tab">Payments</a>

There will be JavaScript which says that when any link with tab=rel is clicked, send an Ajax request and update content of div 1 only. Then modify browser url seen by user to appropriate format depending on whether browser is html5 supported or not (Just like as seen on facebook). 3. Note, this example assumes pages in the MVP architecture Only. For legacy guru code where pages are not in MVP, we will first confirm them into MVP and then make the changes as proposed in these sections.

Section 5
Add a new handler for Main.aspx and Main.cfm.

The main goal of this page is to spit out the <html>, <head>, <body> tags with the 2 divs. It will load chat html in div2 and it will have javascript which will then load content in div 1 via an ajax request to the appropriate page. e.g. here is the sample code received when request is sent to: guru.com/Main.aspx/#!xyz is sent. /* The section below only gives bare bones pseudo-code for high level understanding */
<html> <head> 1. <head data specific to page being loaded in div 1 like title, meta tags, css links, js files for xyz.aspx> 2. <head data required for chat> <script> // This is the function that registers ajax request for all links inside div1 // that have tab=rel. $(function(){ $("a[rel='tab']").click(function(e){ pageurl = $(this).attr('href'); //gets xyz.aspx url = get stuff before . //gets xyz extension = get stuff post . //gets aspx sendAjaxRequest(url, extension, true); return false; }); }); // This is the function invoked in Main.aspx (or main.cfm) after it gets loaded to fetch page content for div1. function makeRequest() { source = window.location; // gets guru.com/Main.aspx/#!xyz

url = extract text post / from source // gets #!xyz or xyz depending on browser if (url . beginsWith(#!) { // in case of non html5 browsers url = url.post(#!) // gets everything post #! } extension = extract extension of Main in source //gets aspx sendAjaxRequest(url, extension);

// Makes the ajax request and gets the content, replaces it in div1, head, updates browser url. function sendAjaxRequest(url, extension) { 1. send ajax request to (#! + url + extension)

// This makes request to #!xyz.aspx. As per URL handler registered in section 3, this gets sent to emp/shared/xyz.aspx. As per section 4, client gets reducedHTML

2. Render the <headdata> from reducedHTML in head above (part 1) 3. Render the rest of the html received in div 1. 4. If (nonHtml5 supported Browser) { 5. Change url in address bar to be guru.com/Main + extension + /#! + url; } else { 6. window.history.pushState(guru.com/Main + extension + / + url ;); } } </script> </head> <body onload = makeRequest()> <div 1> </div1> <div2> Chat html </div2> </html>

This is a simple master page which loads two divs. The first div after loading on client side, gets its content from ajax. It also updates head section for the page requested to load the css, js files required by this page in particular. Inside div1, if user clicks on any link like <a rel=tab href=/payments.aspx>payments</a>, it sends an ajax call to #!payments.aspx which is then handled as seen in section 3. The url of the browser is then updated to be a) Guru.com/Main.aspx/#!Payments (for Non html5 browsers) Or to b) Guru.com/Main.aspx/Payments (for html5 browsers)

Thus users can bookmarks both links of type a or b. The JavaScript function makeRequest above can handle both kinds of urls.

Javascript Libraries: Please note that the JavaScript code shown above is just pseudo code. The actual code will use deep linking libraries like swfadress etc. We will use appropriate libraries
SWFAddress.onChange = function () { var url = window.location.pathname + "home/" + SWFAddress.getValue(); $.ajax({ type: "GET", url: url, dataType: "html", success: function (result) { $("#dvMainContent").html(result); } });

SWFAddress Library Please see for more details: http://www.asual.com/jquery/address/docs/#

IMPORTANT NOTE:
The <head> contains css, js and meta tags specific to the page being rendered. Now, if pages will be rendered using ajax, we must update the links, cs files and meta info on the <head> section using javascript. ** It would be best to refactor the existing code for rendering information in <head> tag in a more modular manner so it can be streamlined and easily updated with Javascript.

Alternate to be discussed with Guru In section 4, instead of sending reducedHTML, we can send the same HTML (as guru sends now) w/o any modification. Then in section 5, the javascript function can parse out the <head> and <body> information and render it in div 1 and <head> as explained above. This depends on re-factoring complexity at Guru and legacy code which needs to be discussed. URL Naming Convention: Please note that the new urls can be further optimized. The current format is being proposed for basic understanding. We can choose to use the same or optimize it. Lets discuss.

Section 6

Ajax Crawling One common problem with ajax is that urls are not crawlable by search engines. Furthermore, the browser back, history buttons will not honor such urls. Users cant bookmark or email them either. Google has recently proposed a strategy for making ajax urls crawlable. All major sites like Facebook are using this technique. This requires adding #! Into the URL which is specially treated by crawlers like the googleBot. Please see this specification from Google: https://developers.google.com/webmasters/ajax-crawling/docs/getting-started?hl=es In html5 browsers, this has been made part of the standard spec. It is called onpopstate. Please see this for more information on Html5. http://spoiledmilk.com/blog/html5-changing-the-browser-url-without-refreshing-page

Section 7
Control Flow 1. User logs in as employer. 2. User is redirected to http://www.guru.com/Main.cfm/#!myadmin 3. The code as shown in section 5 is invoked. Div 1 loads and div2 loads with the chat html in it. 4. the body upon loading Div 1 upon loading makes ajax call to #!myadmin.cfm which is handled by the current handler for /myadmin.cfm. 5. As per the modifications proposed in section 3, the reducedHTML is returned which is loaded in head and div1. So Div1 now shows the current myadmin.cfm page as seen on guru.com. 6. The page will look as follows: Div 1 (myadmin) Header Nav Bar <a tab=rel href=/linkA.aspx>link A</a>

Div 2 (chat)

7. If user clicks on LinkA, then as per section 3, this sends an ajax request to /#!linkA.aspx and the returned HTML is rendered in div 1 only without refreshing the chat window. 8. Depending on whether the users browser is HTML5 compatible or not, the url in the address bar is updated to: http://www.guru.com/Main.aspx/#!linkA OR http://www.guru.com/Main.aspx/linkA

Section 8
Bookmarking Lets suppose a user bookmarks a URL like http://www.guru.com/Main.cfm/#!myadmin or they bookmark something like http://www.guru.com/Main.cfm/myadmin When they copy paste this url in the browser, then section 5 will get invoked and things will work as explained in section 5.

Section 9
Old URLs. Lets suppose users find on old urls in their browser history or bookmarks like: http://www.guru.com/Messages.aspx As per the url re-write urls, this will get re-directed to http://www.guru.com/Main.aspx/#!Messages Then section 5 will get invoked and things will work as expected.

Section 10:
How will SEO work? 1. We will add the new urls to sitemap.xml as mentioned in section 3. 2. When the crawler sends a request like http://www.guru.com/Main.aspx/#!Messages, the guru web server will receive something like http://www.guru.com/Main.aspx/_escaped_fragment_Messages from the crawler. (This is as per the google defined specs under: https://developers.google.com/webmasters/ajax-crawling/docs/getting-started?hl=es ) 3. The output now will need to be an HTML snapshot rather than the regular page. The web server will give the output as identified in section 5 above. 4. This will then be passed through a headless browser like HtmlUnit on the web server. 5. The output of the headless browser will be the html snapshot given to the crawler. NOTE: A crawler like googleBOT, unlike a browser, cannot execute JavaScript. Thus we must return an HTML snapshot to the crawler using a headless browser like HtmlUnit. Please note that An HTML snapshot is all the content that appears on the page after the JavaScript has
been executed.

Thus the headless browser will make then ajax call for div1 and give the final page to the crawler for indexing.

Alternate approaches that were considered and rejected for various reasons Solution 1: User Control with Update Panel Create a Chat folder under User Controls of Marketplace website. Create Chat.ascx User Control under Chat folder. Add a new content place holder cph2 in the following master pages under emp/shared & pro/shared:
o o o o o EmployerBase.Master.master EmployerMaster.master FreelancerMaster.Master FreelancerBase.Master EmpEditMasterPage.Master,EmployerBaseMasterPage.master,FreelancerBaseMasterPage.maste r,FreelancerEditMasterPage.master,FreelancerSTMasterPage.master,FreelancerSTMasterPage.m aster

Note the following master pages of legacy code also need to be considered:

Add Chat.ascx under the cph2 All the existing pages of GURU.com (the way they are) will reside in content place holder cph1. Create an Update Panel upl1 that will contain cph1. Create an Update Panel upl2 that will be contain cph2. The updatePanel will prevent the page refresh of the chat window as user works on guru.com.

Concerns: When user will navigate from one master page to another master page whole page will be refreshed. In other words, when I click on a link, then if both the source and target page use the same master page, the chat window will not be refreshed. However, if the source and target page use separate master pages, then the chat window will get refreshed.

Gurus Scenario:
a) emp/Messages.aspx --> uses emp/shared/EmployerBase.master b) emp/Payments.aspx --> uses emp/shared/EmployerMaster.master This means that when user will go from Guru.com/Messages.aspx TO guru.com/Payments.aspx The chat window will refresh. c) emp/modify_account.aspx uses MarketPlace/MasterPages/EmployerBaseMasterPage.master d) emp/ChangeUsernamePass.aspx --> uses MarketPlace/MasterPages/EmployerBaseMasterPage.master

This means that when user will go from Guru.com/modify_account.aspx TO guru.com/ChangeUsernamePass.aspx, The chat window will NOT refresh. Further Analysis We tried creating a parent-to-all top level master page which could be used by all aspx pages. This way all aspx pages would have the same top level master page and hence the chat should never

refresh. However, on experimentation, we realized that because of the nesting of separate inner layer master pages, the chat window still kept getting refreshed. Hence this approach was rejected. Solution 2: User Control with HTML Frames (NOT RECOMMENDED) o Create one top level HTML page (e.g. index.htm) under the root folder of Marketplace website. o This page will contain 2 frames. In one frame, render the current guru.com pages. In the 2nd frame, render the web chat client. Both frames will point to guru.com domain. o o
o o o o o

Create Chat.ascx User Control under Chat folder. Add content place holder cph2 in the following master pages in emp/shared and pro/shared:
EmployerBase.Master.master EmployerMaster.master FreelancerMaster.Master FreelancerBase.Master EmpEditMasterPage.Master, EmployerBaseMasterPage.master, FreelancerBaseMasterPage.master, FreelancerEditMasterPage.master, FreelancerSTMasterPage.master, FreelancerSTMasterPage.master

Note the following master pages of legacy code also need to be considered:

o o o o

Add Chat.ascx under the cph2. Let all existing pages of GURU.Com reside in cph1. Put all existing web site pages in Iframe 1 and web chat client in Iframe 2. Use Jquery to distinguish calls for which Iframe and refresh accordingly. This way across master pages, the chat window will not refresh.

Concerns: While implementing the Iframe concept to prevent page refresh across the pages and Master pages, the user browser URL will remain the same. Iframes are also Not recommended for SEO purposes.

17. Chat Module on Guru


1. We will add new Module called Chat in guru websites marketplace. This Chat module will contains all the view pages and Chatcontroller.cs class 2. View pages will communicate with ChatController.cs 3. Chatcontroller.cs file will communicate with ChatService under \GuruFramework\Guru.Services.Hosts.Web\ChatService.svc.

4. We will create new WCF service ChatService.svc. This service will contain all the method that requires communication with open fire (between guru and openfire).

18. Theory (and definitions for FUN Learning )


XMPP The Extensible Messaging and Presence Protocol (XMPP) is an application profile of the Extensible Markup Language (XML) that enables the near-real-time exchange of structured yet extensible data between any two or more network entities. XMPP provides a technology for the asynchronous, end-toend exchange of structured data by means of direct, persistent XML streams among a distributed network of globally addressable, presence-aware clients and servers.

XMPP vs. REST Because this architectural style involves ubiquitous knowledge of network availability and a conceptually unlimited number of concurrent information transactions in the context of a given client-to-server or server-to-server session, we label it "Availability for Concurrent Transactions" (ACT) to distinguish it from the "Representational State Transfer" [REST] architectural style familiar from the World Wide Web. XMPP, therefore, is more relevant for applications like IM and chat.

XMPP Transportation The original and "native" transport protocol for XMPP is Transmission Control Protocol (TCP), using open-ended XML streams over long-lived TCP connections. This native XMPP communication is socket or stream based & happens over TCP port no: 5222/5223.However, web clients (or browsers) need HTTP for communication so in such situations XMPP has to be wrapped in HTTP payloads. (Via HTTP) As an alternative to the TCP transport, the XMPP community has also developed an HTTP transport for web clients as well as users behind restricted firewalls. In such scenarios the TCP port used by XMPP is blocked, and the server can listen on the normal HTTP port or one with an HTTP binding. Various websites use this to allow people to sign in to XMPP server via a browser. Originally, XMPP could use HTTP in two ways: polling binding BOSH This specification defines how the Bidirectional-streams Over Synchronous HTTP (BOSH) technology can be used to transport XMPP stanzas. The result is an HTTP binding for XMPP

communications that is useful in situations where a device or client is unable to maintain a longlived TCP connection to an XMPP server. With BOSH, the client uses HTTP long polling to receive messages as soon as they are sent. This push model of notification is more efficient than polling, where many of the polls return no new data. The IANA-registered port for BOSH is actually 5280, not 80. There are JavaScript libraries like sthrope.js and JsJac.js that do XMPP over BOSH. Bosh (vs. Ajax) Bosh is implemented using Ajax only. BOSH is designed to transport any data efficiently and with minimal latency in both directions. For applications that require both "push" and "pull" semantics, BOSH is significantly more bandwidth-efficient and responsive than most other bidirectional HTTP-based transport protocols and the techniques now commonly known as "Ajax". However, just like AJAX, BOSH is also restricted by the same origin policy of the browser. (Via WebSockets) A perhaps more efficient transport for real-time messaging is WebSocket, a web technology providing for bi-directional, full-duplex communications channels over a single TCPconnection. Experimental implementations of XMPP over WebSocket exist, and a (now-expired) InternetDraft documenting this approach was published at the IETF but not yet standardized. WebSockets is a HTML5 feature and it is only supported by latest browsers like Firefox 6 (named MozWebSocket), Google Chrome 14 and Internet Explorer 10 developer preview. There are JavaScript libraries that do XMPP over websockets.

Other things to study: COMET, Reverse HTTP etc. Same Origin Policy The policy permits scripts running on pages originating from the same site to access each other's methods and properties with no specific restrictions, but prevents access to most methods and properties across pages on different sites. Ways to get around SOP: Additional document.domain logic An important extension to the same origin policy implemented for JavaScript DOM access (but not for most of the other flavors of same-origin checks) is that two sites sharing a common toplevel domain may opt to communicate despite failing the "same host" check by each setting

their respective document.domain DOM property to the same qualified, right-hand fragment of their current host name. For example, if http://en.example.com/ and http://fr.example.com/ both set document.domain to "example.com", they would be from that point on considered sameorigin for the purpose of DOM manipulation.

CORS Cross-origin resource sharing (CORS) is a web browser technology specification which defines ways for a web server to allow its resources to be accessed by a web page from a different domain.[1] Such access would otherwise be forbidden by the same origin policy. CORS defines a way in which the browser and the server can interact to determine whether or not to allow the cross-origin request.[2] It is a compromise that allows greater flexibility, but is more secure than simply allowing all such requests. CORS is supported by all browsers based on the following layout engines:

Gecko 1.9.1 (Firefox 3.5,[3] SeaMonkey 2.0[4]) and above. WebKit (Initial revision uncertain, Safari 4 and above,[1] Google Chrome 3 and above, possibly earlier)[5] MSHTML/Trident 4.0 (Internet Explorer 8) provides partial support via the XDomainRequest object.[1] Presto-based browsers (Opera) implement CORS as of Opera 12.00[6] and Opera Mobile 12, but not Opera Mini.[7]

JsonP CORS can be used as a modern alternative to the JSONP pattern. While JSONP supports only the GET request method, CORS also supports other types of HTTP requests. Using CORS enables a web programmer to use regular XMLHttpRequest, which supports better error handling than JSONP. On the other hand, JSONP works on legacy browsers which preclude CORS support. CORS is supported by most modern web browsers. Also, whilst JSONP can cause XSS issues where the external site is compromised, CORS allows websites to manually parse responses to ensure security.[2][1

Web Sockets WebSocket is a web technology providing for bi-directional, full-duplex communications channels over a single TCP connection. The WebSocket API is being standardized by theW3C, and the WebSocket protocol has been standardized by the IETF as RFC 6455.[1]

WebSocket is designed to be implemented in web browsers and web servers, but it can be used by any client or server application. WebSocket also requires web applications on the server to be able to support it. The WebSocket protocol makes possible more interaction between a browser and a web site, facilitating live content and the creation of real-time games. This is made possible by providing a standardized way for the server to send content to the browser without being solicited by the client, and allowing for messages to be passed back and forth while keeping the connection open. In this way a two-way (bidirectional) ongoing conversation can take place between a browser and the server. A similar effect has been achieved in non-standardized ways using stop-gap technologies such as Comet. In addition, the communications are done over the regular TCP port number 80, which is of benefit for those environments which block non-standard Internet connections using a firewall.

Preflighted Requests with CORS While making CROS Ajax requests, browsers can proceed in one of the following two ways: a. Make the POST, GET etc requests directly to the different domain. (These are generally called simple requests & don't negotiate much about the headers.)

b. Make what is called as "Preflighted" request before making the actual POST, GET requests. The http method with which this request is made is - OPTIONS. This preflighted request is like handshaking & agrees upon the specific http methods, headers to be exchanged between browser & server. Once this preflighted request is successful, the browser caches this till the time mentioned by server (with header "Access-Control-Max-Age") and can make the GET, POST directly without making the OPTIONS request again for that time period. Browsers like Firefox and IE (version 9) use option a. They directly made POST requests. Browsers like Safari and Chrome use option b. More Information: https://developer.mozilla.org/en/HTTP_access_control http://www.bennadel.com/blog/2327-Cross-Origin-Resource-Sharing-CORS-AJAX-RequestsBetween-jQuery-And-Node-js.htm