How to Deploy JavaScript QR Code Generator to Google App Engine

Recently, I found an excellent jQuery plugin jquery.qrcode.js, which is written by Jerome Etienne. In this article, I would like to share how to use the QR code plugin to implement a free online app with Google App Engine. You can try my demo http://dynamsoft-test.appspot.com/.

qr code

How to Implement QR Code Generator

<script src="js/jquery.min.js"></script>
<script src="js/jquery.qrcode.min.js"></script>
<script src="js/utf-8.js"></script>
  • Create a text input, a button, and an area for QR code display.
<input type="text" id="text" placeholder="www.dynamsoft.com">
<button onclick="generate()">Try it</button>
<div id="output"></div>
  • Add JS code for button event.
function generate()
{
	jQuery(function(){
		var canvas = document.querySelector("canvas");
		if (canvas != null && canvas.parentNode) {
			canvas.parentNode.removeChild(canvas);
		}
		var text = document.getElementById("text");
		jQuery('#output').qrcode(Utf8.encode(text.value));
		text.value = "";
	})
}
  • You can run it now.

How to Deploy the App to Google App Engine

  • There are four programming languages supported. I would like to pick PHP.

application

  • Change the qrcode.html to qrcode.php.
  • Create a configuration file named app.yaml with the following contents:
application: dynamsoft-test
version: 1
runtime: php
api_version: 1

handlers:
- url: /stylesheets
  static_dir: stylesheets

- url: /js
  static_dir: js

- url: /.*
  script: qrcode.php
  • Modify the source code for loading css and scripts:
<link type="text/css" rel="stylesheet" href="/stylesheets/main.css" />
<script src="/js/jquery.min.js"></script>
<script src="/js/jquery.qrcode.min.js"></script>
<script src="/js/utf-8.js"></script>
  • In the command line tool, type in python google_appengine/dev_appserver.py qrcode/
  • Visit http://localhost:8080/ to check your app.
  • If there is no error, you can upload the project to Google App Engine by the command appcfg.py update qrcode/

Now, you can visit the app domain to have fun. If you have any questions, please feel free to contact {desmond at Dynamsoft dot com}.

 

     

How to Implement a Real-time Commnication Application with WebRTC

Recently, I read the article – Getting Started with WebRTC, and also learned the sample code from WebRTC codelab. It is really interesting that we can implement video chat, audio chat and even message communications with WebRTC (Web browsers with real-time communications). In this post, I would like to share what I have done after digesting what WebRTC is.

What You Should Know?

  1. The sample code is based on the samples from codelab.
  2. You need to install Node.js.
  3. You need to use the npm, the package manager for Node.js, to install socket.io and node-static.

How to Run?

  1. Run command line tool (cmd.exe on Windows, terminal on Mac OS X) and get into the project root directory.
  2. Type in “node server.js”.
  3. Connect your devices (PC, smartphone, and tablet) to local network, and make sure you have installed the latest version of Chrome.
  4. Visit your local IP_address:2013 via arbitrary two devices.
  5. Enjoy it.

What is inside?

  • Video chat
  • Audio chat
  • Message communications
  • Capture camera image
  • Save image to local disk

Code

Video chat, audio chat and message communications:

var constraints = {video: true, audio : true};
getUserMedia(constraints, handleUserMedia, handleUserMediaError); // enable video & audio
function createPeerConnection() {
  try {
    pc = new RTCPeerConnection(null, {'optional': [{'DtlsSrtpKeyAgreement': true}, {'RtpDataChannels': true}]});
    pc.onicecandidate = handleIceCandidate;
    pc.onaddstream = handleRemoteStreamAdded; // receive remote video stream
    pc.onremovestream = handleRemoteStreamRemoved;
    console.log('Created RTCPeerConnnection');
  } catch (e) {
    console.log('Failed to create PeerConnection, exception: ' + e.message);
    alert('Cannot create RTCPeerConnection object.');
      return;
  }

  try {
      // Reliable Data Channels not yet supported in Chrome
      sendChannel = pc.createDataChannel("sendDataChannel",
        {reliable: false});
      sendChannel.onmessage = handleMessage;
      trace('Created send data channel');
    } catch (e) {
      alert('Failed to create data channel. ' +
            'You need Chrome M25 or later with RtpDataChannel enabled');
      trace('createDataChannel() failed with exception: ' + e.message);
    }
    sendChannel.onopen = handleSendChannelStateChange;
    sendChannel.onclose = handleSendChannelStateChange;
    pc.ondatachannel = gotReceiveChannel; // receive message
}

Capture camera image:

function captureImage() {
	if (remoteVideo.src == "") {
		alert('No remote connection');
		return;
	}

	var canvas = document.getElementById('canvas');
	var ctx = canvas.getContext('2d');

	canvas.width = remoteVideo.videoWidth / 4;
	canvas.height = remoteVideo.videoHeight / 4;
	ctx.drawImage(remoteVideo, 0, 0, canvas.width, canvas.height);
}

