I have a visual C# application that connects via WebSocket to my remote Node.js server that is deployed using Heroku.
The server uses the npm WebSocket module "ws" to create a WebSocket-Server.
The C# client application uses the WebSocketSharp Library from this GitHub repository: https://github.com/sta/websocket-sharp to create a WebSocket-Client that connects to the server.
Here is the necessary Node Server server.js code:
require('dotenv').config()
var express = require('express');
const API = require('./api_handler').api;
const PORT = process.env.PORT || 5000;
const HOSTNAME = process.env.HOST || '127.0.0.1';
const app = express();
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
app.post('/', function(req, res){
api.Auth(req.body,res);
});
app.get('/', function(req, res){
res.send(':)');
});
const httpServer = app.listen(PORT, function () {
console.log('Server running at http://' + HOSTNAME + ':' + PORT + '/');
});
const api = new API(httpServer);
Here is the Node Server ApiHandler.js code
class API_Handler{
constructor(httpServer){
const Database = require('./database').db;
const { Server : wsServer} = require('ws');
this.db = new Database();
this.wss = new wsServer({ server: httpServer });
this.actions = {write : 'write', read : 'read', authenticate : 'authenticate'};
this.operations = {insert : 'insert', update : 'update', delete : 'delete', login : 'login', logout : 'logout'};
this.template = {action : 'action', operation : 'operation',categories : 'categories',category : 'category',IDs : 'ids',fields : 'fields',data : 'data',userid : 'userid',password : 'password',Token : 'Token',Logged : 'logged'};
this.status = {ok : 'ok', error : 'error'};
this.CLIENT_TOKENS = [];
this.wsClients = new Object();
this.ClientIDs = new Object();
this.wss.on('connection', (client,req) => {
var cookie = this.CookieParseJSON(req.headers.cookie)
if(this.CLIENT_TOKENS.includes(cookie.Token)){
this.CLIENT_TOKENS.splice(this.CLIENT_TOKENS.indexOf(cookie.Token), 1);
this.wsClients[cookie.Token] = client;
client.on('message', (msg) => {
this.handle(JSON.parse(msg),client);
});
client.on('close', () => {
console.log('Client disconnected');
});
console.log('Client connected');
this.InitializeClient(client);
}
else{
console.log('Unauthorized Client connected');
client.close();
}
});
}
async Auth(req,res){
if(this.template.password in req){
var tmp = JSON.parse(JSON.stringify(req));
tmp.password = (Array.from({length:req.password.length}).map(x=>'*')).join('');
console.log(tmp);
}else{console.log(req);}
var result;
if(this.template.action in req){
if (req.action === this.actions.authenticate){
if(this.template.operation in req){
if(req.operation === this.operations.login){
if(this.template.userid in req && this.template.password in req ){
result = await this.executeLogin(req);
}else{result = this.missingAuthCredentialsResult();}
}else{result = this.invalidAuthOperationResult();}
} else{result = this.noAuthOperationResult();}
}else if(req.operation === this.operations.read || req.operation === this.operations.write || req.operation === this.operations.logout){
result = this.UnAuthedActionResult();
} else{result = this.invalidActionResult();}
}else{result = this.noActionResult();}
res.json(result);
}
async handle(req,client){
console.log(req);
var result;
if(this.template.Token in req){
if(req.Token in this.wsClients){
if(this.template.action in req){
if(req.action === this.actions.authenticate){
if(this.template.operation in req){
if(req.operation === this.operations.logout){
await this.executeLogout(req);
client.close();return;
}else{result = this.invalidAuthOperationResult();}
} else{result = this.noAuthOperationResult();}
}if (req.action === this.actions.read){
if(this.template.categories in req){
if(this.db.validateCategories(req.categories)){
result = await this.executeRead(req);
client.send(JSON.stringify(result));return;
}else{result = this.invalidCategoriesResult();}
}else{result = this.noCategoriesResult()}
}else if (req.action === this.actions.write){
if(this.template.category in req){
if(this.db.validateCategory(req.category) && this.db.isWritableCategory(req.category)){
if(this.template.operation in req){
if(req.operation === this.operations.insert){
if(this.template.data in req){
await this.executeInsert(req);
return;
}else{result = this.noDataResult()}
}else if(req.operation === this.operations.update){
if(this.db.isUpdatableCategory(req.category)){
if(this.template.IDs in req){
if(this.template.fields in req && Array.isArray(req.fields) && req.fields.length > 0){
if(this.template.data in req){
await this.executeUpdate(req);
return;
}else{result = this.noDataResult()}
}else{result = this.noFieldsResult()}
}else{result = this.noIDsResult()}
}else{result = this.invalidCategoryResult();}
}else if(req.operation === this.operations.delete){
if(this.template.IDs in req){
await this.executeDelete(req);
return;
}else{result = this.noIDsResult()}
}else{result = this.invalidOperationResult();}
}else{result = this.noOperationResult();}
}else{result = this.invalidCategoryResult();}
}else{result = this.noCategoryResult();}
}else{result = this.invalidActionResult();}
}else{result = this.noActionResult();}
}else{result = this.invalidTokenResult();}
}else{result = this.noTokenResult();}
client.send(JSON.stringify(result));
client.close();
}
async executeLogin(req){
if(await this.db.authenticate(req.userid,req.password)){ //successfully logged in
console.log("Auth Passed");
var res = new Object();
var token = this.hex();
res[this.template.Token] = token;
res[this.template.Logged] = true;
this.CLIENT_TOKENS.push(token);
this.ClientIDs[token] = req.userid;
return new Promise((resolve,reject) =>{resolve ({ status : this.status.ok, message : this.messages.success.loggedIn, result: res});});
}else{
console.log("Auth Failed");
var res = new Object();
res[this.template.Logged] = false;
return new Promise((resolve,reject) =>{resolve ({ status : this.status.ok, message : this.messages.error.loggedIn, result: res});});
}
}
async executeLogout(req){
this.wsClients[req.Token].close();
delete this.wsClients[req.Token];
delete this.ClientIDs[req.Token];
}
async executeRead(req){
req.categories = this.removeDuplicates(req.categories);
var res = new Object();
var promises = [];
for(var i = 0; i < req.categories.length; i++){ promises[i] = this.db.select(req.categories[i]); }
await Promise.all(promises).then( (results) => {
for(var i = 0; i < results.length; i++){
res[req.categories[i]] = (results[i].command === 'SELECT')?{count: results[i].rowCount, values: results[i].rows} : this.messages.error.selectCategory;
}});
return new Promise((resolve,reject) =>{ resolve ({ status : this.status.ok, message : this.messages.success.read, result: res});});
}
async executeInsert(req){
for(var i = 0; i < req.data.length; i++){
var dbResponse = await this.db.insert(req.category,req.data[i],this.ClientIDs[req.Token]);
}
this.UpdateClientData(req,(req.category === this.db.tables.Transactions || req.category === this.db.tables.ItemTypes));
}
async executeUpdate(req){
for(var i = 0; i < req.ids.length; i++){
var dbResponse = await this.db.update(req.category,req.ids[i],req.fields,req.data[i],this.ClientIDs[req.Token]);
}
this.UpdateClientData(req);
}
async executeDelete(req){
req.ids = this.removeDuplicates(req.ids);
for(var i = 0; i < req.ids.length; i++){var dbResponse = await this.db.delete(req.category,req.ids[i],this.ClientIDs[req.Token]);}
this.UpdateClientData(req);
}
async InitializeClient(client){
var read = await this.ReadInit();
client.send(JSON.stringify(read));
}
async ReadInit(){
return this.executeRead({categories : this.db.tableNames});
}
async UpdateClientData(req, updateSender = false){
var cats = [req.category];
if(req.category === this.db.tables.ItemListings){cats.push(this.db.tables.Transactions);}
var read = await this.ReadAll(cats);
var readString = JSON.stringify(read);
this.wss.clients.forEach((client) => {
if(!updateSender || client !== this.wsClients[req.Token]){
client.send(readString);
console.log("REFRESH: Client updated, columns: " + cats);
}else{
console.log("REFRESH: Sender-Client was skipped");
}
});
};
async ReadAll(cats){
return this.executeRead({categories : cats});
}
removeDuplicates(array){return [...new Set(array)]; }
hex(){
return this.randHex(16);
}
randHex(len) {
var maxlen = 8;
var min = Math.pow(16,Math.min(len,maxlen)-1);
var max = Math.pow(16,Math.min(len,maxlen)) - 1;
var n = Math.floor( Math.random() * (max-min+1) ) + min;
var r = n.toString(16);
while ( r.length < len ) { r = r + this.randHex( len - maxlen ); }
return r;
}
CookieParseJSON(cookieStr){
var sep = cookieStr.indexOf('=');
var key = cookieStr.substr(0,sep);
var value = cookieStr.substr(sep+1,cookieStr.length - sep -1);
var obj = new Object();
obj[key] = value;
return obj;
}
}
module.exports.api = API_Handler;
Here is the necessary C# Client Application Code
public void init_Socket() {
wsSocket = new WebSocket(Socket_URL);
wsSocket.OnOpen += (sender, e) => {
Console.WriteLine("Connected to server at "+Socket_URL);
};
wsSocket.OnClose += (sender, e) => {
setToken(NO_TOKEN);
Console.WriteLine("Disconnected from server! - " + e.Code.ToString());
};
wsSocket.OnMessage += (sender, e) => {
if (wsSocket.ReadyState == WebSocketState.Open && e.IsText) {
Console.WriteLine("Server Message: " + e.Data);
ReadHandler handler = new ReadHandler(null);
handler.handle(e.Data);
}
};
}
public void setToken(string token) {
if(wsSocket != null) {
wsSocket.SetCookie(new Cookie(Props.Token, token));
}
}
public void SocketConnect() {
wsSocket.Connect();
}
private void SendSocketMessage(APIRequest req, APIResponseHandler handler) {
Console.WriteLine("SOCKET MESSAGE: " + req.ToString());
if (wsSocket.IsAlive && wsSocket.ReadyState == WebSocketState.Open) {
wsSocket.SendAsync(req.ToString(), new Action<bool>((bool completed) => {
Console.WriteLine("SENDING completed!");
})
);
} else{ Console.WriteLine("Socket must be alive in order to send a message.");}
}
So how it works is that when a client updates information on the server it sends the updated information to the server and the server then sends this updated information to all its clients except the sender.
When the Clients receive updated information from the server they update their local copies to match the data received from the server.
The problem is that after the client has been connected to the server via WebSocket for about a minute, the client throws a WebSocketException with the message "The header of frame cannot be read from the stream".
A quick google search leads me to this GitHub issue https://github.com/sta/websocket-sharp/issues/202.
From the above-mentioned link:
"
We managed to fully fix it by modifying
ReadBytesAsync(this Stream stream, int length, Action completed, Action error) in Ext.cs.
We noticed that 'NetworkStream.EndRead' can return zero (0) bytes even when connection is not being closed. Close reading of documentation of 'EndRead' only says that zero bytes is returned when connection is closed. Other way around is not always true, receiving 0 bytes does not always mean connection is being closed it seems. It is quite common to get zero bytes returned even when the connection is not being closed. Happens once in a while with Chrome, and seems to happen more often with Firefox.
Therefore replacing:
if (nread == 0 || nread == length)
with:
if(nread == length)
fixes the problem. It makes sure that when one is waiting for example for the two frame bytes one really gets two bytes back.
".
But on inspecting the code in the WebSocket-Sharp library the above-quoted fix has already been applied to the library(the post was from 2016 so it was probably pathed).
The code from the library in 2020:
public static class Ext{
//Other functions and properties of Ext goes here
internal static void ReadBytesAsync (this Stream stream,int length,Action<byte[]>
completed,Action<Exception> error){
var buff = new byte[length];
var offset = 0;
var retry = 0;
AsyncCallback callback = null;
callback =
ar => {
try {
var nread = stream.EndRead (ar);
if (nread <= 0) {
if (retry < _retry) {
retry++;
stream.BeginRead (buff, offset, length, callback, null);
return;
}
if (completed != null)
completed(buff.SubArray(0, offset));
return;
}
if (nread == length) {
if (completed != null)
completed (buff);
return;
}
retry = 0;
offset += nread;
length -= nread;
stream.BeginRead (buff, offset, length, callback, null);
}
catch (Exception ex) {
if (error != null) {
error(ex);
}
}
};
try {
stream.BeginRead (buff, offset, length, callback, null);
}
catch (Exception ex) {
if (error != null)
error (ex);
}
}
}
Another strange thing is that when I run the server locally and connect to it this problem never occurs. It only happens when I connect to the remote copy that is deployed on Heroku.
ScreenCast Video Screen Capture: https://www.screencast.com/t/iwbNw1qwzGa
When you see the screen capture. It will show the first part where I demonstrate the calling of API using the Angular UI and the second part is where I use Swagger UI to call the API. You can see there that the first part displays calls the API and returns 0 records on the response when executing the GetAll() function of the default method of the ASP.Net Zero. But in the second part where it executes the API via swagger it returns the expected value from the DB. Please help on this issue. Thanks in advance.
See details of my code:
Component
ngOnInit(): void {
this.loadGroupHeaderCombo();
}
loadGroupHeaderCombo()
{
this._groupHeadersService.getAllGroupHeaderCombo()
.pipe(finalize(() => this.primengTableHelper.hideLoadingIndicator()))
.subscribe(result =>{
this.groupHeaderNamesSelectItems = _.map(result.groupHeaderNames, function(groupHeader) {
return {
label: groupHeader.displayText, value: groupHeader.value
};
});
return result;
});
}
Service-proxies
getAllGroupHeaderCombo(): Observable<GetGroupHeaderOutput> {
let url_ = this.baseUrl + "/api/services/app/GroupHeaders/GetAllGroupHeaderCombo";
url_ = url_.replace(/[?&]$/, "");
let options_ : any = {
observe: "response",
responseType: "blob",
headers: new HttpHeaders({
"Accept": "application/json"
})
};
return this.http.request("get", url_, options_).pipe(_observableMergeMap((response_ : any) => {
return this.processGetAllGroupHeaderCombo(response_);
})).pipe(_observableCatch((response_: any) => {
if (response_ instanceof HttpResponseBase) {
try {
return this.processGetAllGroupHeaderCombo(<any>response_);
} catch (e) {
return <Observable<GetGroupHeaderOutput>><any>_observableThrow(e);
}
} else
return <Observable<GetGroupHeaderOutput>><any>_observableThrow(response_);
}));
}
protected processGetAllGroupHeaderCombo(response: HttpResponseBase): Observable<GetGroupHeaderOutput> {
const status = response.status;
const responseBlob =
response instanceof HttpResponse ? response.body :
(<any>response).error instanceof Blob ? (<any>response).error : undefined;
let _headers: any = {}; if (response.headers) { for (let key of response.headers.keys()) { _headers[key] = response.headers.get(key); }};
if (status === 200) {
return blobToText(responseBlob).pipe(_observableMergeMap(_responseText => {
let result200: any = null;
let resultData200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
result200 = GetGroupHeaderOutput.fromJS(resultData200);
return _observableOf(result200);
}));
} else if (status !== 200 && status !== 204) {
return blobToText(responseBlob).pipe(_observableMergeMap(_responseText => {
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
}));
}
return _observableOf<GetGroupHeaderOutput>(<any>null);
}
API Function
public async Task<GetGroupHeaderOutput> GetAllGroupHeaderCombo()
{
var output = new GetGroupHeaderOutput();
//Issue is ocurring here
var x = _groupHeaderRepository.GetAll();
//GroupHeader
output.GroupHeaderNames = _groupHeaderRepository
.GetAll()
.Select(s => new ComboboxItemDto
{
DisplayText = s.GroupTitle,
IsSelected = false
})
.ToList();
}
what i notice are 1.from your API your change list async
2.clean your service.proxy file and run 'refresh.bat' in your nswag file to generate new proxies.
I'm using javascript to set the value of a cookie when I open the debugger panel so that if the user has already opened it, it will automatically open when they reload the page.
Here is the javascript:
jQuery(document).ready(function () {
DebuggingPanel.init(jQuery);
DebuggingPanel.GetPanelState();
});
DebuggingPanel.GetPanelState = function () {
jQuery.ajax({
url: "/sitecore modules/DebuggingPanel/DebuggingPanel.asmx/GetPanelState",
type: 'POST',
success: function(data) {
if (data.open === true) {
DebuggingPanel.TogglePanel();
}
}
});
}
DebuggingPanel.TogglePanel = function (changeState) {
var tinyDiv = $('.debuggingPanel.tinyDiv');
if (tinyDiv.text() == '+') {
tinyDiv.text('-');
DebuggingPanel.GetInformation();
DebuggingPanel.panel.slideDown();
interval = setInterval(DebuggingPanel.GetInformation, 5000);
if (changeState) {
DebuggingPanel.SetPanelState("open");
}
} else {
tinyDiv.text('+');
DebuggingPanel.panel.slideUp();
clearInterval(interval);
if (changeState) {
DebuggingPanel.SetPanelState("closed");
}
}
};
tinyDiv.click(function () {
DebuggingPanel.TogglePanel(true);
});
And here are the methods related to the cookie:
public void SetPanelState(string state)
{
var panelCookie = HttpContext.Current.Response.Cookies["PanelState"];
if (panelCookie == null)
{
panelCookie = new HttpCookie("PanelState") {Value = state};
HttpContext.Current.Response.Cookies.Add(panelCookie);
}
else
{
HttpContext.Current.Response.Cookies["PanelState"].Value = state;
}
}
[ScriptMethod(ResponseFormat = ResponseFormat.Json), WebMethod(EnableSession = true)]
public void GetPanelState()
{
var panelCookie = HttpContext.Current.Response.Cookies["PanelState"];
var data = new PanelState(){open = false};
if (panelCookie == null || panelCookie.Value == null)
{
data.open = false;
}
else if (panelCookie.Value == "open")
{
data.open = true;
}
WriteOut(data);
}
In debugging the cookie looks as though it is getting the value correctly, but the next time I go into GetPanelState(), panelCookie.Value is always "" (not "open" as it should be, or "closed", which would indicate it was set by the toggle).
This happens when I reload the page, and it also happens when I call GetPanelState() at the end of SetPanelState(); panelCookie.Value = "open" in SetPanelState() but then equals "" in GetPanelState()
When you are reading from the Cookie, you need to use the Request instead of the response. So your code will be as follows:
public void SetPanelState(string state)
{
var panelCookie = HttpContext.Current.Response.Cookies["PanelState"];
if (panelCookie == null)
{
panelCookie = new HttpCookie("PanelState") {Value = state};
HttpContext.Current.Response.Cookies.Add(panelCookie);
}
else
{
HttpContext.Current.Response.Cookies["PanelState"].Value = state;
}
}
[ScriptMethod(ResponseFormat = ResponseFormat.Json), WebMethod(EnableSession = true)]
public void GetPanelState()
{
//It is here that you are reading the cookie.
var panelCookie = HttpContext.Current.Request.Cookies["PanelState"];
var data = new PanelState(){open = false};
if (panelCookie == null || panelCookie.Value == null)
{
data.open = false;
}
else if (panelCookie.Value == "open")
{
data.open = true;
}
WriteOut(data);
}
Thanks
I have been given this API which is based in PHP but I need to convert it into C#. This is only the first method and this is my result.
I seem to be missing a lot. Will this c# method function as the PHP ones does
PHP:
// Call SVP Service
function callSVPService($service, $arg, $raw_data = false) {
global $entry_point;
$count_attempts = 3;
$token_error_codes = array(2004, 2005, 2006);
$counter = 0;
// Make a attempts to call a SVP API service.
// With this we can automatically generate a new token if the token is empty,
// the token is not valid or the validation period of the token is expired.
while(true) {
$counter++;
$token_response = getToken();
// If token error occurs - exit calling service.
if($token_response['error_code']) {
return array('error_code' => $token_response['error_code'], 'response' => '');
}
$token = $token_response['token'];
$arg['token'] = $token;
$response = callSVP_API_Service($service, $arg);
$result = getTextBetweenTags($response, 'result', true);
if(!$result) {
if(!$raw_data) {
return array('error_code' => -1, 'response' => $response);
}
else if(!$response) {
return array('error_code' => -1, 'response' => 'Empty server response');
}
else {
$error_code = 0;
}
}
else if($result == 'ERROR') {
$error_code = getTextBetweenTags($response, 'code', true);
}
else if($result == 'OK') {
$error_code = 0;
}
if(!$error_code) {
return array('error_code' => 0, 'response' => $response);
}
else if(in_array($error_code, $token_error_codes)) {
// The service response contains a token error. Try to generate a new token.
saveToken(0);
}
if($counter >= $count_attempts) {
return array('error_code' => $error_code, 'response' => $response);
}
}
}
C#
public dynamic callSVPService(dynamic service, dynamic arg, dynamic raw_data)
{
var countAttempt = 3;
int[] tokenErrorCodes;
tokenErrorCodes = new int[2004];
tokenErrorCodes = new int[2005];
tokenErrorCodes = new int[2006];
var counter = 0;
while (true)
{
counter++;
var tokenResponse = getToken();
var token = tokenResponse["token"];
var response = callSVPAPIService(service, arg);
var result = getTextBetweenTags(response, "result", true);
return result;
}
}
Currently I am working on a web page which will tell user about certain configurations on client machine. Out of this there is also requirement of detecting if Adobe Reader is installed on client machine or not. I am using ASP.NET/C#.
I have looked the following url for the answer
"Check Adobe Reader is installed (C#)?" but the code look into the server registry entires where IIS is installed and not the client machine where browser is running.
Is it possible to detect if Adobe reader is installed on client machine and not the server which is hosting the website?
pls, check the script below, it worked fine for me in IE, FireFox and Chrome
<html>
<body>
<script type="text/javascript">
var found = false;
var info = '';
try
{
acrobat4 = new ActiveXObject('PDF.PdfCtrl.1');
if (acrobat4)
{
found = true;
info = 'v. 4.0';
}
}
catch (e)
{
//???
}
if (!found)
{
try
{
acrobat7 = new ActiveXObject('AcroPDF.PDF.1');
if (acrobat7)
{
found = true;
info = 'v. 7+';
}
}
catch (e)
{
//???
}
if (!found && navigator.plugins && navigator.plugins.length>0)
{
for (var i = 0; i<navigator.plugins.length; i++)
{
if (navigator.plugins[i].name.indexOf('Adobe Acrobat') > -1)
{
found = true;
info = navigator.plugins[i].description + ' (' + navigator.plugins[i].filename + ')';
break;
}
}
}
}
document.write("Acrobat Reader Installed : " + found);
document.write("<br />");
if (found) document.write("Info : " + info);
</script>
</body>
</html>
hope this helps, regards
I used this script and called it on ready function :
Note: i used the alerts here just to know how to use it.
<script type="text/javascript">
$(document).ready(function () {
alert(getAcrobatInfo().browser);
alert(getAcrobatInfo().acrobat === "installed");
alert(getAcrobatInfo().acrobatVersion);
});
var getAcrobatInfo = function () {
var getBrowserName = function () {
return '<%=Session["browser"].ToString()%>';
};
var getActiveXObject = function (name) {
try { return new ActiveXObject(name); } catch (e) { }
};
var getNavigatorPlugin = function (name) {
for (key in navigator.plugins) {
var plugin = navigator.plugins[key];
if (plugin.name == name) return plugin;
}
};
var getPDFPlugin = function () {
return this.plugin = this.plugin || function () {
if (getBrowserName() == 'ie' || getBrowserName().toLocaleLowerCase() == 'internetexplorer') {
//
// load the activeX control
// AcroPDF.PDF is used by version 7 and later
// PDF.PdfCtrl is used by version 6 and earlier
return getActiveXObject('AcroPDF.PDF') || getActiveXObject('PDF.PdfCtrl');
}
else {
return getNavigatorPlugin('Adobe Acrobat') || getNavigatorPlugin('Chrome PDF Viewer') || getNavigatorPlugin('WebKit built-in PDF') || getWebKitPlugin();
}
}();
};
var getWebKitPlugin = function () {
for (var key in navigator.plugins) {
var plugin = navigator.plugins[key];
if (plugin.name && plugin.name.substring(0, 6) == "WebKit" && (plugin.name.indexOf("pdf") != -1 || plugin.name.indexOf("PDF") != -1)) return plugin;
}
};
var isAcrobatInstalled = function () {
return !!getPDFPlugin();
};
var getAcrobatVersion = function () {
try {
var plugin = getPDFPlugin();
if (getBrowserName() == 'ie' || getBrowserName().toLocaleLowerCase() == 'internetexplorer') {
var versions = plugin.GetVersions().split(',');
var latest = versions[0].split('=');
return parseFloat(latest[1]);
}
if (plugin.version) return parseInt(plugin.version);
return plugin.name
}
catch (e) {
return null;
}
}
// The returned object
return {
browser: getBrowserName(),
acrobat: isAcrobatInstalled() ? 'installed' : false,
acrobatVersion: getAcrobatVersion()
};
};
</script>
Also add this code behind:
public void detectBrowser()
{ //Set the Browser session variable
System.Web.HttpBrowserCapabilities browser = Request.Browser;
Session["Browser"] = browser.Browser;
}
Hope it helps.