Browser extension sample – Chrome/Edge – HttpRequestViewer

Browser Extensions

The Evolution of Browser Extensions: From Web Customization to Advanced Development Tools – Part 2
We discussed about The evolution of the Browser extensions in the previous post. Lets quick learn how to create a Chrome/Edge/Firefox extension. I have mentioned “Advanced development tools” in the title, but never got chance to explore those capabilities earlier. We will create a simple extension to explore the power of it.

Creating a browser extension has never been easier, thanks to the comprehensive documentation and support provided by browser vendors. Below, we’ll walk through the steps to create a simple extension for both Chrome and Microsoft Edge using Manifest V3. We will use this tool to print the list of HTTP requests that are fired in a given browser and list it in the page.

Basics of extensions:

Manifests – A manifest is a JSON file that contains metadata about a browser extension, such as its name, version, permissions, and the files it uses. It serves as the blueprint for the extension, informing the browser about the extension’s capabilities and how it should be loaded.

Key Components of a Manifest File:

Here are the key components typically found in a Manifest V3 file:

1. Manifest Version: There are different versions of the manifest file, with Manifest V3 being the latest and most widely adopted version. Manifest V3 introduces several changes aimed at improving security, privacy, and performance with lot of controversies around it. Read more about the controversies at Ghostery.
2. Name and Version: These fields define the name and version of the extension. Choose a unique name and version. An excellent guide of version semantics is available here.
3. Description: A short description of the extension’s functionality.
4. Action: Defines the default popup and icon for the browser action (e.g., toolbar button).
5. Background: Specifies the background script that runs in the background and can handle events like network requests and alarms.
6. Content Scripts: Defines scripts and stylesheets to be injected into matching web pages.
7. Permissions: Lists the permissions the extension needs to operate, such as access to tabs, storage, and specific websites.
8. Icons: Specifies the icons for the extension in different sizes. For this post I created a simple icon using Microsoft Designer. I gave a simple prompt with the description above and I got the below image. Extension requires different sizes for showing it in different places. I used Chrome Extension Icon Generator and generated different sizes as needed.

     

9. Web Accessible Resources: Defines which resources can be accessed by web pages.

Create a project structure as follows:

HttpRequestViewer/
|-- manifest.json
|-- popup.html
|-- popup.js
|-- background.js
|-- history.html
|-- history.js
|-- popup.css
|-- styles.css
|-- icons/
    |-- icon.png
    |-- icon16.png
    |-- icon32.png
    |-- icon48.png
    |-- icon128.png

Manifest.json

{
  "name": "API Request Recorder",
  "description": "Extension to record all the HTTP request from a webpage.",
  "version": "0.0.1",
  "manifest_version": 3,
  "host_permissions": [""],
  "permissions": ["activeTab", "webRequest", "storage"],
  "action": {
    "default_popup": "popup.html",
    "default_icon": "icons/icon.png"
  },
  "background": {
    "service_worker": "background.js"
  },
  "icons": {
    "16": "icons/icon16.png",
    "32": "icons/icon32.png",
    "48": "icons/icon48.png",
    "128": "icons/icon128.png"
  },
  "content_security_policy": {
    "extension_pages": "script-src 'self'; object-src 'self';"
  },
  "web_accessible_resources": [{ "resources": ["images/*.png"], "matches": ["https://*/*"] }]
}

popup.html
We have two options with the extension.

1. A button with record option to start recording all the HTTP requests
2. Link to view the history of HTTP Requests recorded

<!DOCTYPE html>
<html>
  <head>
    <title>API Request Recorder</title>

    <link rel="stylesheet" href="popup.css" />
  </head>
  <body>
    <div class="heading">
      <img class="logo" src="icons/icon48.png" />
      <h1>API Request Recorder</h1>
    </div>
    <button id="startStopRecord">Record</button>

    <div class="button-group">
      <a href="#" id="history">View Requests</a>
    </div>

    <script src="popup.js"></script>
  </body>
</html>

popup.js
Two event listeners are registered for recording (with start / stop) and viewing history.
First event is used to send a message to the background.js, while the second one instructs chrome to open the history page in new tab.