Save image to local disk:

function saveImage() {
	if (remoteVideo.src == "") {
		alert('No remote connection');
		return;
	}

	var ua = window.navigator.userAgent;

	if (ua.indexOf("Chrome") > 0) {
		var canvas = document.getElementById("canvas");

		// save image as png
		var link = document.createElement('a');
		link.download = "test.png";
		link.href = canvas.toDataURL("image/png").replace("image/png", "image/octet-stream");
		;
		link.click();
	} else {
		alert("Please use Chrome");
	}
}

You can download the source code WebRTC. If you have any questions, please email {desmond at Dynamsoft dot com}.

     

How to Configure and Install Nginx on Mac OS X

In this tutorial, I would like to show how to configure and install nginx on Mac OS X. My Mac system is Mavericks 10.9.2.

mac environment

Prerequisites

You have to install Xcode command line tools.

Install Nginx

1. Download the latest stable version – nginx 1.4.7.

2. Unzip the downloaded package by the command “tar xvzf nginx-1.4.7.tar.gz”.

3. “cd nginx-1.4.7″.

4. “sudo ./configure”. There is an error displayed:

./configure: error: the HTTP rewrite module requires the PCRE library.

You can either disable the module by using --without-http_rewrite_module

option, or install the PCRE library into the system, or build the PCRE library

statically from the source with nginx by using --with-pcre=<path> option.

5. To fix the error, visit the tutorial page and read “Building nginx from Sources”, in which you can find the link of PCRE library.

6. Go to PCRE official site, and find the latest version of PCRE library on SourceForge.

7. Download the package and unzip it by the command “tar xvzf pcre-8.34.tar.bz2″.

8. Now, you can run the configure file again with the parameters  ”sudo ./configure –with-pcre=path”.

9. Configuration is done.

  nginx path prefix: "/usr/local/nginx"

  nginx binary file: "/usr/local/nginx/sbin/nginx"

  nginx configuration prefix: "/usr/local/nginx/conf"

  nginx configuration file: "/usr/local/nginx/conf/nginx.conf"

  nginx pid file: "/usr/local/nginx/logs/nginx.pid"

  nginx error log file: "/usr/local/nginx/logs/error.log"

  nginx http access log file: "/usr/local/nginx/logs/access.log"

  nginx http client request body temporary files: "client_body_temp"

  nginx http proxy temporary files: "proxy_temp"

  nginx http fastcgi temporary files: "fastcgi_temp"

  nginx http uwsgi temporary files: "uwsgi_temp"

  nginx http scgi temporary files: "scgi_temp" 

the default nginx path prefix is “/usr/local/nginx”.

10. To install nginx, type in “sudo make install”.

11. Find the executable file “cd /usr/local/nginx/sbin”

12. Launch nginx “sudo ./nginx”

The nginx is successfully running now!

nginx running

If you have any questions, just email {desmond at dynamsoft dot com}.

 

     

Take a Photo and Upload it on Mobile Phones with HTML5

In my daily life, I enjoy taking photos with my smartphone and uploading them to various websites. So I started thinking, “Is it possible to implement these functions in web browser?” Although Dynamsoft ImageCapture Suite allows us to capture images from webcam, it is designed for a desktop App development on windows and Mac, not for mobile platforms. Fortunately, the new HTML5 SDK is capable of uploading local images or captured images to web servers. Everything becomes easier if we have a mobile web browser, like Chrome, which is fully compatible to HTML5.

Take Photos in Browsers of Android and iOS

Using HTML5 to invoke the camera is very simple.

For more information, you can reference HTML Media Capture. Due to the platform-dependency, you need to read Mobile HTML5 and search HTML Media Capture for relevant information. Referring to the article Html5 File Upload with Progress, I have made some improvements. The source code has been tested on Android 4.1 and iOS 7.0.6. You can see the following figures.

Android:

android

iOS: 

image capture image upload

Source Code

Client:

<!DOCTYPE html>

<html>

