9

i try to write simplest possible server app in Java, displaying html form with textarea input, which after submitting gives me possibility to parse xml typed in that textarea. For now i build simple serversocket based server like that:

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class WebServer {

  protected void start() {
    ServerSocket s;
    String gets = "";
    System.out.println("Start on port 80");
    try {
      // create the main server socket
      s = new ServerSocket(80);
    } catch (Exception e) {
      System.out.println("Error: " + e);
      return;
    }

    System.out.println("Waiting for connection");
    for (;;) {
      try {
        // wait for a connection
        Socket remote = s.accept();
        // remote is now the connected socket
        System.out.println("Connection, sending data.");
        BufferedReader in = new BufferedReader(new InputStreamReader(
            remote.getInputStream()));
        PrintWriter out = new PrintWriter(remote.getOutputStream());

        String str = ".";

        while (!str.equals("")) {
          str = in.readLine();
          if (str.contains("GET")){
            gets = str;
            break;
          }
        }

        out.println("HTTP/1.0 200 OK");
        out.println("Content-Type: text/html");
        out.println("");
        // Send the HTML page
        String method = "get";
        out.print("<html><form method="+method+">");
        out.print("<textarea name=we></textarea></br>");
        out.print("<input type=text name=a><input type=submit></form></html>");
        out.println(gets);
        out.flush();

        remote.close();
      } catch (Exception e) {
        System.out.println("Error: " + e);
      }
    }
  }

  public static void main(String args[]) {
    WebServer ws = new WebServer();
    ws.start();
  }
}

