Java Http Proxy

2k Views Asked by At

I'm writing a small proxy in Java that basically picks out 2 specific files and does some extra processing on them. One URL it just grabs some info out of the content before passing it along. The other file I want to filter the response content, which is just xml deflate encoded (I want to remove some child elements).

Now, the proxy works fine when I just pass though all content. However, when I try to filter the xml file it doesn't actually send the content to the client ???

Here is some code:

Within the Thread run() method that is spawned when accepting a Socket connection, once I determine the request is for the file I want to filter, I call:

filterRaceFile(serverIn, clientOut);     // This won't send content     
//streamHTTPData(serverIn, clientOut, true); // This passes through fine (but all content of course).

and here is the filtering method itself:

private void filterRaceFile(InputStream is, OutputStream os) {
    // Pass through headers before using deflate and filtering xml
    processHeader(is, os, new StringBuilder(), new StringBuilder());        

    // Seems to be 1 line left, inflater doesn't like it if we don't do this anyway...?
    try {
        os.write(readLine(is, false).getBytes());
    } catch (IOException e) {
        e.printStackTrace();
    }       

    InflaterInputStream inflate = new InflaterInputStream(is);
    DeflaterOutputStream deflate = new DeflaterOutputStream(os);
    int c = 0;
    try {
        DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
        Document xdoc = db.parse(inflate);
        Node id = xdoc.getElementsByTagName("id").item(0);
        Node msg = xdoc.getElementsByTagName("message").item(0);
        StringBuilder xml_buf = new StringBuilder("<race>\n");

        xml_buf.append("<id>").append(id.getTextContent()).append("</id>\n");
        xml_buf.append("<message>").append(msg.getTextContent()).append("</message>\n");
        xml_buf.append("<boats>\n");

        NodeList allBoats = xdoc.getElementsByTagName("boat");
        int N = allBoats.getLength();

        for (int i = 0; i < N; ++i)
        {
            Node boat = allBoats.item(i);
            Element boat_el = (Element)boat;                
            double lat = Double.parseDouble(boat_el.getElementsByTagName("lat").item(0).getTextContent());
            double lon = Double.parseDouble(boat_el.getElementsByTagName("lon").item(0).getTextContent());
            double dist = Geodesic.vincenty_earth_dist(proxy.userLat, proxy.userLon, lat, lon)[0];
            if (dist <= LOS_DIST)
            {
                String boatXML = xmlToString(boat);
                //<?xml version="1.0" encoding="UTF-8"?> is prepended to the xml
                int pos = boatXML.indexOf("?>")+2;
                boatXML = boatXML.substring(pos);
                xml_buf.append(boatXML);
                ++c;
            }
        }
        System.out.printf("%d boats within LOS distance\n", c);

        xml_buf.append("</boats>\n");
        xml_buf.append("</race>");

        byte[] xml_bytes = xml_buf.toString().getBytes("UTF-8");
        deflate.write(xml_bytes);

    } catch (Exception e) {
        e.printStackTrace();
    }

    // flush the OutputStream and return
    try {
        os.flush();
    } catch (Exception e) {
        e.printStackTrace();
    }
    }

I also have a simple pass through method that simply writes the server's InputStream to the client's OutputStream, uses readLine also and works fine - ie any other url shows up in the browser no problems, so readLine is ok. The boolean parameter is to let it know it is reading from a deflate stream, as it uses mark and read internally, which isn't supported on deflate streams.

XML is very simple:

<race> 
  {some data to always pass thru}
    <boats>
       <boat>
          <id>1234</id>
          ....
          <lat>-23.3456</lat>
          <lon>17.2345</lon>
       </boat>
       {more boat elements}
     </boats>
   </race>

And it produces the xml I want it to send to the client fine, but the client just doesn't receive it (shows content-length of 0 in a web-debugger, although there is no Content-Length header in the original response either)

Any ideas as to what is going on, or what I should be doing that I am not ??

2

There are 2 best solutions below

0
On

I don't see any error, but if you are already using a Document, can't you just modify the document and then use your XML library to write the whole Document out? This seems more robust than manual XML formatting.

1
On

You need to add a call to deflate.close() after deflate.write() in order to flush the output and correctly close the stream.