<head>

    <title>Take or select photo(s) and upload</title>

    <script type="text/javascript">

      function fileSelected() {

        var count = document.getElementById('fileToUpload').files.length;

              document.getElementById('details').innerHTML = "";

              for (var index = 0; index < count; index ++)

              {

                     var file = document.getElementById('fileToUpload').files[index];

                     var fileSize = 0;

                     if (file.size > 1024 * 1024)

                            fileSize = (Math.round(file.size * 100 / (1024 * 1024)) / 100).toString() + 'MB';

                     else

                            fileSize = (Math.round(file.size * 100 / 1024) / 100).toString() + 'KB';

                     document.getElementById('details').innerHTML += 'Name: ' + file.name + '<br>Size: ' + fileSize + '<br>Type: ' + file.type;

                     document.getElementById('details').innerHTML += '<p>';

              }

      }

      function uploadFile() {

        var fd = new FormData();

              var count = document.getElementById('fileToUpload').files.length;

              for (var index = 0; index < count; index ++)

              {

                     var file = document.getElementById('fileToUpload').files[index];

                     fd.append(file.name, file);

              }

        var xhr = new XMLHttpRequest();

        xhr.upload.addEventListener("progress", uploadProgress, false);

        xhr.addEventListener("load", uploadComplete, false);

        xhr.addEventListener("error", uploadFailed, false);

        xhr.addEventListener("abort", uploadCanceled, false);

        xhr.open("POST", "savetofile.aspx");

        xhr.send(fd);

      }

      function uploadProgress(evt) {

        if (evt.lengthComputable) {

          var percentComplete = Math.round(evt.loaded * 100 / evt.total);

          document.getElementById('progress').innerHTML = percentComplete.toString() + '%';

        }

        else {

          document.getElementById('progress').innerHTML = 'unable to compute';

        }

      }

      function uploadComplete(evt) {

        /* This event is raised when the server send back a response */

        alert(evt.target.responseText);

      }

      function uploadFailed(evt) {

        alert("There was an error attempting to upload the file.");

      }

      function uploadCanceled(evt) {

        alert("The upload has been canceled by the user or the browser dropped the connection.");

      }

    </script>

</head>

<body>

  <form id="form1" enctype="multipart/form-data" method="post" action="Upload.aspx">

    <div>

      <label for="fileToUpload">Take or select photo(s)</label><br />

      <input type="file" name="fileToUpload" id="fileToUpload" onchange="fileSelected();" accept="image/*" capture="camera" />

    </div>

    <div id="details"></div>

    <div>

      <input type="button" onclick="uploadFile()" value="Upload" />

    </div>

    <div id="progress"></div>

  </form>

</body>

</html>

Server savetofile.aspx:

<%@ Page Language="C#" %>

<%

       HttpFileCollection files = HttpContext.Current.Request.Files;

       for (int index = 0; index < files.Count; index ++)

       {

              HttpPostedFile uploadfile = files[index];

                             // You must create “upload” sub folder under the wwwroot.

              uploadfile.SaveAs(Server.MapPath(".") + "\\upload\\" + uploadfile.FileName);

       }

             HttpContext.Current.Response.Write("Upload successfully!");

%>

 

     

How to Use JavaScript to Save Canvas Data in Chrome

In HTML5, there is a new tag <canvas>, which is used to draw graphics via JavaScript. In this tutorial, I would like to share how to draw images on canvas, and how to save the canvas data to local disk by clicking button.

Draw Images on Canvas

To develop web application, I prefer using Aptana Studio which contains a built-in web server.

Now, let’s create a basic web project which includes only a default index.html file.

aptana studio

Open index.html and add a <img> tag in <body>.

<img src="dynamsoft_logo_black.png" id="img" width="73" height="73" alt="dynamsoft"/>

Below the image, add a <canvas>.

<canvas width="73" height="73" id="canvas">canvas</canvas>

Create a button to trigger click event.

<button type="button" onclick="saveImage()">save image</button>

Draw the canvas when the page is finished loading.

<script type="text/javascript">
			window.onload = function() {
				var canvas = document.getElementById("canvas");
				var ctx = canvas.getContext("2d");
				var img = document.getElementById("img");
				ctx.drawImage(img, 0, 0);
			}
</script>

Run the project to see what we have done so far.

 GUI

Save Canvas Data to Local Disk

Finally, we can use following code to popup a dialog for saving the canvas data.

var canvas = document.getElementById("canvas");
document.location.href = canvas.toDataURL("image/png").replace("image/png", "image/octet-stream");

Both Chrome and Firefox support the method, whereas they behave differently.

Chrome

Chrome

Firefox 

Firefox

The problem is that we cannot programmatically set the file name and type.

Fortunately, there is an alternative in Chrome.

var link = document.createElement('a');
link.download = "test.png";
link.href = canvas.toDataURL("image/png").replace("image/png", "image/octet-stream");;
link.click();

Full code:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
		<title>Save Image</title>
	</head>
	<body>
		<h1>Save Image</h1>
		<img src="dynamsoft_logo_black.png" id="img" width="73" height="73" alt="dynamsoft"/>
		<canvas width="73" height="73" id="canvas">canvas</canvas>
		<script type="text/javascript">
			window.onload = function() {
				var canvas = document.getElementById("canvas");
				var ctx = canvas.getContext("2d");
				var img = document.getElementById("img");
				ctx.drawImage(img, 0, 0);
			}
		</script>

		<button type="button" onclick="saveImage()">save image</button>
		<script type="text/javascript">
			function saveImage() {
				var ua = window.navigator.userAgent;

				if (ua.indexOf("Chrome") > 0) {
					// save image without file type
					var canvas = document.getElementById("canvas");
					document.location.href = canvas.toDataURL("image/png").replace("image/png", "image/octet-stream");

					// save image as png
					var link = document.createElement('a');
    				link.download = "test.png";
    				link.href = canvas.toDataURL("image/png").replace("image/png", "image/octet-stream");;
    				link.click();
				}
				else {
					alert("Please use Chrome");
				}
			}
		</script>
	</body>
