0

I'm working with a system that, in order to make a particular service call, requires the following:

  • Issue an HTTP PUT command
  • Set the URL to some_url_here
  • Set the end user certificate.
  • Ensure that the entity body is empty and set the Content-Length headers to 0.

Here's the method I wrote to build secure connections. I've tested the GETs; they work fine. I know the problem isn't in the certificate.

public HttpsURLConnection getSecureConnection(final URL url, final String method, final int connectTimeout,
                                              final int readTimeout) throws IOException {
    Validate.notNull(sslContext);
    Validate.notNull(url);
    Validate.notNull(method);
    Validate.isTrue(connectTimeout > 0);
    Validate.isTrue(readTimeout > 0);
    HttpsURLConnection connection;
    try {
        connection = (HttpsURLConnection) url.openConnection();
    } catch (final IOException ioe) {
        LOGGER.error("[CertificateLoader] Unable to open URL connection!", ioe);
        throw new IOException("Unable to open URL connection!", ioe);
    }
    connection.setSSLSocketFactory(sslContext.getSocketFactory());
    connection.setRequestMethod(method);
    connection.setConnectTimeout(connectTimeout);
    connection.setReadTimeout(readTimeout);
    connection.setHostnameVerifier(NoopHostnameVerifier.INSTANCE);
    if (method.equals("PUT")) {
        connection.setRequestProperty("Content-Length", "0");
    }
    if (connection.getContentLength() > 0) {
        Object foo = connection.getContent();
        LOGGER.error("This is what's in here: " + foo.toString());
    }
    return connection;
}

Now, the reason for that funky if at the bottom is that when I go to make the PUT call, even though I'm not putting a body on the call directly, my logs insist I'm getting a non-zero content length. So, I added that little block to try to figure out what's in there, and lo and behold it reports the following:

This is what's in here: sun.net.www.protocol.http.HttpURLConnection$HttpInputStream@70972170

Now, that sucker's in there by default. I didn't put it in there. I didn't create that object to put in there. I just created the object as is from the URL, which I created from a String elsewhere. What I need is a way to remove that HttpInputStream object, or set it to null, or otherwise tell the code that there should be no body to this PUT request, so that my server won't reject my message as being ill-formatted. Suggestions?

KristinaTracer
  • 108
  • 1
  • 10
  • 1
    You're mixing up the request and response - you're examining the response content length rather than the request content length. – Erwin Bolwidt Nov 09 '17 at 03:44
  • It's the only content length I can find. At that point in the code I haven't called `connect()` and don't have a response object. Where do I find the request content length, then, so I can set it appropriately? – KristinaTracer Nov 09 '17 at 03:49
  • 1
    That object isn't for the content you send (or don't send); it's for the content you retrieve (or don't retrieve). You should be able to read from that input stream and find that it's empty. – Dawood ibn Kareem Nov 09 '17 at 03:50
  • let me add to this that I know the call is failing because the remote server is returning a 411, so I know the content length is wrong. Setting Content-Length to "0" wasn't enough to fix it, so I'm trying to find what other options I have. – KristinaTracer Nov 09 '17 at 03:51
  • [Related](https://stackoverflow.com/q/19227142) – Dawood ibn Kareem Nov 09 '17 at 03:53
  • You'd think that, so what am I doing here that's setting a body or content? – KristinaTracer Nov 09 '17 at 03:57
  • The server might believe that Content-Length=0 is invalid for a PUT request. – Dawood ibn Kareem Nov 09 '17 at 03:59
  • It better not; it's working in Production. – KristinaTracer Nov 09 '17 at 04:22
  • More to the point, the server works as descibed when other systems call it in the way described. I'm trying to automate it using Java, and the code isn't doing what it claims it should be doing. – KristinaTracer Nov 09 '17 at 04:31

1 Answers1

2

Now, the reason for that funky if at the bottom is that when I go to make the PUT call, even though I'm not putting a body on the call directly, my logs insist I'm getting a non-zero content length.

The way to set a zero Content-length is as follows:

connection.setDoOutput(true); // if it's PUT or POST
connection.setRequestMethod(method);
connection.getOutputStream().close(); // send a zero length request body

It is never necessary to call connection.setRequestProperty("Content-Length", "0"). Java sets it for you. Or possibly it is omitted, in which case you may be able to ensure it via

connection.setFixedLengthStreamingMode(0);

So, I added that little block to try to figure out what's in there, and lo and behold it reports the following:

This is what's in here: sun.net.www.protocol.http.HttpURLConnection$HttpInputStream@70972170

Now, that sucker's in there by default. I didn't put it in there.

Java put it there.

I didn't create that object to put in there.

Java put it there.

I just created the object as is from the URL, which I created from a String elsewhere. What I need is a way to remove that HttpInputStream object, or set it to null, or otherwise tell the code that there should be no body to this PUT request, so that my server won't reject my message as being ill-formatted.

No it isn't. It is an input stream, not a piece of content. And it is an input stream to the content of the response, not of the request. And in any case, the server is perfectly entitled to return you content in response to your request.

Your task is to:

  1. Get the response code and log it.
  2. If it is >=200 and <= 299, get the connection's input stream.
  3. Otherwise get the connection's error stream.
  4. Whichever stream you got, read it till end of stream, and log it.

That will tell you what is really happening.

I will add that a PUT without a body is a really strange thing to do. Are you sure you've understood the requirement? 411 means Length required.

user207421
  • 305,947
  • 44
  • 307
  • 483
  • Did you read the remainder of the exchange? I did log it. I'm getting a 411, which says the server is rejecting my connection. I know what's wrong; something I'm doing, or not doing is causing my request to be sent with a content body. I wish to remove the content body from my outbound message, which confuses me because I'm not doing anything specific to _set_ a message body in my request. – KristinaTracer Nov 09 '17 at 06:03
  • The first part of your message is useful, thank you. I'll test it when I get in to work tomorrow. – KristinaTracer Nov 09 '17 at 06:41
  • Did you read my answer? There is no evidence in your question of the request containing a content body. There is only a *response* input stream, which you aren't even troubling to consume, so you don't know whether it's zero length or not, which in any case is immaterial. – user207421 Nov 09 '17 at 07:16
  • So, having added a consumer for the error stream elsewhere in the code, it returned precisely what I thought it would: "HTTP Error 411. The request must be chunked or have a content length." Which is why I'm confused, as I'm not _setting_ a content body, so I don't know why it thinks there's a content length if the header should be automatic. – KristinaTracer Nov 09 '17 at 19:47
  • Also: `setDoOutput(true);` forced it to use POST, despite explicitly setting the method to PUT; these got rejected with 405s. Attempting `connection.getOutputStream().close();` either runtime-failed (if setDoOutput(false)) or didn't help because the connection type was wrong (if setDoOutput(true)). `connection.setFixedLengthLengthStreamingMode(0);` doesn't appear to have had any measurable impact one way or the other. – KristinaTracer Nov 09 '17 at 19:59
  • 1
    It would, perhaps, help future querents if you explain _why_ the code you're posting works the way it does, instead of simply posting _that_ it works. It's fair to say I didn't copy the code exactly. It's also fair to say that expecting people to simply copy your answers doesn't lead to fewer questions in the future. You're really doing a great job of demonstrating why so many non-engineers dislike dealing with technical people. – KristinaTracer Nov 10 '17 at 01:55