Raw HTTPS requests using Java

Posted in software by Christopher R. Wirz on Mon Apr 02 2018



HTTP is just a protocol that involves making a TCP connection, sending a request body, then awaiting a response. The request body and response follow the HTTP Specification. A sample request may be as follows:

GET /chrisfact HTTP/1.1
Host: www.chriswirz.com
Content-Length: 0

In order to change this into a HTTPS request (the extra S is for "Secure"), you have to change two things. First off, it is custom to use port 443 for HTTPS connections. This is done both in the request header and the creation of the TCP socket. We can change the request as follows:

GET /chrisfact HTTP/1.1
Host: www.chriswirz.com:443
Content-Length: 0

Now, we just have to create a TCP connection. Here is the complete java solution to call the chrisfact API (it's an Alexa skill). This is actually the same as just going to https://www.chriswirz.com/chrisfact in your browser.


import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import java.lang.Integer;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class Main
{
    public static void main(String[] args) {
        String response = "";
        try {
            // Fetch IP address by getByName()
            final InetAddress ip = InetAddress.getByName(new URL("https://www.chriswirz.com")
                                                        .getHost());
            final SSLSocketFactory factory =
                (SSLSocketFactory)SSLSocketFactory.getDefault();
            final SSLSocket socket =
                (SSLSocket)factory.createSocket(ip.getHostAddress(), 443);
        
            socket.startHandshake();
        
            final PrintWriter out = new PrintWriter(
                new BufferedWriter(
                    new OutputStreamWriter(
                        socket.getOutputStream())));
        
            // Send the raw HTTP document to the TCP stream
            out.println("GET /chrisfact HTTP/1.1");
            out.println("Host: www.chriswirz.com:443");
            out.println("Content-Length: 0");
            out.println();
            out.flush();
        
            if (out.checkError()) {
                System.out.println(
                    "SSLSocketClient:  java.io.PrintWriter error");
            }
        
            final BufferedReader in = new BufferedReader(new InputStreamReader(
                socket.getInputStream()
            ));
            
            String inputLine = "start";
            final StringBuffer responseBuffer = new StringBuffer();
            while (!inputLine.equals("") && ( inputLine = in.readLine()) != null) {
                responseBuffer.append(inputLine);
                responseBuffer.append("\r\n");
            }
            int contentLength = 0;
            final String responseBufferString = responseBuffer.toString();

            // The TCP connection doesn't close unless it times out, so we use Content-Length
            if (responseBufferString.contains("Content-Length: ")) {
                final String contentLengthStr = responseBufferString.split("Content-Length: ")[1].split("\n")[0].trim();
                contentLength = Integer.parseInt(contentLengthStr);
            }
            int lastChar = -1;
            while (contentLength > 0 && (lastChar = in.read())!=-1) {
                responseBuffer.append((char)lastChar);
                contentLength--;
            }
            in.close();
        
            // get the result
            response = responseBuffer.toString();
        
            out.close();
            socket.close();
        
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(response);
    }
}

Running the above code will give the following response:

HTTP/1.1 200 OK
Server: nginx/1.18.0 (Ubuntu)
Date: Tue, 06 Mar 2018 16:34:55 GMT
Content-Type: text/json
Content-Length: 396
Connection: keep-alive

{
    "version": "1.0",
    "response": {
    "outputSpeech": {
        "type": "SSML",
        "ssml": "<speak>Here's your ChrisWirz.com fact: Chris runs a 5k every day.</speak>"
    },
    "card": {
        "type": "Simple",
        "title": "Chris Fact",
        "content": "Chris runs a 5k every day."
    },
    "shouldEndSession": true
    },
    "hostedOnChrisWirzCom": true,
    "sessionAttributes": {}
}

Now you can use Java to write tests against your REST API, or gather data from various sources - securely using HTTPS.