</html>

You can download the source code - SaveImage. If you have any questions, please feel free to contact me at {Desmond at Dynamsoft dot com}.

     

How to Check PageRank and AlexaRank in Android

If you have implemented a Java program on desktop, why not bring it to mobile platform? In this tutorial, I will show how to transplant the Java program for PageRank and AlexaRank to Android platform. Let’s glance at the screenshot in advance.

Android Rank Checker

Android Project

Create a new Android project in Eclipse. To access Internet, you have to open AndroidManifest.xml and add

<uses-permission android:name="android.permission.INTERNET"/>

Create an UI layout, which includes TextView, EditText and Button.

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <LinearLayout
        android:id="@+id/linearlayout"
        android:layout_width="match_parent"
        android:layout_height="60dp" >

        <TextView
            android:id="@+id/text"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_gravity="center"
            android:gravity="center"
            android:text="@string/url" />

        <EditText
            android:id="@+id/url"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_gravity="center"
            android:layout_weight="1"
            android:ellipsize="end"
            android:hint="@string/hint" 
            android:inputType="textUri"/>

        <Button
            android:id="@+id/btSearch"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_gravity="center"
            android:text="@string/check" />
    </LinearLayout>

    <TextView
        android:id="@+id/results"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/linearlayout"
        android:scrollbars="vertical"
        android:text="" />

</RelativeLayout>

In main Activity, we need to create an event listener for button and set check results in text view. In case of blocking UI, we should create a thread for network connection.

public void onClick(View v) {
		// TODO Auto-generated method stub
		String url = mURL.getText().toString();
		String tmpURL = url.trim();
		if (url != null && !url.equals("")) {
			if (!url.startsWith("http")) {
				tmpURL = "http://" + url;
			}
			final String finalURL = tmpURL;
			final String logURL = url;
			mProgressDialog = ProgressDialog.show(this,
					getString(R.string.process_dialog_title),
					getString(R.string.process_dialog_content));
			new Thread(new Runnable() {

				@Override
				public void run() {
					// TODO Auto-generated method stub
					final int pageRank = PageRank.get(finalURL);
					final int alexRank = AlexaRank.getAlexaRank(finalURL);
					runOnUiThread(new Runnable() {

						@Override
						public void run() {
							// TODO Auto-generated method stub
							mResults.append(logURL + ": PageRank = " + pageRank
									+ "; Alexa rank = " + alexRank + mNewline);
							mURL.setText("");
							mProgressDialog.dismiss();
						}

					});

				}

			}).start();
		}
	}

Run the application on your smartphone to have fun. Download the source code AndroidRankChecker.

Now, no matter where you are, you can instantly check PageRank and AlexaRank. Please feel free to contact me at {desmond at Dynamsoft dot com} if you have any questions.

     

Libwebsockets Introduction

Libwebsockets is a lightweight pure C library; built to use minimal CPU and memory resources as well as providing fast throughput in both directions.

  • Provides server and client APIs for v13 websocket protocol, along with http[s].
  • Can be configured to use OpenSSL or CyaSSL to provide fully encrypted client and server links including client certificate support.
  • It’s a fully autotools, and optionally CMake, based project that has been used in a variety of OS contexts; including Linux (uclibc and glibc), ARM-based embedded boards, MIPS / OpenWRT, Windows, Android, Apple iOS and even Tivo.
  • It includes a stub webserver that is enough to deliver your scripts to the browser that open websocket connections back to the same server, so it can solve the entire server side, ws://, wss://, ​http:// and ​https:// in one step. Apache, Java or any other server-side support is not needed.
  • Chrome 26 and Firefox 18 are supported, including the webkit websocket compression extension.
  • Architectural features like zero-copy for payload data and FSM-based protocol parsers make it ideal for realtime operation on resource-constrained devices.
  • Websocket + HTTP serving for ARM: code + data + bss combined is under 15K, plus 12K at init to support up to 1024 fds, and 112 bytes per connection… minimal single client case in < 35KBytes total including library footprint
  • Valgrind-clean, reliable and robust.
  • It’s licensed under LGPL2 + static link exception and comes with test servers that demonstrate client and server communication between the test apps and a test browser applet.

libwebsockets provides a simple and understandable interface to help us implement our own features or even a fully functional lightweight websocket server. So let’s start:

1. First, to use the libwebsockets, include its header file:

#include "../lib/libwebsockets.h"

Its dependents for the project will construct on your own machine. A tip is, this header file should be included before <windows.h> to avoid the redefine error.

2. Lbwebsockets allows you to define any protocols at your pleasure. So just define your protocol structure as follows:

//////////////////////////////////////////////////////////////////////////
/* http-only */
struct per_session_data__http {
	HANDLE hFile;
};

static int callback_http(struct libwebsocket_context *context, struct libwebsocket *wsi, enum libwebsocket_callback_reasons reason, void *user, void *in, size_t len);

