Sending SOAP messages in Java without any libraries

Needed to send some SOAP requests in Android. There was no nice library available to do it all. Not that I would care for them much, SOAP is bloated, Java libraries are typically bloated, and combining the two probably gets obese. Anyway, how can I send SOAP messages with only the most basic methods?

After a bunch of experiments, here is one just using plain sockets:

  public static void send(String hostname, String uri, String msg) throws Exception {
    int port = 80;

    String authString = "username:password";
    byte[] authEncBytes = Base64.getEncoder().encode(authString.getBytes());
    String authStringEnc = new String(authEncBytes);

    StringWriter sw = new StringWriter();

    Socket socket = new Socket(hostname, port);
    PrintWriter pw = new PrintWriter(sw);
    PrintWriter pw2 = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
    pw.print("POST "+uri+" HTTP/1.1");
    pw.print("\r\n");
    pw.print("Host: " + hostname);
    pw.print("\r\n");
//    pw.print("Content-Type: application/soap+xml; charset=utf-8");
    pw.print("Content-Type: text/xml");
    pw.print("\r\n");
    pw.print("Authorization: Basic " + authStringEnc);
    pw.print("\r\n");
    int length = msg.getBytes().length;
    pw.print("Content-Length: " + length);
    pw.print("\r\n");
    pw.print("\r\n");
    pw.print(msg);
    pw.print("\r\n");
    pw.flush();

    String all = sw.getBuffer().toString();
    System.out.println("SENDING:");
    System.out.println(all);
    pw2.print(all);
    pw2.flush();

    BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
    for (String line ; (line = reader.readLine()) != null ; ) {
      System.out.println(line);
    }
    reader.close();
    pw.close();
    socket.close();
  }

Why all the print(“\r\n”) and not println()? because I was going nuts trying to figure out what was the problem and tried to ensure no linefeed conversion problems would be there..

Problem: At first I looked up the examples from W3C SOAP tutorials/examples. There the content-type was set to “application/soap+xml” as shown in the commented line in the example above. This kept producing “500 internal server error”, with “soap-fault” mentioned in the header. Was confusing since there seemed to be no other explanation. I had copy-pasted the response read code from somewhere and it stopped after headers.. Fixed in the above now.

Anyway, after installing Wireshark (what a pain on OSX..), I finally figured it out. There was also an error string in the response body “Transport level information does not match with SOAP Message namespace URI”. The problem is that the namespace in my SOAP message was for SOAP 1.1, and the content-type “application/soap+xml” is for SOAP 1.2. And in some combination the SOAPAction header would be required as well.. The answer? Change the content-type to “text/xml”, which makes it all match SOAP 1.1 specs. Whooppee.

Some good diffs at http://www.cnblogs.com/wangpei/p/3937541.html.

The socket version is handy to print out exactly what you are sending and to debug if the error is in some configuration of the libraries used or just in the data being sent. Since the socket version uses no libraries, it should rule that out..

Of course, running this on Android might as well use the Apache HttpClient that comes with it. Version using Apache HttpClient v4.4.1 on a desktop (yesyes port it to Android later..):

  public static void sendHTTPClient(String url, String body) throws Exception {
    CloseableHttpClient httpclient = HttpClients.createDefault();
    HttpPost post = new HttpPost(url);

    String authString = "username:password";
    byte[] authEncBytes = Base64.getEncoder().encode(authString.getBytes());
    String authStringEnc = new String(authEncBytes);

    StringEntity strEntity = new StringEntity(body, "UTF-8");
    strEntity.setContentType("text/xml");
    post.setHeader("Authorization", "Basic " + authStringEnc);
    post.setEntity(strEntity);

    HttpResponse response = httpclient.execute(post);
    HttpEntity respEntity = response.getEntity();

    System.out.println("Response:");
    System.out.println(EntityUtils.toString(respEntity));
  }

Leave a comment