document.getElementById("startStopRecord").addEventListener("click", () => {
  chrome.runtime.sendMessage({ action: "startStopRecord" });
});

document.getElementById("history").addEventListener("click", () => {
  chrome.tabs.create({ url: chrome.runtime.getURL("/history.html") });
});

history.html

 
<!DOCTYPE html>
<html>
  <head>
    <title>History</title>
    <link rel="stylesheet" href="styles.css" />
  </head>
  <body>
    <h1>History Page</h1>
    <table>
      <thead>
        <tr>
		  <th>Method</th>
          <th>URL</th>
          <th>Body</th>
        </tr>
      </thead>
      <tbody id="recorded-data-body">
        <!-- Data will be populated here -->
      </tbody>
    </table>
    <script src="history.js"></script>
  </body>
</html>

history.js
Requests background.js to “getRecordedData” and renders the result in the html format.

document.addEventListener("DOMContentLoaded", () => {
  chrome.runtime.sendMessage({ action: "getRecordedData" }, (response) => {
    const tableBody = document.getElementById("recorded-data-body");
    response.forEach((record) => {
      const row = document.createElement("tr");
      const urlCell = document.createElement("td");
      const methodCell = document.createElement("td");
      const bodyCell = document.createElement("td");

      urlCell.textContent = record.url;
      methodCell.textContent = record.method;
      bodyCell.textContent = record.body;

      row.appendChild(methodCell);
      row.appendChild(urlCell);
      row.appendChild(bodyCell);
      tableBody.appendChild(row);
    });
  });
});

background.js
Background JS works as a service worker for this extension, listening and handling events.
The background script does not have access to directly manipulate the user page content, but can post results back for the popup/history script to handle the cosmetic changes.

let isRecording = false;
let recordedDataList = [];

chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  console.log("Obtined message: ", message);
  if (message.action === "startStopRecord") {
    if (isRecording) {
      isRecording = false;
      console.log("Recording stopped...");
      sendResponse({ recorder: { status: "stopped" } });
    } else {
      isRecording = true;
      console.log("Recording started...");
      sendResponse({ recorder: { status: "started" } });
    }
  } else if (message.action === "getRecordedData") {
    sendResponse(recordedDataList);
  } else {
    console.log("Unhandled action ...");
  }
});

chrome.webRequest.onBeforeRequest.addListener(
  (details) => {
    if (isRecording) {
      let requestBody = "";
      if (details.requestBody) {
        if (details.requestBody.formData) {
          requestBody = JSON.stringify(details.requestBody.formData);
        } else if (details.requestBody.raw) {
          requestBody = new TextDecoder().decode(new Uint8Array(details.requestBody.raw[0].bytes));
        }
      }
      recordedDataList.push({
        url: details.url,
        method: details.method,
        body: requestBody,
      });
      console.log("Recorded Request:", {
        url: details.url,
        method: details.method,
        body: requestBody,
      });
    }
  },
  { urls: [""] },
  ["requestBody"]
);

Lets load the Extension

All set, now lets load the extension and test it.

  • Open Chrome/Edge and go to chrome://extensions/ or edge://extensions/ based on your browser.
  • Enable “Developer mode” using the toggle in the top right corner.
  • Click “Load unpacked” and select the directory of your extension.

Load extensionupload extension

  • Your extension should now be loaded, and you can interact with it using the popup.
  • When you click the “Record” button, it will start logging API requests to the console.

  • Click the “Record” button again and hit the “View requests” link in the popup to view the history of APIs.