//////////////////////////////////////////////////////////////////////////
/* dumb-increment-protocol */
struct per_session_data__dumb_increment {
	int number;
};

static int callback_dumb_increment(struct libwebsocket_context *context, struct libwebsocket *wsi, enum libwebsocket_callback_reasons reason, void *user, void *in, size_t len);

//////////////////////////////////////////////////////////////////////////
/* lws-command-protocol */
struct per_session_data__lws_command {
	bool isSyn;
};

static int callback_lws_command(struct libwebsocket_context *context, struct libwebsocket *wsi, enum libwebsocket_callback_reasons reason, void *user, void *in, size_t len);
//////////////////////////////////////////////////////////////////////////
char pro_http[] = "http-only";
char pro_dumb_increment[] = "dumb-increment-protocol";
char pro_lws_command[] = "lws-command-protocol";
//////////////////////////////////////////////////////////////////////////
struct libwebsocket_protocols protocols[] = {
	{
		pro_http,			/* protocol name, char */
		callback_http,		/* callback function */
		sizeof (struct per_session_data__http),	/* per_session_data_size */
		0,	/* max buffer */
	},
	{
		pro_dumb_increment,
		callback_dumb_increment,
		sizeof(struct per_session_data__dumb_increment),
		0,
	},
	{
		pro_lws_command,
		callback_lws_command,
		sizeof(struct per_session_data__lws_command),
		0,
	},
	{ NULL, NULL, 0, 0 } /* terminator */
};

libwebsocket_protocols is a protocol structure defined in libwebsockets. Field instructions:

  •  [pro_http] is the name of the protocol. You can define any protocol names you like, but the first protocol must always be the HTTP handler.
  •  [callback_http] is the callback function for you to implement your own business logic. We will introduce it below.
  •  [sizeof (struct per_session_data__http)] is the user data structure size you want to transfer.
  •  [0] means the maximum frame size.

So a group of protocols like this can be defined here and processed in libwebsockets. The only job left is to define your own callback function. A typical http callback function may be like this:

static int callback_http(struct libwebsocket_context *context, struct libwebsocket *wsi, enum libwebsocket_callback_reasons reason, void *user, void *in, size_t len)
{
	int n, m;
	unsigned char *p;
	static unsigned char buffer[4096];

	TCHAR cache_file[MAX_PATH];
	//struct stat stat_buf;
	unsigned int fileSize = 0;
	struct per_session_data__http *pss =
			(struct per_session_data__http *)user;

	DWORD readLen = 0;

	char client_name[128];
	char client_ip[128];

	switch (reason) {
	case LWS_CALLBACK_HTTP:

		lwsl_notice("http.\n");
		/* check for the "send a big file by hand" example case */
		sprintf(cache_file, "%s\\cache.png", g_cache_path);
		p = buffer;

		pss->hFile = CreateFile(cache_file, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);

		if (pss->hFile == NULL || pss->hFile == INVALID_HANDLE_VALUE)
			return -1;
		/*
		 * we will send a big jpeg file, but it could be
		 * anything.  Set the Content-Type: appropriately
		 * so the browser knows what to do with it.
		 */

		p += sprintf((char *)p,
			"HTTP/1.0 200 OK\x0d\x0a"
			"Server: libwebsockets\x0d\x0a"
			"Content-Type: image/png\x0d\x0a"
				"Content-Length: %u\x0d\x0a\x0d\x0a",
				GetFileSize(pss->hFile, 0));

		/*
		 * send the http headers...
		 * this won't block since it's the first payload sent
		 * on the connection since it was established
		 * (too small for partial)
		 */
		n = libwebsocket_write(wsi, buffer,
			   p - buffer, LWS_WRITE_HTTP);

		if (n < 0) {
			CloseHandle(pss->hFile);
			return -1;
		}
		/*
		 * book us a LWS_CALLBACK_HTTP_WRITEABLE callback
		 */
		libwebsocket_callback_on_writable(context, wsi);
		break;

	case LWS_CALLBACK_HTTP_FILE_COMPLETION:
//		lwsl_info("LWS_CALLBACK_HTTP_FILE_COMPLETION seen\n");
		/* kill the connection after we sent one file */
		return -1;

	case LWS_CALLBACK_HTTP_WRITEABLE:
		/*
		 * we can send more of whatever it is we were sending
		 */
		do {
			if (ReadFile(pss->hFile, buffer, sizeof buffer, &readLen, NULL) == INVALID_HANDLE_VALUE)
			{
				CloseHandle(pss->hFile);
				return -1;
}
			/* problem reading, close conn */
			if (readLen <= 0)
			{
				CloseHandle(pss->hFile);
				return -1;
			}
			/*
			 * because it's HTTP and not websocket, don't need to take
			 * care about pre and postamble
			 */
			m = libwebsocket_write(wsi, buffer, readLen, LWS_WRITE_HTTP);
			if (m < 0)
			{
				CloseHandle(pss->hFile);
				return -1;
			}
			if (m != readLen)
				/* partial write, adjust */
				SetFilePointer(pss->hFile, m - readLen, 0, FILE_CURRENT);

		} while (!lws_send_pipe_choked(wsi));
		libwebsocket_callback_on_writable(context, wsi);
		break;

	/*
	 * callback for confirming to continue with client IP appear in
	 * protocol 0 callback since no websocket protocol has been agreed
	 * yet.  You can just ignore this if you won't filter on client IP
	 * since the default uhandled callback return is 0 meaning let the
	 * connection continue.
	 */

	case LWS_CALLBACK_FILTER_NETWORK_CONNECTION:
		libwebsockets_get_peer_addresses(context, wsi, (int)(long)in, client_name, sizeof(client_name) ,client_ip, sizeof(client_ip));

		fprintf(stderr, "Received network connect from %s (%s)\n",
							client_name, client_ip);
		/* if we returned non-zero from here, we kill the connection */
		break;

	default:
		break;
	}
	return 0;
}

