-1

I need request to have a particular headers order. So I call setRequestProperty for each property in required order:

        URL url = new URL(urlString);
        HttpURLConnection request = (HttpURLConnection) url.openConnection();
        request.setRequestMethod("GET");
        request.setRequestProperty("Host", "myhostname.com");
        request.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:85.0) Gecko/20100101 Firefox/85.0");
        request.setRequestProperty("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8");
        request.setRequestProperty("Accept-Language", "ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3");
        request.setRequestProperty("Accept-Encoding", "gzip, deflate, br");
        request.setRequestProperty("Connection", "keep-alive");
        request.setRequestProperty("Cookie", cookies);
        request.setRequestProperty("Upgrade-Insecure-Requests", "1");
        request.setRequestProperty("Cache-Control", "max-age=0");

But actual (sniffed) request looks like:

    GET /api/apitest&code=1 HTTP/1.1\r\n
    User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:85.0) Gecko/20100101 Firefox/85.0\r\n
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\r\n
    Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3\r\n
    Accept-Encoding: gzip, deflate, br\r\n
    Cookie: <cookie>\r\n
    Upgrade-Insecure-Requests: 1\r\n
    Cache-Control: max-age=0\r\n
    Host: myhostname.com\r\n
    Connection: keep-alive\r\n

Is there a way to keep headers order unchanged?

Dr Mido
  • 2,414
  • 4
  • 32
  • 72
Sergey K
  • 19
  • 5
  • 2
    Why is that an issue? – Progman Feb 26 '21 at 21:02
  • 1
    Check page 32 of [RFC 2616 Hypertext Transfer Protocol -- HTTP/1.1](https://tools.ietf.org/html/rfc2616#page-32): "The order in which header fields with differing field names are received is not significant.", so the order of the headers shouldn't be relevant for the receiver. – Progman Feb 26 '21 at 21:20
  • 'I need request to have a particular headers order': no you don't. What makes you think otherwise? Java doesn't provide a way to do that, and additionally no server based on a Java Servlet technology can see the header order anyway. Nor should any other HTTP server care. – user207421 Feb 26 '21 at 23:32
  • I understand that normally headers order does not matter. But in my case I'm accessing API that simply returns error if headers order is changed. For example moving Accept-Encoding before Accept-Language makes server return "403 Forbidden" error. – Sergey K Feb 27 '21 at 08:16
  • Then the problem is at the server, and that's where you need to fix it. It doesn't comply with RFC 2616 and successors. You cannot solve this at the Java client end. Assuming you've diagnosed it correctly, which doesn't seem likely. – user207421 Feb 27 '21 at 09:11
  • It's not my server, I cannot fix it. And there is a probability that it was made on purpose. I diagnosed it correctly, I was just sending plain text HTTP requests with different headers order and getting correct anwers or errors depending on their order. – Sergey K Feb 27 '21 at 15:51
  • It seems far more likely that you were missing an `Authorization:` header. – user207421 Feb 28 '21 at 02:57
  • As I said I was sending plain text HTTP requests using Fiddler. Changing headers order really makes server answer different. Just swapping 2 headers is enough to get 403 Forbidden – Sergey K Feb 28 '21 at 07:58

1 Answers1

0

So I solved it using RawHttp:

RawHttp http = new RawHttp();
RawHttpRequest httpRequest = http.parseRequest("GET /api/apitest&code="+code+" HTTP/1.1\r\n" +
                "Host:myhostname.com\r\n" +
                "User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:85.0) Gecko/20100101 Firefox/85.0\r\n" +
                "Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\r\n" +
                "Accept-Language:ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3\r\n" +
                "Accept-Encoding:gzip, deflate, br\r\n" +
                "Connection:keep-alive\r\n" +
                "Cookie:" + cookies + "\r\n" +
                "Upgrade-Insecure-Requests:1\r\n" +
                "Cache-Control:max-age=0");

        URL url = new URL(api_url);
        Socket httpsSocket = HttpsURLConnection.getDefaultSSLSocketFactory().createSocket(api_url.getHost(),  443);

        httpRequest.writeTo(httpsSocket.getOutputStream());
        RawHttpResponse httpResponse = http.parseResponse(httpsSocket.getInputStream());

        BodyReader body = httpResponse.getBody().get();
        String response = body.decodeBodyToString(StandardCharsets.UTF_8);

Body existance could be checked with

httpResponse.getBody().isPresent()

Now headers order is unchanged and this non-RFC compliant server answers correctly.

To show the results I've changed port to 80, but everything else is same:

Frame 6099: 680 bytes on wire (5440 bits), 680 bytes captured (5440 bits) on interface \Device\NPF_{28393799-9889-4CF5-B65C-ED851CC47ECC}, id 0
Ethernet II, Src: ASUSTekC_<MAC>, Dst: D-Link_<MAC>
Internet Protocol Version 4, Src: 192.168.1.3, Dst: <IP>
Transmission Control Protocol, Src Port: 58530, Dst Port: 80, Seq: 4, Ack: 1, Len: 626
[2 Reassembled TCP Segments (629 bytes): #6096(3), #6099(626)]
Hypertext Transfer Protocol
    GET /api/apitest&code=1 HTTP/1.1\r\n
    Host: myhostname.com\r\n
    User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:85.0) Gecko/20100101 Firefox/85.0\r\n
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\r\n
    Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3\r\n
    Accept-Encoding: gzip, deflate, br\r\n
    Connection: keep-alive\r\n
    Cookie: <cookie>\r\n
    Upgrade-Insecure-Requests: 1\r\n
    Cache-Control: max-age=0\r\n
    \r\n
    [Full request URI: http://myhostname.com/api/apitest&code=1]
    [HTTP request 1/1]
    [Response in frame: 6101]

Then I moved Host header and captured packet changed accordingly:

Frame 78: 680 bytes on wire (5440 bits), 680 bytes captured (5440 bits) on interface \Device\NPF_{28393799-9889-4CF5-B65C-ED851CC47ECC}, id 0
Ethernet II, Src: ASUSTekC_<MAC>, Dst: D-Link_<MAC>
Internet Protocol Version 4, Src: 192.168.1.3, Dst: <IP>
Transmission Control Protocol, Src Port: 58619, Dst Port: 80, Seq: 4, Ack: 1, Len: 626
[2 Reassembled TCP Segments (629 bytes): #76(3), #78(626)]
Hypertext Transfer Protocol
    GET /api/apitest&code=1 HTTP/1.1\r\n
    User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:85.0) Gecko/20100101 Firefox/85.0\r\n
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\r\n
    Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3\r\n
    Accept-Encoding: gzip, deflate, br\r\n
    Connection: keep-alive\r\n
    Cookie: <cookie>\r\n
    Host: myhostname.com\r\n
    Upgrade-Insecure-Requests: 1\r\n
    Cache-Control: max-age=0\r\n
    \r\n
    [Full request URI: http://myhostname.com/api/apitest&code=1]
    [HTTP request 1/1]
    [Response in frame: 80]
Sergey K
  • 19
  • 5
  • Do you have any evidence that you have controlled the order of headers put on the wire with this technique? – user207421 Feb 28 '21 at 02:56
  • Should I proof it somehow? Sniffed packets using Wireshark and order is same as in the source code. Changed order in the source and see the same in captured packets. – Sergey K Feb 28 '21 at 08:10
  • Updated my answer to show captured packets – Sergey K Feb 28 '21 at 08:17