I have a sample page (https://itechgenie.com/demos/apitesting/index.html) with 4 API calls, which also loads images based on the API responses. You could see all the API requests that is fired from the page including the JS, CSS, Images and API calls.


Now its up to the developers imagination to build the extension to handle these APIs request and response data and give different experience.

Code is available in GitHub at HttpRequestViewer

Enable Registration in WordPress

In WordPress custom installations, the option for Registration of new users is disabled by default. To enable the registrations and allow logins for public you can follow the below steps.

Single site installation:

As soon as the installation is completed go to the WordPress Dashboard -> Settings

You will find an option Allow new registrations, you may need to select the Default Role for the newly registered members.

wp-user-registeration-single-site

Multi-site/Network installation:

In case if you have installed WordPress in Multi site mode go to the Network Admin -> Settings and select the options under Allow new registrations section.

wp-user-registeration-multi-site

Once the registration is enabled it may be needed to add login options at places to find it easily. Follow these methods to do the same.

Method 1:

Add Meta widget in the sidebars or in footers. Select Appearance -> Widgets and select the Meta Widget.  Note that the menu will be available in Individual Site Dashboard, not in the Network Admin dashboard in case of Multi-site installation.

wp-user-login-widget

 

Method 2:

Add the login link in your Posts/Pages and it should be in the pattern, http(s)://HOSTNAME(:PORT)/(SUBDOMAINS)/wp-login.php.

Examples:

  1. http://itechgenie.com/myblog/wp-login.php – MultiSite installation
  2. http://itechgenie.com/wp-login.php – SingleSite installation

Maven and Cloud Foundry Integration

Cloud Fountry provides as easy integration plugins to move the build packages to its servers through Maven. Here is the sample configuration.

Add Servers to settings.xml

<settings> 
	...
	<servers>
		... 
		<server>
			<id>cloud-foundry-credentials</id>
			<username>cf_user_id_you_created</username>
			<password>cf_password_you_created</password>
		</server>
	</servers> 
</settings>

You can encrypt you password in MAVEN settings. Check out here on how to do it.

Add Dependency and Plugin settings in pom.xml

</project>
	...	
	<build>
		...
		<plugins>
				<groupId>org.cloudfoundry</groupId>
				<artifactId>cf-maven-plugin</artifactId>
				<version>1.1.3</version>
				<configuration>
					<server>cloud-foundry-credentials</server>
					<target>https://url.to.cloud.foundry.com</target>
					<memory>512</memory>
					<appname>application-name</appname>
					<org>ORG_NAME</org>
					<space>SPACE_NAME</space>
					<instances>1</instances>
				</configuration>
				<executions>
					<execution>
						<phase>package</phase>
						<goals>
							<goal>push</goal>
						</goals>
					</execution>
				</executions>
		</plugins>
	</build>
</project>

Thats it !!. Build you project using Maven and check if you application gets deployed into your Cloud Foundry server.

Installing Oracle JDK in Amazon AWS EC2 Ubuntu

Lately I tried to install Oracle JDK in one of my Ubuntu servers on Amazon EC2 instance. Unfortunately the inbuilt installers support the installation of OpenJDK.

For some requirements, I was in need of installing a specific version of JDK and test my application, you could get the older version from Oracle Site. I used the following script from one of the blogs, hope it helps someone.

#!/usr/bin/env bash
wget -O 'jdk-7u80-linux-x64.tar.gz' --no-cookies --no-check-certificate --header 'Cookie:gpw_e24=http://www.oracle.com; oraclelicense=accept-securebackup-cookie' 'http://download.oracle.com/otn-pub/java/jdk/7u80-b15/jdk-7u80-linux-x64.tar.gz'
tar -xvf jdk-7u80-linux-x64.tar.gz
sudo mkdir /usr/lib/jvm
sudo mv ./jdk1.7* /usr/lib/jvm/jdk1.7.0
sudo update-alternatives --install "/usr/bin/java" "java" "/usr/lib/jvm/jdk1.7.0/bin/java" 1
sudo update-alternatives --install "/usr/bin/javac" "javac" "/usr/lib/jvm/jdk1.7.0/bin/javac" 1
sudo update-alternatives --install "/usr/bin/javaws" "javaws" "/usr/lib/jvm/jdk1.7.0/bin/javaws" 1
sudo chmod a+x /usr/bin/java
sudo chmod a+x /usr/bin/javac
sudo chmod a+x /usr/bin/javaws

The Key here is Oracle need you to accept the license terms before using the any version of Oracle JDK. You could do the same from the scripting by just adding --no-cookies --no-check-certificate --header 'Cookie:gpw_e24=http://www.oracle.com; oraclelicense=accept-securebackup-cookie' params to the WGET.

Alternative, you could download the installers/zip files from external CDNs, like REUCON, move it to EC2 instance through SFTP and install it.

Adding Oracle Datasource to JBoss EAP server

To add a Oracle Datasource to the JBOSS server, follow the steps

1. In the standalone.xml or in standalone-full.xml

<subsystem xmlns="urn:jboss:domain:datasources:1.2">
		<datasources>
			<datasource jndi-name="java:jboss/datasources/ExampleDS" pool-name="ExampleDS" enabled="true" use-java-context="true">
				<connection-url>jdbc:h2:tcp://localhost/~/jbpm-db-new;MVCC=TRUE;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE</connection-url>
				<driver>h2</driver>
				<security>
					<user-name>sa</user-name>
					<password>sa</password>
				</security>
			</datasource>
			<datasource jndi-name="java:jboss/datasources/JbpmDS" pool-name="JbpmDS" enabled="true" use-java-context="true">
				<connection-url>jdbc:oracle:thin:@localhost:1521:XE</connection-url>
				<driver>oracle</driver>
				<security>
					<user-name>username</user-name>
					<password>password</password>
				</security>
			</datasource>
			<drivers>
				<driver name="h2" module="com.h2database.h2">
					<xa-datasource-class>org.h2.jdbcx.JdbcDataSource</xa-datasource-class>
				</driver>
				<driver name="oracle" module="com.oracle.jdbc">
					<driver-class>oracle.jdbc.driver.OracleDriver</driver-class>
					<xa-datasource-class>oracle.jdbc.xa.client.OracleXADataSource</xa-datasource-class>
				</driver>
			</drivers>
		</datasources>
</subsystem>

2. In $JBOSS_HOME/modules/com/oracle/jdbc/main I have copied the ojdbc6.jar and created the module.xml file.

<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:jboss:module:1.0" name="com.oracle.jdbc">
  <resources>
    <resource-root path="ojdbc6.jar"/>
  </resources>
  <dependencies>
    <module name="javax.api"/>
    <module name="javax.transaction.api"/>
  </dependencies>
</module>

3. Now you could use the JNDI “java:jboss/datasources/JbpmDS” in your application

Android device showing offline with USB debugging

When I tried to run Android application in my mobile from Android Studio, I stuck with the error stating the application my Device was Offline.

When I run the command adb devices the error as follows

List of devices attached
00b445g34****** offline

To Enable the USB debugging option in Android device

1. Go to Settings, and select Applications > Development
2. Enable USB debugging
3. For Devices with Android KitKat and go to Settings > About devices > Tap 7 times on “Build Number”
4. Once Developer Option is enabled in the Settings, Enable USB debugging
5. Update the Android SDK Tools in Android SDK Manager
6. Traverse to \android-sdk\platform-tools and run adb devices
7. If still the problem exists restart the ADB server

adb kill-server
set ADB_TRACE=all
adb nodaemon server
adb devices

8. The ADB_TRACE will help you trace and resolving the issues like permission and others.
9. If still the problem exists kill the adb process killall adb in linuxoids and taskkill /IM adb.exe
10. Disconnect your mobile, Developer option > Revoke USB debugging authorizations (KitKat and above)
11. Restart your mobile and connect to PC. You will be asked to verify the RSA, do the same and add the device
12. Now try running adb devices, boom the device is online

List of devices attached
00b445g34****** device

The same scenario happened when I tried to run debug my AIR application in Flash developer, follow all the steps above in addition the next few steps too.

1. Copy the following files from \android-sdk\platform-tools aapt.exe, adb.exe, AdbWinApi.dll, AdbWinUsbApi.dll, dx.jar (dx.jar from lib folder)
2. Paste them into \lib\android\bin
3. Run the command adb version from the same folder in command prompt
4. Result should be as follows
Android Debug Bridge version 1.0.31
5. Version should be greater or equal to 1.0.31

Errors running builder ‘JavaScript Validator’ on project

I got this annoying exception on every auto build of my Dynamic Web Project.

“Errors occurred during the build.
Errors running builder ‘JavaScript Validator’ on project ‘GenieAlert’.
java.lang.NullPointerException”

I was trying to stop the validation of Javascript in the Eclipse properties with the following options,

Windows -> Preferences -> Validation -> Client-side JavaScript Validator -> Checked Manual & Unchecked Build.

This option didn’t work out, then I realized that it happens only on the Build time. The following option came in handy to do that.

Project -> Properties -> Builders -> Unchecked ‘Javascript Validator’

Sometimes when you try to run the Web projects from eclipse on servers these JavaScript validation stops the deployment of the project on servers stating “JavaScript Validation Exception found”. I hope the above solutions will help in those situations too.

Bhuvan, The Earth browser – A Geoportal of Indian Space Research Organisation

Most of us would have used the mapping services like Google Maps, Nokia Maps, Bing maps, Wiki Mapia etc on the net. But how many of us knew that we in India have a dedicated Mapping system. Yes, all those PSLV family satellites, Remote sensing satellites from ISRO send us a lot of images; details etc daily and these details are available to the public’s view.

Bhuvan is a Geo-portal of Indian Space Research Organization Showcasing Indian Imaging Capabilities in Multi-sensor, Multi-platform and Multi-temporal domain. This Earth browser gives a gateway to explore and discover virtual earth in 3D space with specific emphasis on Indian Region. The other services provided by the Bhuvan are Land services, Ground water prospects, Weather services, Ocean services, Disaster services

The Mapping system provides both the 2D and 3D viewing capability and can see information that is otherwise dry and academic, in ways that are visually fascinating. It helps you capture large databases of satellite data, which can be transformed into 3D presentations that capture the imaginations of the rest of us. Users can experience the comprehensive globe with multi resolution imagery, thematic information, historical multi temporal imagery, and other points of interest. As a User we can explore and visualize the world in a 3D landscape along with all other wide ranging tools to explore Bhutan.

Bhuvan also provides a mobile version of its site and can be accessed from here.

As a feast for developers, Bhuvan also provides the API’s to embed a true 3D digital globe, into the web pages. Using the API you can draw markers and lines, drape images over the terrain, allowing you to build sophisticated 3D map applications.

Bhuvan Quick Tour:

If you are unable view the video, click Here to download.

Create Web Services using Axis Java2WSDL, WSDL2Java and Eclipse for all Servers manually – Part 2

With all the basic configurations done as specified in the last Article we continue to develop the Business logic.

  1. Create a class named Calculator.java, place four public methods add, subtract, multiply and delete and place the appropriate logics in it.
    package com.itechgenie.services.impl;
    public class Calculator {
    	public int add(int a, int b) {
    		return a+b  ;
    	}
    
    	public int subtract(int a, int b) {
    		return a-b ;
    	}
    
    	public int multiply(int a, int b) {
    		return a * b ;
    	}
    
    	public int divide(int a, int b) throws ArithmeticException {
    		return a /b ;
    	}
    }
  2. This is the class that has to be exposed as the Web Service and we write the funky TestRunner.java class to do all out operations like creating WSDL file, creating stub file etc.
  3. Generate WSDL file using Java2WSDL: Axis has a tool called Java2WSDL, which generates a WSDL file for a web service using a Java class. Java2WSDL file takes the following arguments.
    1. o – name for WSDL file -> calculator.wsdl
    2. n – target namespace -> mx:com.itechgenie.services.Calculator
    3. l – url of web service -> http://<host:port>/<Project-Name>/services/calculator

    Summing up the above arguments the following command line arguments is created.

    String java2wsdlArgs[] = {"-ocalculator.wsdl", "-nmx:com.itechgenie.services.Calculator", "-v", "-lhttp://localhost:8080/axis/services/calculator", "com.itechgenie.services.Calculator"} ;

    Read this Article on how to run the command line java tools from Eclipse.
    You can run the Java2WSDL as follows in the TestRunner class. Naah, don’t ask how, just put the following lines the main method and press CTRL + F11.

    try {
    	Java2WSDL.main(java2wsdlArgs) ;
    } catch (Exception e) {
    	e.printStackTrace() ;
    }

    The Java2WSDL class has the System.exit(0); method called from inside. So lines after the Java2WSDL will not be executed. To get the other arguments supported you can just run Java2WSDL.main(new String[0]) ;. This will display all the arguments supported by Java2WSDL Utility and this works for other utilities also.
    After running this Utility you will find the calculator.wsdl file created in the root folder of the Project.

  4. Generate Server side and Client side codes using WSDL2Java: WSDL2Java is another tools provided by the AXIS, which can generate server side and client side Java classes using a WSDL file. These classes are needed for deploying the web service and also for accessing the web service using a Java client. This tool expects the following argument which includes the WSDL file generated in the last step.
    1. o – output folder -> src
    2. p – package for generated classes -> mx:com.itechgenie.services. generated
    3. s – generate server side classes as well
    4. *.wsdl – WSDL file of any web service

    Summing up the above arguments the following command line arguments is created.

    String wsdl2javaArgs[] = {"-osrc", "-pcom.itechgenie.generated.service", "-s", "calculator.wsdl", "-v"} ;

    Read this Article on how to run the command line java tools from Eclipse.
    Now run the WSDL2Java utility as follows.

    try {
    	WSDL2Java.main(wsdl2javaArgs) ;
    } catch (Exception e) {
    	e.printStackTrace() ;
    }

    Once the above command is run, Just refresh the project in eclipse, you will find the following files created inside the “com.itechgenie.generated.service” package.

    1. Calculator.java
    2. CalculatorService.java
    3. CalculatorServiceLocator.java
    4. CalculatorSoapBindingImpl.java
    5. CalculatorSoapBindingStub.java
    6. deploy.wsdd
    7. undeploy.wsdd

    The above files can be used in both Server and Clients side as Skeleton (CalculatorSoapBindingImpl.java) and the Stub (CalculatorSoapBindingStub.java) respectively.

  5. Binding the business logic with the Skeleton: Take the Skeleton file and you will find the exact methods that were available in our Business logic class (Calculator.java.).
    Create a instance of the Business class and invoke the appropriate method from the skeleton as follows (Find the lines highlighted in yellow.).

    package com.itechgenie.generated.service;
    
    import com.itechgenie.services.Calculator;
    
    public class CalculatorSoapBindingImpl implements com.itechgenie.generated.service.Calculator{
    
    	Calculator calculatorImpl = new Calculator() ;
    
        public int add(int in0, int in1) throws java.rmi.RemoteException {
        	return calculatorImpl.add(in0, in1) ;
        }
    
        public int subtract(int in0, int in1) throws java.rmi.RemoteException {
        	return calculatorImpl.subtract(in0, in1) ;
        }
    
        public int divide(int in0, int in1) throws java.rmi.RemoteException {
        	return calculatorImpl.divide(in0, in1) ;
        }
    
        public int multiply(int in0, int in1) throws java.rmi.RemoteException {
        	return calculatorImpl.multiply(in0, in1) ;
        }
    
    }

    That’s it; we are now done with the development part of the Web Service. All we have to do is to configure to make the service up and running.

  6. Last configurations to make our service available: Open the server-config.wsdd file inside the WEB-INF folder. You will find the following lines.
      <!--  Your Service from the deploy.wsdd file - Starts here -->
    
      <!--  Your Service from the deploy.wsdd file - Ends here -->

    Keep the file aside and open the deploy.wsdd from the WSDL2Java generated files. Copy the <service> … </service> tag completely and paste in between the comments said above.

  7. Conclusion: You can follow the steps 6 to 11 and create as many services as you want and paste them in the server-config.wsdd.
    With this the configurations for the Web Service is over. Export the Project as a War and deploy it in Web Server and point to the URL http://<host:port>/<Project-Name>/services
  8. This URL should display all the services generated from steps 6 to 11 with the links the WSDL files for the above.

    Click here to download the sample project.