Actually, libwebsockets will call this function after receiving any http request. By judging the call ‘reason’ in switch(reason), we can implement our business logic in the right ‘case’.

3. After defining all of these necessary structure, we can create a libwebsockets context to call all these functions:

int main(int argc, char **argv)
{
	char cert_path[1024];
	char key_path[1024];
	int n = 0;
	int use_ssl = 0;
	struct libwebsocket_context *context;
	int opts = 0;
	char interface_name[128] = "";
	const char *iface = NULL;
#ifndef WIN32
	int syslog_options = LOG_PID | LOG_PERROR;
#endif
	unsigned int oldus = 0;
	struct lws_context_creation_info info;

	int debug_level = 7;

	memset(&info, 0, sizeof info);
	info.port = 7681;	// port is important

	signal(SIGINT, sighandler);

	/* tell the library what debug level to emit and to send it to syslog */
	lws_set_log_level(debug_level, lwsl_emit_syslog);

	lwsl_notice("libwebsockets test server - "
			"(C) Copyright 2010-2013 Andy Green <andy@warmcat.com> - "
						    "licensed under LGPL2.1\n");

	info.iface = iface;
	info.protocols = protocols;	// protocols is the libwebsocket_protocols array we defined before
	info.extensions = libwebsocket_get_internal_extensions();

	if (!use_ssl) {
		info.ssl_cert_filepath = NULL;
		info.ssl_private_key_filepath = NULL;
	} else {
		if (strlen(resource_path) > sizeof(cert_path) - 32) {
			lwsl_err("resource path too long\n");
			return -1;
		}
		sprintf(cert_path, "%s/libwebsockets-test-server.pem",
								resource_path);
		if (strlen(resource_path) > sizeof(key_path) - 32) {
			lwsl_err("resource path too long\n");
			return -1;
		}
		sprintf(key_path, "%s/libwebsockets-test-server.key.pem",
								resource_path);

		info.ssl_cert_filepath = cert_path;
		info.ssl_private_key_filepath = key_path;
	}
	info.gid = -1;
	info.uid = -1;
	info.options = opts;

	context = libwebsocket_create_context(&info);
	if (context == NULL) {
		lwsl_err("libwebsocket init failed\n");
		return -1;
	}

	n = 0;
	while (n >= 0 && !force_exit) {
		struct timeval tv;

		gettimeofday(&tv, NULL);

		if (((unsigned int)tv.tv_usec - oldus) > 50000) {
			libwebsocket_callback_on_writable_all_protocol(&protocols[PROTOCOL_DUMB_INCREMENT]);
			oldus = tv.tv_usec;
		}

		/*
		 * If libwebsockets sockets are all we care about,
		 * you can use this api which takes care of the poll()
		 * and looping through finding who needed service.
		 *
		 * If no socket needs service, it'll return anyway after
		 * the number of ms in the second argument.
		 */

		n = libwebsocket_service(context, 50);
	}

	libwebsocket_context_destroy(context);

	lwsl_notice("libwebsockets-test-server exited cleanly\n");

	return 0;
}

The important field is the port number and use_ssl. If the port number is already being used by another process, libwebsocket_create_context(&info); will failed. And, if you need to use ssl connection in your application, right cert path and key path is also necessary.

This is a simple introduction for libewebsockets use, Any suggestions or corrections will be appreciated.

     

How to Check Alexa Rank in Java

Since we have known how to check PageRank in Java, let’s strengthen our tool by adding the functionality of checking Alexa rank today.

To get the Alexa rank, you can use following code or read the excellent tutorial.

import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

public class AlexaRank {

