XML technology for "traversing" firewalls

Source: Internet
Author: User
Tags empty execution final getmessage hash connect socket tostring
Programmers can often come across a servlet application that connects to a company's database and provides customers with a specific service that is protected by a powerful authentication mechanism that thousands of customers worldwide use. The question now arises: How will you provide user access to the database from the application when the application is outside the company's firewall? You know, a network administrator is not going to open a special port specifically for your application to connect to the database.

HTTP tunneling Technology and XML
How to connect to a client/server application over a firewall this problem has been bothering programmers for a long time. In most cases, a company's firewall always opens the port as little as possible. In general, the only port you can use is 80, which is the port used by the Web.

The way to solve this problem is to use the HTTP Tunneling Technique (HTTP tunneling). This method first wraps the request in an HTTP POST request, and then the request is processed by a CGI application (such as a servlet) on a Web server inside the firewall.

The servlet restores the original request, executes it, and then inserts the result into the HTTP response stream. The firewall interprets this interaction as a general request for a Web page and allows it to continue processing. This is like a Trojan horse: it looks like a normal request, but it hides the unexpected load.

The next question is how do you format the request? Of course, using XML is the best choice to send these loads to an HTTP request. The idea is ingenious, and the XML above HTTP is a hot emerging field, and some new rules are being written to become standard communication protocols for distributed applications in the future. Among them, simple Object Access Protocol (SOAP) is the most recognized.

Unfortunately, there is no stable soap for us to use, and the best we can find now is from the Apache group, but it only supports simple return types. Therefore, it is of no use to our project. But that's also good, which means we can propose our own XML protocol on HTTP and learn the concepts it contains.
Concept
Let's now build a simple framework structure that takes XML on HTTP as a basic communication strategy, allowing us to create a set of services that can be accessed from desktop applications that are distributed all over the Internet.

First, we need to establish the syntax for common requests and responses. The request looks like this:

<?xml version= ' 1.0 ' encoding= ' utf-8 '?>
<requestType>
[Type of request]
</requestType>
<request>
[Application specific request. This is an XML elment]
</request>

The response looks like this:

<?xml version= ' 1.0 ' encoding= ' utf-8 '?>
<response>
<responseMessage>
[The response message]
</responseCode>
<responseCode>
[A application specific return code.]
</responseCode>
<response>
[Application specific request. This'll is an XML Element]
</response>

To understand the concept behind this framework structure, we want to write an application service routine: A simple database service that handles any SQL statement and returns the result as a vector. The details of the request, which are the XML tags contained in the request element, are simple as follows:

<sql-statement>
[The SQL statement to is executed]
</sql-statement >
The response result is this:
<result-set>
</result-count>
[The number of rows in the ' result set]
</result-count>
<row>
<col name= ' name ' >
[The value]
</col>
?
</row>
?
<result-set>

Httpservice is a servlet that responds to a POST request, restores the XML payload, and uses it to create a servicerequest example. Then, depending on the type of request, the request is given to a specific subclass of the Httpservicehandler abstract class. The handler class executes the request, stores the result in an example of Serviceresponse, and sends the result back to the client application.

By following the convention of naming service processor classes, we can use the image function of Java to create an example of the processor class, which is based solely on the service type attributes of the Servicerequest object. This cancels the dependency between Httpservice and all service processor classes, which means that when we add a new service, we no longer need to change the Httpservice class.

In our case, the type of service is dbservice, so we'll create a subclass of Httpservicehandler, called Dbservicehandler. In Httpservice, we use the following code:

String className = package_name + "." + request.getrequesttype () + "Handler";
Httpservicehandler handler = Class.fromname (className). newinstance ();
Handler.handlerequest (Request);

A httpservicehandler subclass needs to execute a method ProcessRequest (), it takes a Servicerequest object, and then returns a Serviceresponse object. This method is invoked by subclasses during the HandleRequest method:

public void HandleRequest (Servicerequest request)
{
Serviceresponse response = ProcessRequest (request);
Sendresponse (response);
}

This is a typical example of using the template (template) method pattern, in which an abstract superclass invokes a method executed in a subclass.

The Servicerequest class stores service-specific data as an XML document. This class is used by the visitor to set up and get the type of request, and it has methods to handle the details of the request. The Getrequest () method returns the XML node contained in the request token, and the Setrequest () method overwrites the original request with a newly generated request. This class also uses two factory methods to create new elements and text nodes that allow developers to generate new requests. The Serviceresponse class handles the details of the request in a very simple way.

Although these two classes allow us to process all types of requests and responses, developers must also understand the special syntax for each request. The developer cannot make any acknowledgement of the requested format correctly.

To simplify this process, we'll create subclasses of Servicerequest and Serviceresponse, which are called dbservicerequest and Dbserviceresponse, each with a way to handle service-specific details. For example, Dbservicerequest has a way to set up and get SQL statements, while dbserviceresponse a way to set and get results and to set vectors for values and results.

Services are accessed using the Httpserviceclient class. In the client application, you have the following code:

Httpserviceclient client = new Httpserviceclient (serviceurl);
Dbservicerequest request = new Dbservicerequest ();
Request.setsqlstatement (statement);
Dbserviceresponse response = new Dbserviceresponse (Request) (client.executerequest);

The URL of the service is this:

Http://myHost/servlet/httpservice.HttpService.

Details
We've seen all the elements in the frame structure, and now look at the interesting details. First, let's take a look at the protocol layer. How should we create an HTTP POST request that wraps an XML payload? What should we do with HTTP responses?

The HTTP request is a standardized, ASCII-based, socket communication with a Web server. Here's an example:

post/servlet/
Httpservice.httpservice http/1.0
Host:localhostt:80
Content-type:text/xml
content-length:248
<?xml version= ' 1.0 ' encoding= ' utf-8 '?>
<requestType>DBService</requestType>
<request>
<sql-statement>
SELECT * from MyTable
</sql-statement >
</request>

The response from the Web server is as follows:

http/1.0 OK
Date:fri, Nov 16:09:57 GMT
status:200
Servlet-engine:tomcat Web server/3.1 (JSP 1.1;
Servlet 2.2; Java 1.3.0; Windows 5.0 x86;
Java.vendor=sun Microsystems Inc.)
Content-type:text/xml
content-length:726
Content-language:en
<?xml version= ' 1.0 ' encoding= ' utf-8 '?>
<responseMessage>OK</responseCode>
<responseCode>200</responseCode>
<response>
<result-set>
</result-count>2 </result-count>
<row>
<col name= ' col1 ' >value11</col>
<col name= ' col2 ' >value12</col>
</row>
<row>
<col name= ' col1 ' >value21</col>
<col name= ' col2 ' >value22/</col>
</row>
<result-set>
</response>


The following code shows the execution of the Httpserviceclient class, which handles all the details of the HTTP request. As you can see, once you understand the exact format of those requests, this is a very simple process:

public class Httpserviceclient
{
private static final String http_version = "1.0";
private static final String http_post_request = "POST";
private static final String Header_host = "HOST";
private static final String Header_content_type = "Content-type";
private static final String Xml_mime_type = "Text/xml";
private static Final String
Header_content_length = "Content-length";
private static final int default_port = 80;

Private String serviceurl;
private int ReturnCode;
Private String ReturnMessage;
Private Reader responsepayload;

Public httpserviceclient (String serviceurl)
{
This.serviceurl = serviceurl;
}

Public Serviceresponse executerequest (servicerequest request)
Throws Httpserviceexception
{

Try
{
String data = request.serializerequesttostring ("Utf-8");
Postrequest (data);

Check for failures
if (ReturnCode!= 200)
throw new Httpserviceexception (ReturnMessage);

InputSource Source =
New InputSource (responsepayload);
Domparser parser = new Domparser ();
Parser.parse (source);
Serviceresponse Serviceresponse =
New Serviceresponse (Parser.getdocument ());

String Theresponse =
Serviceresponse.serializeresponsetostring ("Utf-8");
System.err.println (Theresponse);

return serviceresponse;

}
catch (Exception ex)
{
Ex.printstacktrace (System.err);
throw new Httpserviceexception (Ex.getmessage ());
}
}


private void Postrequest (String payload)
Throws Httpserviceexception
{
PrintWriter out = null;
BufferedReader in = null;


URL url = null;
Try
{
url = new URL (serviceurl);

No Port? Use default port 80
int port = url.getport () < 0? DEFAULT_PORT:url.getPort ();

Socket soket = new socket (Url.gethost (), port);
out = new PrintWriter (Soket.getoutputstream ());

in = new BufferedReader (New InputStreamReader (Soket.getinputstream ()));
}
catch (Exception ex)
{
throw new Httpserviceexception ("Error opening socket:" + ex.getmessage ());
}

Out.print (Http_post_request + "" + url.getfile () + "http/" + http_version + "\ r \ n");
Out.print (Header_host + ":" + url.gethost () + ': ' + url.getport () + "\ r \ n");
Out.print (Header_content_type + ":" + Xml_mime_type + "\ r \ n");
Out.print (Header_content_length + ":" + payload.length () + "\ r \ n");
Out.print ("\ r \ n");
Out.print (payload);
Out.print ("\r\n\r\n");
Out.flush ();

Try
{
String statusline = In.readline ();
System.err.println (Statusline);
Parsestatusline (Statusline);
}
catch (Exception ex)
{
throw new Httpserviceexception ("Error parsing HTTP status line:" + ex.getmessage ());
}

I'll ignore all the headers and keep reading
Until I get a empty line
Try
{
String headerline = null;
while ((Headerline = In.readline ())!= null)
{
if (headerline.length () = = 0)
Break
}
}
catch (Exception ex)
{
throw new Httpserviceexception ("Error reading HTTP headers:" + ex.getmessage ());
}

What remains of the input Stream is my payload
Responsepayload = in;

}

private void Parsestatusline (String statusline)
Throws Exception
{
StringTokenizer st =
New StringTokenizer (Statusline);

This is the HTTP Version
St.nexttoken ();

ReturnCode = Integer.parseint (St.nexttoken ());

StringBuffer retmessage = new StringBuffer ();

while (St.hasmoretokens ())
{
Retmessage.append (St.nexttoken ());
if (St.hasmoretokens ())
{
Retmessage.append ("");
}
}

ReturnMessage = Retmessage.tostring ();

}

}


When the Web server accepts an HTTP request, it creates a new example of the Httpservice servlet, and then calls the Dopost () method, which is passed in the HttpServletRequest and HttpServletResponse objects. The servlet then restores the XML payload and creates an example of the Servicerequest class, and finally forwards it to the correct processor:

Document Dom;
Domparser parser = new Domparser ();
InputSource input = new InputSource (Request.getinputstream ());
Parser.parse (input);
Dom = Parser.getdocument ();
Servicerequest servicerequest = new Servicerequest (DOM);
String className = package_name + "." + request.getrequesttype () + "Handler";
Httpservicehandler handler = Class.fromname (className). newinstance ();
Handler.handlerequest (Request);


The following code shows the execution of the Dbservicehandler class, which creates a database connection, executes a query, and generates a Dbserviceresponse object. Also, note that this process is very simple, because many complex problems are hidden behind the Serviceresponse and Servicerequest classes and subclasses:

public class Dbservicehandler extends
Httpservicehandler

{
Public Serviceresponse ProcessRequest (servicerequest req)
Throws Httpserviceexception
{
Dbservicerequest request = new Dbservicerequest (req);

String sql = Request.getsqlstatement ();
Dbserviceresponse response = new Dbserviceresponse ();
Connection Connection;

Try
{
Class.forName ("Oracle.jdbc.driver.OracleDriver");
String connectionString = "Jdbc:oracle:thin: @fender. OPENPORT.COM:1521:IPLP";
Connection = Drivermanager.getconnection (connectionString, "OP1", "OP1");

}
catch (ClassNotFoundException ex)
{
Ex.printstacktrace (System.err);
Response.setresponsecode (400);
Response.setresponsemessage ("Oracle Driver not Found");
return response;
}
catch (SQLException ex2)
{
Ex2.printstacktrace (System.err);
Response.setresponsecode (400);
Response.setresponsemessage ("Could not Connect to database!");
return response;
}

String thesql = Sql.trim (). toUpperCase ();
ResultSet ResultSet;

Try
{
Statement Statement =
Connection.createstatement ();

if (Thesql.startswith ("select"))
{
ResultSet = Statement.executequery (Thesql);

Vector theresults = Parseresultset (ResultSet);
Response.setresultscount (Theresults.size ()-1);
Response.setresultset (Theresults);
}
Else
{
Statement.executeupdate (Thesql);
Response.setresultscount (-1);
Response.setresultset (New Vector ());
}

}catch (SQLException ex)
{
Response.setresponsecode (400);
Response.setresponsemessage (Ex.getmessage ());
return response;
}

Response.setresponsecode (200);
Response.setresponsemessage ("OK");

String res = response.serializeresponsetostring ("Utf-8");
System.out.println (RES);

return response;
}
?
}


In the following code, you can see the execution of Servicerequest and dbservicerequest. Note that as Dbservice-request also provides an additional constructor, it will servicerequest as an argument. This allows us to use the original requested XML document as the current document, thus providing an additional application-specific interface to the existing data:

public class Servicerequest

{
Public final static String request_type_tag_name = "RequestType";
Public final static String request_tag_name = "REQUEST";
Public final static String root_tag_name = "Http-request";

Protected Document dom;

Public servicerequest (Document request)
{
dom = Request;
}

Public Servicerequest ()
{
Dom = new Documentimpl ();
InitializeRequest ();
}

Initializes an empty request
private void initializerequest ()
{
Element root = dom.createelement (root_tag_name);
Dom.appendchild (root);

Element Erequesttype =
Dom.createelement (Request_type_tag_name);
Erequesttype.appendchild (Dom.createtextnode (""));

Root.appendchild (Erequesttype);

Element erequest =
Dom.createelement (Request_tag_name);
Root.appendchild (erequest);
}

Public String Getrequesttype ()
Throws Httpserviceexception
{
Try
{
Return Gettextattribute (Request_type_tag_name);
}
catch (Exception ex)
{
Ex.printstacktrace (System.err);
throw new Httpserviceexception ("Invalid Request Format.");
}

}

public void Setrequesttype (String requesttype)
Throws Httpserviceexception
{
Try
{
Settextattribute (Request_type_tag_name,requesttype);
}
catch (Exception ex)
{
Ex.printstacktrace (System.err);
throw new Httpserviceexception ("Invalid Request Format.");
}

}

Public Node getrequest ()
Throws Httpserviceexception
{
Try
{
Node request =
((nodelist) dom.getelementsbytagname (request_tag_name)). Item (0);

Return Request.getfirstchild (). CloneNode (True);

}
catch (Exception ex)
{
Ex.printstacktrace (System.err);
throw new Httpserviceexception ("Invalid Request Format.");
}
}

Public Element Createelementnode (String elementname)
{
Return dom.createelement (elementname);
}

Public Text createTextNode (String value)
{
return Dom.createtextnode (value);
}

public void Setrequest (Node request)
Throws Httpserviceexception
{

Try
{
Node requestelement =
((nodelist) dom.getelementsbytagname (request_tag_name)). Item (0);
Node oldrequest =
Requestelement.getfirstchild ();

if (oldrequest!= null)
Requestelement.removechild (oldrequest);

Requestelement.appendchild (Request);
}
catch (Exception ex)
{
Ex.printstacktrace (System.err);
throw new Httpserviceexception ("Invalid Request Format.");
}
}

Public byte[] Serializerequesttobytearray (String encoding)
Throws Httpserviceexception
{
Try
{
return Serializedom (Encoding). Tobytearray ();
}
catch (Exception ex)
{
Ex.printstacktrace (System.err);
throw new Httpserviceexception ("Error during serialization");
}
}

public string serializerequesttostring (string encoding)
Throws Httpserviceexception
{
Try
{
return Serializedom (Encoding). ToString ();
}
catch (Exception ex)
{
Ex.printstacktrace (System.err);
throw New Httpserviceexception (
"Error during serialization");
}
}

Private Bytearrayoutputstream Serializedom (String encoding)
Throws Httpserviceexception
{
Try
{
Bytearrayoutputstream bytes =
New Bytearrayoutputstream (4096);
PrintWriter out = new PrintWriter (new OutputStreamWriter (bytes,encoding), true);
OutputFormat of =
New OutputFormat (dom,encoding,true);
XMLSerializer serializer = new XMLSerializer (out,of);
Serializer.serialize (DOM);
Out.close ();

return bytes;
}
catch (Exception ex)
{
Ex.printstacktrace (System.err);
throw new Httpserviceexception ("Error during serialization");
}
}

Protected string Gettextattribute (string name)
{
Node Textattributenode = ((nodelist) dom.getelementsbytagname (name)). Item (0);
Node Textattribute = Textattributenode.getfirstchild ();
if (textattribute.getnodetype () = = Node.text_node)
return Textattribute.getnodevalue ();
Else
return null;
}

protected void Settextattribute (string name, String value)
{
if (value = = null)
Value = "";
Node Textattributenode = ((nodelist) dom.getelementsbytagname (name)). Item (0);
Node Textattribute =
Textattributenode.getfirstchild ();
Textattribute.setnodevalue (value);

}
}

public class Dbservicerequest extends Servicerequest
{

Public final static String service_name = "Dbservice";

Public final static String sql_statement_tag_name = "Sql-statement";

Public Dbservicerequest ()
{
Super ();
Initializeparameters ();
}

Public dbservicerequest (Document request)
{
Super (Request);
}

Public dbservicerequest (Servicerequest request)
{
dom = Request.dom;
}

public void setsqlstatement (String sql)
{
Settextattribute (Sql_statement_tag_name,sql);
}

Public String getsqlstatement ()
{
Return Gettextattribute (Sql_statement_tag_name);
}
private void Initializeparameters ()
{
Element edbrequest = null;
Not very nice but this should never fail

Try
{
Setrequesttype (service_name);
Edbrequest = Createelementnode (sql_statement_tag_name);
}
catch (Exception ex)
{}

Edbrequest.appendchild (Dom.createtextnode (""));

Try
{
Setrequest (edbrequest);
}
catch (Exception ex)
{
}
}


Extended frame Structure
We can extend this framework to handle any type of service. To create a new service, you must first define the syntax for the XML request and response. Then, you create a subclass of Servicerequest and Serviceresponse that will help with service-specific data. Finally, create a new subclass of the Http-servicehandler that will process the request and generate the appropriate response. This is the whole process.

Although this framework contains some features, it is not practical in practical applications without adding more functionality. I deliberately omitted these features to keep the frame structure simple and focused on the most important details. For the sake of completeness, let's briefly analyze some of the limitations of the framework and how to overcome these limitations.

First, this framework structure does not restrict access to services. This means that everyone who knows how to access the service is able to access it. Before you allow access to a service, ask for some kind of proof to solve the problem. You can use this same framework to create a certification service that confirms the user and generates a unique ID that will be required when the user accesses any other service. The system stores this ID in certain types of access lists, and requires that for a limited period of time, each request must pass a valid ID to access the service.

In addition, each time a user accesses a service, the service creates a connection to the database. Combining the service with a connection pooling framework solves this problem by assigning an existing connection to the request instead of creating a new connection each time.

The last limitation is the lack of session management. Because we are accessing the servlet directly through a socket, we cannot use particularly useful httpsession objects. Because the session cookie is not generated on the client computer during the interaction between the Web server and the browser, automatic sessions cannot be managed. To overcome this limitation, we can perform our own session management. A session can be an object that is associated with a unique ID, and it can store other objects. For example, you can have a context hash signal table in which to store the session object, using a unique ID as the keyword. The session object can also contain a hash signal table that stores each object that you want to stick with in the session.

This framework leverages HTTP tunneling technology to allow a desktop application to access services behind the firewall, extending it to provide easy access to other types of services.



Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.