After form (textarea with xml and one additional text input) is submitted in 'gets' String-type variable I have Urlencoded values of my variables (also displayed on the screen, it looks like that:

gets = GET /?we=%3Cnetwork+ip_addr%3D%2210.0.0.0%2F8%22+save_ip%3D%22true%22%3E%0D%0A%3Csubnet+interf_used%3D%22200%22+name%3D%22lan1%22+%2F%3E%0D%0A%3Csubnet+interf_used%3D%22254%22+name%3D%22lan2%22+%2F%3E%0D%0A%3C%2Fnetwork%3E&a=fooBar HTTP/1.1 

What can i do to change GET to POST method (if i simply change it in form and than put " if (str.contains("POST")){" it gives me string like

gets = POST / HTTP/1.1

with no variables. And after that, how i can use xml from my textarea field (called 'we')?

skaffman
  • 398,947
  • 96
  • 818
  • 769
qqryq
  • 1,814
  • 3
  • 18
  • 20
  • Hi qqryq. I have the same code. Did you figured out how to read POST body? I tried to run second while cycle as cygri says, but methods inputStream.read or bufferedReader.readline block my thread. How you read POST body? – Alexmelyon Feb 12 '14 at 09:27
  • 1
    Oh, I realized! I need read "Content-Length: " header and do for-cycle to to this length. – Alexmelyon Feb 12 '14 at 13:09

5 Answers5

22

As there is a blank line following the headers, here is my relatively simple way of getting the post payload data, after having read the header information using the readLine() method of BufferedReader.

//socket is an instance of Socket
InputStream is = socket.getInputStream();
InputStreamReader isReader = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isReader);

//code to read and print headers
String headerLine = null;
    while((headerLine = br.readLine()).length() != 0){
        System.out.println(headerLine);
    }

//code to read the post payload data
StringBuilder payload = new StringBuilder();
        while(br.ready()){
            payload.append((char) br.read());
            }
System.out.println("Payload data is: "+payload.toString())
VSarin
  • 359
  • 2
  • 5
  • thank you so much... this what i'm looking for . . . . – Mang Jojot Mar 08 '19 at 16:39
  • This worked well. But it return not only data but entire post request description. Is there any way to get only data. or we have to string splitting as conventional way? – SWIK Jan 10 '21 at 10:56
9

This is my implementation for read POST body:

try {
    Socket socket = params[0];
    BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
    BufferedWriter out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
    // read request
    String line;
    line = in.readLine();
    StringBuilder raw = new StringBuilder();
    raw.append("" + line);
    boolean isPost = line.startsWith("POST");
    int contentLength = 0;
    while (!(line = in.readLine()).equals("")) {
        raw.append('\n' + line);
        if (isPost) {
            final String contentHeader = "Content-Length: ";
            if (line.startsWith(contentHeader)) {
                contentLength = Integer.parseInt(line.substring(contentHeader.length()));
            }
        }
    }
    StringBuilder body = new StringBuilder();
    if (isPost) {
        int c = 0;
        for (int i = 0; i < contentLength; i++) {
            c = in.read();
            body.append((char) c);
            Log.d("JCD", "POST: " + ((char) c) + " " + c);
        }
    }
    raw.append(body.toString());
    publishProgress(raw.toString());
    // send response
    out.write("HTTP/1.1 200 OK\r\n");
    out.write("Content-Type: text/html\r\n");
    out.write("\r\n");
    out.write(new Date().toString());
    if (isPost) {
        out.write("<br><u>" + body.toString() + "</u>");
    } else {
        out.write("<form method='POST'>");
        out.write("<input name='name' type='text'/>");
        out.write("<input type='submit'/>");
        out.write("</form>");
    }
    //
    // do not in.close();
    out.flush();
    out.close();
    socket.close();
    //
} catch (Exception e) {
    e.printStackTrace();
    StringWriter sw = new StringWriter();
    e.printStackTrace(new PrintWriter(sw));
    publishProgress('\n' + sw.toString());
}

I do it for android and publishProgres in my case means:

protected void onProgressUpdate(String... values) {
        super.onProgressUpdate(values);
        instance.logTextView.append(values[0]);
    }
Alexmelyon
  • 1,168
  • 11
  • 18
  • 2
    Basically, First read should pickup all header lines. After that Content-Length header should be used to know upfront how many characters should be read and then simply move ahead to further processing – prash Sep 08 '16 at 14:15
6

A typical HTTP POST request looks like this:

POST / HTTP/1.1
Host: www.example.com
Accept: text/html,*/*;q=0.5
User-Agent: BrowserName/1.0
Referer: http://www.example.com/
Content-type: application/x-www-form-urlencoded; charset=utf-8

foo=1&bar=2

The first line contains the method (typically GET or POST, but there's more, like HEAD, PUT, DELETE), the request URI, and the protocol version. Then there are a number of request headers, which may not be so important for a simple server. If the method is one that takes a request body (POST and PUT), then after the headers there's a blank line followed by the request body. In the case of a POST from an HTML form, the body will consists of key=value pairs for all form elements, joined by &. The values will be %-encoded.

You just need to take care of properly parsing the entire request.

You should be aware that line endings in HTTP are supposed to be Windows-style (\r\n). The readline() method might interpret this as two linebreaks, so it might look like there's an empty line between each of the real lines.

cygri
  • 9,412
  • 1
  • 25
  • 47
4

From this -

We need to first read headers and then read again from the same BufferedReader using actual content length provided in the header section :-

BufferedReader in = new BufferedReader(new InputStreamReader(is));
String line;
line = in.readLine();
while ((line = in.readLine()) != null && (line.length() != 0)) {
    System.out.println("HTTP-HEADER: " + line);
    if (line.indexOf("Content-Length:") > -1) {
    postDataI = new Integer(
        line.substring(
            line.indexOf("Content-Length:") + 16,
            line.length())).intValue();
    }
}
String postData = "";
// read the post data
if (postDataI > 0) {
    char[] charArray = new char[postDataI];
    in.read(charArray, 0, postDataI);
    postData = new String(charArray);
}

HTH

prash
  • 896
  • 1
  • 11
  • 18
3

POST data is not in the first line. Print all lines and you'll see. It's actually immediately after a blank line.

irreputable
  • 44,725
  • 9
  • 65
  • 93
  • 3
    ok, but how can i pront everything if i have 'while (!str.equals("")) {' statement? Ignore the first blank line and stop on another? – qqryq Jun 13 '10 at 21:19