	public static int getAlexaRank(String domain) {

		int result = 0;

		String url = "http://data.alexa.com/data?cli=10&url=" + domain;

		try {

			URLConnection conn = new URL(url).openConnection();
			InputStream is = conn.getInputStream();

			DocumentBuilder dBuilder = DocumentBuilderFactory.newInstance()
					.newDocumentBuilder();
			Document doc = dBuilder.parse(is);

			Element element = doc.getDocumentElement();

			NodeList nodeList = element.getElementsByTagName("POPULARITY");
			if (nodeList.getLength() > 0) {
				Element elementAttribute = (Element) nodeList.item(0);
				String ranking = elementAttribute.getAttribute("TEXT");
				if(!"".equals(ranking)){
					result = Integer.valueOf(ranking);
				}
			}

		} catch (Exception e) {
			System.out.println(e.getMessage());
		}

		return result;
	}
}

Now, let’s update our Java code to write Alexa rank to Excel files.

public void getPageRankAndAlexaRank() {
		// TODO Auto-generated method stub		
		try {
			InputStream excelFile = new FileInputStream(mFileName);
			XSSFWorkbook wb = new XSSFWorkbook(excelFile);
			XSSFSheet sheet = wb.getSheetAt(0);
			XSSFRow row;
			XSSFCell cellPR, cellAR;

			Iterator<Row> rows = sheet.rowIterator();

			int col = 0, colPR = 1, colAR = 2;
			int pageRank = 0, alexaRank = 0;
			String url = null;
			String log = null;

			while (rows.hasNext()) {
				row = (XSSFRow) rows.next();
				url = row.getCell(col).getStringCellValue();
				if (url.matches(Utils.REGEX)) { // check whether URL is valid
					System.out.println(url);
					pageRank = PageRank.get(url); // check page rank
					alexaRank = AlexaRank.getAlexaRank(url); // get alexa rank

					// write PageRank to excel
					cellPR = row.createCell(colPR);
					cellPR.setCellValue(pageRank);

					cellAR = row.createCell(colAR);
					cellAR.setCellValue(alexaRank);

					log = "PR = " + pageRank + ", AR = " + alexaRank;
					if (mEventListener != null) {
						mEventListener.log(log);
					}
					System.out.println(log);
				}
				else {
					System.out.println("URL not valid");
				}

				System.out.println("--------------------------");
			}

			FileOutputStream out = new FileOutputStream(mFileName);
	        wb.write(out);
	        out.flush();
	        out.close();
		}
		catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

Download the updated source package here. Please feel free to contact me at {desmond at dynamsoft dot com} if you have any question.

     

How to Use OpenSSL to Sign Certificate

In this tutorial, let’s continue to learn how to use OpenSSL to sign certificate.

Steps:

  1. We can use command line to quickly generate ca certificate.
    openssl genrsa -out cakey.pem 2048
    openssl req -new -days 365 -x509 -key cakey.pem -out cacert.pem -nodes -subj /C=CA/ST=BC/L=Vancouver/O=Dynamsoft/OU=Dynamsoft/CN=Dynamsoft/emailAddress=support@dynamsoft.com
    openssl rsa -in cakey.pem -pubout -out ca_pub.key
  2. Load ca certificate, ca private key and X.509 certificate request.
  3. Set version, serial number, issuer name, time, subject, public key to X.509 certificate, and sign it with private key.

Here is the code:

#include <stdio.h>
#include <iostream>

#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/bio.h>
#include <openssl/bn.h>
#include <openssl/evp.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
#include <openssl/objects.h>
#include <openssl/ocsp.h>

// load ca
bool loadCA(const char *f, X509 ** px509)
{
	bool ret;
	BIO	*in = NULL;

	in = BIO_new_file(f,"r");

	ret = (PEM_read_bio_X509(in, px509, NULL, NULL) != NULL);

	BIO_free(in);
	return ret;
}

// load ca private key
bool loadCAPrivateKey(const char *f, EVP_PKEY **ppkey)
{
	bool ret;
	BIO	*in = NULL;
	RSA *r = NULL;
	EVP_PKEY *pkey = NULL;

	in = BIO_new_file(f,"r");
	ret = (PEM_read_bio_RSAPrivateKey(in, &r, NULL, NULL) != NULL);
	if(!ret)
		goto free_;

	pkey = EVP_PKEY_new();
	EVP_PKEY_assign_RSA(pkey, r);
	*ppkey = pkey;
	r = NULL;

free_:
	BIO_free(in);
	return ret;
}

// load X509 Req
bool loadX509Req(const char *f, X509_REQ **ppReq)
{
	bool ret;
	BIO	*in = NULL;

	in = BIO_new_file(f,"r");
	ret = (PEM_read_bio_X509_REQ(in, ppReq, NULL, NULL) != NULL);

free_:
	BIO_free(in);
	return ret;
}

// sign cert
int do_X509_sign(X509 *cert, EVP_PKEY *pkey, const EVP_MD *md)
{
	int rv;
	EVP_MD_CTX mctx;
	EVP_PKEY_CTX *pkctx = NULL;

	EVP_MD_CTX_init(&mctx);
	rv = EVP_DigestSignInit(&mctx, &pkctx, md, NULL, pkey);

	if (rv > 0)
		rv = X509_sign_ctx(cert, &mctx);
	EVP_MD_CTX_cleanup(&mctx);
	return rv > 0 ? 1 : 0;
}

bool sign_X509_withCA()
{
	int				ret = 0;

	const char		*caFile = "cacert.pem";
	const char		*caPrivateKeyFile = "cakey.pem";
	const char		*x509ReqFile = "x509Req.pem";

	const char		*szUserCert = "cert.pem";

	int serial = 1;
	long days = 3650 * 24 * 3600; // 10 years
	char *md = NULL;

	X509 * ca = NULL;
	X509_REQ * req = NULL;
	EVP_PKEY *pkey = NULL, *pktmp = NULL;

	X509_NAME *subject = NULL, *tmpname = NULL;
	X509 * cert = NULL;
	BIO	*out = NULL;

	if(!loadCA(caFile, &ca))
		goto free_all;

	if(!loadCAPrivateKey(caPrivateKeyFile, &pkey))
		goto free_all;

	if(!loadX509Req(x509ReqFile, &req))
		goto free_all;

	cert = X509_new();
	// set version to X509 v3 certificate
	if (!X509_set_version(cert,2)) 
		goto free_all;

	// set serial
	ASN1_INTEGER_set(X509_get_serialNumber(cert), serial);

	// set issuer name frome ca
	if (!X509_set_issuer_name(cert, X509_get_subject_name(ca)))
		goto free_all;

	// set time
	X509_gmtime_adj(X509_get_notBefore(cert), 0);
	X509_gmtime_adj(X509_get_notAfter(cert), days);

	// set subject from req
	tmpname = X509_REQ_get_subject_name(req);
	subject = X509_NAME_dup(tmpname);
	if (!X509_set_subject_name(cert, subject)) 
		goto free_all;

	// set pubkey from req
	pktmp = X509_REQ_get_pubkey(req);
	ret = X509_set_pubkey(cert, pktmp);
	EVP_PKEY_free(pktmp);
	if (!ret) goto free_all;

	// sign cert
	if (!do_X509_sign(cert, pkey, EVP_sha1()))
		goto free_all;

	out = BIO_new_file(szUserCert,"w");
	ret = PEM_write_bio_X509(out, cert);

free_all:

	X509_free(cert);
	BIO_free_all(out);

	X509_REQ_free(req);
	X509_free(ca);
	EVP_PKEY_free(pkey);

	return (ret == 1);
}

int main(int argc, char* argv[]) 
{
	sign_X509_withCA();
	return 0;
}

You can feel free to download the sample code, and run it in Visual Studio.

     

How to Read and Write Excel Files in Java

Previously, I talked about how to check PageRank in Java. In this article, I will combine PageRank with Excel files. To operate Excel files in Java, I used Apache POI – the Java API for Microsoft Documents. Apparently, you can download the API package, and follow the relevant tutorials to learn how to use POI. When I first downloaded the Java library, I was astonished by how many jar files there were. I had no idea which ones would be useful for my project. Fortunately, I managed to filter out what I want. You can save your time to just load the libraries as follows:

  • poi-3.10-FINAL-20140208.jar
  • poi-ooxml-3.10-FINAL-20140208.jar
  • poi-ooxml-schemas-3.10-FINAL-20140208.jar
  • dom4j-1.6.1.jar
  • xmlbeans-2.3.0.jar

My project configuration is shown in figure 1.

eclipse configuration

Figure 1

In the following source code, I hard-coded the input data in first column and the output data in the second column. You can feel free to modify it.

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.Iterator;

import main.java.google.pagerank.PageRank;

import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import com.common.Utils;

public class ExcelOperator extends Operator {

	public ExcelOperator(String fileName) {
		super(fileName);
	}

	@Override
	public void getPageRank() {
		// TODO Auto-generated method stub		
		try {
			InputStream excelFile = new FileInputStream(mFileName);
			XSSFWorkbook wb = new XSSFWorkbook(excelFile);
			XSSFSheet sheet = wb.getSheetAt(0);
			XSSFRow row;
			XSSFCell cell;

			Iterator<Row> rows = sheet.rowIterator();

			int col = 0, colPR = 1;
			int pageRank = 0;
			String url = null;

			while (rows.hasNext()) {
				row = (XSSFRow) rows.next();
				url = row.getCell(col).getStringCellValue();
				if (url.matches(Utils.REGEX)) { // check whether URL is valid
					System.out.println(url);
					pageRank = PageRank.get(url); // check page rank

					// write PageRank to excel
					cell = row.createCell(colPR);
					cell.setCellValue(pageRank);

					System.out.println("PR = " + pageRank);
				}
				else {
					System.out.println("URL not valid");
				}

				System.out.println("--------------------------");
			}

			FileOutputStream out = new FileOutputStream(mFileName);
	        wb.write(out);
	        out.flush();
	        out.close();
		}
		catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

You can feel free to download the source code, which also includes GUI implementation. Do not hesitate to contact me at {desmond at dynamsoft dot com} if you have any question.