views:

524

answers:

4

I try to create the simplest Simplest WebServer and Client using HTTP. (Please, don't tell me to using Apache HTTPClient).

Client: try to PUT some file to Server.

// **PUT**
if(REQUEST.toUpperCase().equals("PUT")) {

  File sourceFile = new File(fileName);

  if(!sourceFile.canRead()) {
      System.out.println("Have not access to this file...");
      return;
  }

  try {
      BufferedInputStream is = new BufferedInputStream(new FileInputStream(sourceFile));

      URL url = new URL("http://" + HOST+":"+PORT);
      System.setProperty("http.keepAlive", "true");
      HttpURLConnection connection = (HttpURLConnection) url.openConnection();
      connection.setDoInput(true);
      connection.setDoOutput(true);

      connection.setRequestMethod("PUT");
      connection.setRequestProperty("Content-Type", "Application/octet-stream");
      connection.setRequestProperty("Content-Length",Long.toString(sourceFile.length()));
      connection.addRequestProperty("Content-disposition","attachment; filename="+fileName);

      BufferedOutputStream os = new BufferedOutputStream(connection.getOutputStream());

      byte[] buf = new byte[sizeArr];
      int r = 1;
      while((r = is.read(buf)) > 0) {
          os.write(buf, 0, r);
      }
      os.flush();
      os.close();

      System.out.println("Waiting for the response...");//this is written to console

      System.out.println(connection.getResponseCode());//HERE infinite waiting
      is.close();


  } catch (MalformedURLException ex) {
      ex.printStackTrace();
  } catch (IOException ex) {
      ex.printStackTrace();
  }
  }

On Server: if Request == PUT, then:

// **PUT**
if (header.toUpperCase().equals("PUT")) {
    System.setProperty("http.keepAlive", "true");
    String fileName = null;

    if((fileName = extract(request.toUpperCase(),"FILENAME=","\n")) == null) {
        fileName = "UnknownFile.out";
    }

    try {
    File sourceFile = new File(fileName);
    BufferedOutputStream osFile = new BufferedOutputStream 
        (new FileOutputStream(sourceFile));

    byte[] locbuf = new byte[sizeArr];
    int locr = 1;
    while((locr = is.read(locbuf)) > 0) {
        System.out.println("locr= "+locr);//this is written to console
        osFile.write(locbuf, 0, locr);
    }
    System.out.println("Ending to record the data to the file."); 
    //  this is NOT written to console
    osFile.flush();
    osFile.close();
    }
    catch(IOException ex) {
        os.write(CodeRequest("500 Internal Server Error").getBytes());
        os.close();
        ex.printStackTrace();
        return;
    }

    System.out.println("Trying to send 200 OK");
    os.write(CodeRequest("200 OK").getBytes());
    os.flush();
    os.close(); // where os = clientSocket.getOutputStream();
    }

Why doesn't the Client get a Response from the Server? If I interrupted the Client's infinite loop, then WebServer would correctly record data to file. But Client will never know that his file was normally uploaded to the server. If I comment out this statement on Client:

// System.out.println(connection.getResponseCode());

Then Client correctly exit from loop and ends. But Server doesn't even write to console this:

while((locr = is.read(locbuf)) > 0) {
  System.out.println("locr= "+locr);//this is NOT written to console
  osFile.write(locbuf, 0, locr);
}

Server ONLY writes this to console this:

localExcString index out of range: -1

without any Error message.

What's wrong?

A: 

You need to call URLConnection#getInputStream() after the write to actually send the request (and thus retrieve the response). Only after this point you can request the response status. This is implemented so because it might take longer to build the request body than to actually sending it and also because there's actually no point of having a request if you're not interested in the response.

Edit sorry, I was wrong. The getResponseCode() already implicitly calls getInputStream().

Anyway, I created a small testcase (see SSCCE for more info) and it just works fine. Try it and see if it works in your case as well.

Client:

import java.io.IOException;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;

public class Test {

    public static void main(String[] args) throws Exception {
        String data = "Hello!";
        URL url = new URL("http://localhost:8080/playground/test");
        OutputStream output = null;

        try {
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setDoOutput(true);
            connection.setRequestMethod("PUT");

            output = connection.getOutputStream();
            output.write(data.getBytes());

            System.out.println("Response code: " + connection.getResponseCode());
        } finally {
            if (output != null) try { output.close(); } catch (IOException ignore) {}
        }
    }
}

Server:

package mypackage;

import java.io.IOException;
import java.io.InputStream;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class Test extends HttpServlet {

    protected void doPut(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException
    {
        InputStream input = null;

        try {
            input = request.getInputStream();
            int data;
            while ((data = input.read()) > -1) {
                System.out.write(data);
            }
            System.out.println();
        } finally {
            if (input != null) try { input.close(); } catch (IOException ignore) {}
        }
    }
}

Result should be a Response code: 200 in the client's stdout and a Hello! in the server's stdout.

Hope this helps in pinpointing the cause of your problem.

BalusC
BalusCI don`t understand you.On Client I make Headers, which automatically are sended to Server.Then I get OutputStream to write the content (file):BufferedOutputStream os = new BufferedOutputStream(connection.getOutputStream()); byte[] buf = new byte[sizeArr]; int r = 1; while((r = is.read(buf)) > 0) { os.write(buf, 0, r); }------------Server get`s firstly Header`s, then content (file) and THEN send Response to Client.What`s wrong?
rauch
I must have said, the request **body**. Did you try my advice at any way? And what's the result?
BalusC
BalusC :I added statement: BufferedInputStream isTEMP = new BufferedInputStream(connection.getInputStream()); But result is same as before.
rauch
A: 

On Server:

String strFileLen = extract(request.toUpperCase().trim(),"CONTENT-LENGTH:","\n");
                long fileLength = 0;
                if(strFileLen != null) {
                fileLength = Long.parseLong(strFileLen.trim());
                System.out.println("fileLength= "+fileLength);
                };

                byte[] locbuf = new byte[sizeArr];
                int locr = 1;long sumLen = 0;

                if(fileLength != 0) {
                while((sumLen += (locr=is.read(locbuf))) != fileLength) {
                    System.out.println("sumLen= "+sumLen);
                    osFile.write(locbuf, 0, locr);
                }
                }

=================================================== This works well.

rauch
I edited my answer. Check it.
BalusC
A: 

What string is the CodeRequest method returning in the server code? I think that the problem may be that you are not putting a CRLF at the end of the status line and another one at the end of the response header. For details, read the HTTP 1.1 specification.

Stephen C
protected String CodeRequest(String code) { String response = "HTTP/1.1 "+code+"\n"; DateFormat df = DateFormat.getTimeInstance(); df.setTimeZone(TimeZone.getTimeZone("GMT")); response = response + "Date: " + df.format(new Date()) + "\n"; response = response + "Connection: close\n" + "Server: WEBServer\n\n"; return response; }
rauch
@raunch - according to the HTTP spec, those "\n"s should be "\r\n".
Stephen C
A: 

Your example code for the server doesn't show the declaration and initialisation of 'is'. However, my guess is that since the session is keep alive the call to is.read() will block until some data arrives. You have set the content length in the client, so I would be expecting to see the read loop complete when that amount of data has been successfully read.

Roger Orr
ServerSocket server = new ServerSocket(PORT); System.out.println("server is started"); while(true) { WEBServer thr = new WEBServer(server.accept()); thr.run(); }==============================Class wich implements Runnable: Socket sockClient; public WEBServer(Socket socket) { this.sockClient = socket; }public void run() { int sizeArr = 1024; try { InputStream is = sockClient.getInputStream(); OutputStream os = sockClient.getOutputStream();
rauch
Since your session is keep-alive is.read() will keep reading and won't return with a negative or zero value until the client disconnects.You need to change your read loop to terminate once the number of bytes read equals the content length given in the header.
Roger Orr
Oh yeah man... You were right.Thanks a lot.Now it works...
rauch