I just written a small program to test something, as below:
public class Main {
public static void main(String[] args){
ServerSocket serverSocket = null;
Socket clientSocket = null;
DataInputStream dataInputStream= null;
BufferedWriter bufferedWriter = null;
String line ;
try {
serverSocket = new ServerSocket(80);
clientSocket = serverSocket.accept();
dataInputStream = new DataInputStream(clientSocket.getInputStream());
while((line = dataInputStream.readLine()) != null){
System.out.println(line);
}
bufferedWriter = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream()));
bufferedWriter.write("HTTP/1.0 200 OK \n Date: Fri, 31 Dec 1999 23:59:59 GMT \n Content-Type: text/html \n Content-Length: 1354 \n <html>abcde<html/>");
bufferedWriter.flush();
} catch (IOException e) {
System.out.println("socket port cannot be opened.");
e.printStackTrace();
} finally{
try {
serverSocket.close();
bufferedWriter.close();
} catch (IOException e) {
System.out.println("socket port cannot be closed.");
e.printStackTrace();
}
}
}
}
I found the http response format from the internet, it should be correct. The problem is my browser keep on waiting for the response data (determine from the spinning logo), but the data is not returned successful. What mistake I have made?
I connect to the Java program by typing "localhost" in the browser, I can print out the request string in Java program but only fail to send back the response.
First things first, there are a few issues with the HTTP message being sent: Each line of an HTTP message is separated/ended by CR LF, rather than just a line feed (although I doubt this could be the problem, you should replace "\n" with "\r\n"). In addition, the Content-Length isn't equal to the real size of the message's body, which should be replaced. Before the actual body, all HTTP messages should have an empty line, which you have not. Finally, the forward slash at <html/> should also come before html, like this: </html>
To sum up:
bufferedWriter.write("HTTP/1.0 200 OK\r\nDate: Fri, 31 Dec 1999 23:59:59 GMT\r\nContent-Type: text/html\r\nContent-Length:18\r\n\r\n<html>abcde</html>");
Now for the real issue: the reading loop was constantly waiting for more data. Comparing the result of readLine() to null actually doesn't do what you were looking for. TCP connections make streams of data, and you never know if the client just stopped sending data in a particular moment. Instead, you can read until you find an empty line, which marks the end of the HTTP message's head. Web browsers usually do not send additional content in GET messages, therefore you will not be missing any data.
BufferedReader reader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
while(!(line = reader.readLine()).isEmpty()){
System.out.println(line);
}
Could it be the Content-Length header field? Try replacing the response with this:
bufferedWriter.write("HTTP/1.0 200 OK \n Date: Fri, 31 Dec 1999 23:59:59 GMT \n Content-Type: text/html \n Content-Length: 18\n <html>abcde<html/>");
(Notice the Content-Length: 18 vs. the original Content-Length: 1354)
I suspect the browser is waiting for your application to send more data.
Edit: This works for me:
import java.util.*;
import java.io.*;
import java.net.*;
public class Main {
public static void main(String[] args) throws Exception{
ServerSocket serverSocket = null;
Socket clientSocket = null;
DataInputStream dataInputStream= null;
BufferedWriter bufferedWriter = null;
try {
serverSocket = new ServerSocket(8080);
clientSocket = serverSocket.accept();
BufferedReader reader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
while (clientSocket.getInputStream().available() > 0) {
String line = reader.readLine();
if (line == null) {
break;
}
System.out.println(line);
}
bufferedWriter = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream()));
bufferedWriter.write("HTTP/1.0 200 OK \n Date: Fri, 31 Dec 1999 23:59:59 GMT \n Content-Type: text/html \n\n <html>abcde<html/>");
bufferedWriter.flush();
clientSocket.close();
} catch (IOException e) {
System.out.println("socket port cannot be opened.");
e.printStackTrace();
} finally{
try {
serverSocket.close();
bufferedWriter.close();
} catch (IOException e) {
System.out.println("socket port cannot be closed.");
e.printStackTrace();
}
}
}
}
The culprit was the while loop. The method you were calling to fetch more data was blocking until that data was available (which would never happen). I'm not sure whether or not what I did was completely necessary, but it works.
I see two problems:
The content-length header says 1354 bytes will follow. You only are writing 20 or so. Content-length is optional; don't include it if you're not using the correct value.
You need a blank line at the end of the headers and before the content: ".... Content-Length: xx\n\n<html>..."
Related
Hello I'm making an HTTP client. I'm trying to fetch google.com's html code. I have a problem the the BufferedReader.readLine() function is blocking endlessly because the remote server apparently doesn't send a blank line? Or could it be that my request is wrong?
Appreciate any help!
public static void main(String[] args) {
String uri = "www.google.com";
int port = 80;
Socket socket = new Socket(uri, port);
PrintWriter toServer = new PrintWriter(socket.getOutputStream(), true);
InputStream inputStream = socket.getInputStream();
get(uri, port, language, socket, toServer, inputStream);
}
public static void get(String uri, int port, String language, Socket socket, PrintWriter toServer, InputStream inputStream) {
try {
toServer.println("GET / HTTP/1.1");
toServer.println("Host: " + uri + ":" + port);
toServer.println();
// Parse header
StringBuilder stringBuilder = new StringBuilder();
BufferedReader fromServer = new BufferedReader(new InputStreamReader(inputStream));
String line;
while ((line = fromServer.readLine()) != null) {
stringBuilder.append(line);
}
System.out.println("done");
} catch (IOException e) {
e.printStackTrace();
}
}
You are sending a HTTP/1.1 request which by default enables HTTP keep-alive. This means that the server might keep the TCP connection open after the response was sent in order to accept more requests from the client. Your code instead assumes that the server will close the connection after the response was finished by explicitly expecting readline to return null. But since the server will not close the connection (or only after some long timeout) the readline will just block.
To fix this either use HTTP/1.0 (which has keep-alive off by default) instead of HTTP/1.1 or explicitly tell the server that no more requests will be send by adding a Connection: close header.
Please note that in general HTTP is way more complex than you might think if you've just seen a few examples. The problem you face in your question is only a glimpse into more problems which you will face when continuing this path. If you really want to implement your own HTTP handling instead of using established libraries please study the actual standard instead of just assuming a specific behavior.
I am trying to read the data from an HttpPost, but when I read the data from BufferedReader I only get the header info. Please see the code below.
Here is the server
try {
ServerSocket server = new ServerSocket(8332);
System.out.println("Listening for connection on port 8332 ....");
while (true) {
try (Socket socket = server.accept()) {
BufferedReader buffer = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String request = "";
String line;
while ((line = buffer.readLine()) != null) {
System.out.println(line);
request += line;
}
System.out.print("port 8332 reading: " + request);
} catch (IOException e) {
System.out.print(e.getMessage());
}
}
} catch (IOException e){
System.out.print(e.getMessage());
}
Here is the Client
HttpClient client = HttpClientBuilder.create().build();
HttpPost post = new HttpPost("http://localhost:8332");
try {
StringEntity params =new StringEntity("details={\"name\":\"myname\",\"age\":\"20\"} ");
post.addHeader("content-type", "application/x-www-form-urlencoded");
post.setEntity(params);
client.execute(post);
} catch (IOException e){
System.out.println(e);
}
When I run this program I just get the following output
Listening for connection on port 8332 ....
POST / HTTP/1.1
content-type: application/x-www-form-urlencoded
Content-Length: 37
Host: localhost:8332
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.5.6 (Java/1.8.0_131)
Accept-Encoding: gzip,deflate
Upon debugging it seems like the program is not exiting this while loop
while ((line = buffer.readLine()) != null) {
System.out.println(line);
request += line;
}
But I can't figure out why. Please help I have been stuck all day.
Thanks in advance
But I can't figure out why.
The only way that your server will get a null from buffer.readLine() is if the client closes its socket output stream.
The problem is that the client side is trying to keep the connection alive ... which means that it won't close its socket output stream. That means that the server needs to respect the "content-length" header; i.e. count the number of bytes read rather than looking for an end-of-stream.
Fundamentally, your server side is not implementing the HTTP 1.1 specification correctly.
What to do?
Well my advice is to not try to implement HTTP starting from sockets. Use and existing framework ... or the Apache HttpComponents library.
But if you insist on doing it this way1, read the HTTP specification thoroughly before you start trying to implement it. And consult the spec whenever you run into problems with your implementation to check that you are doing the right thing.
1 - Definition: Masochism - the enjoyment of an activity that appears to be painful or tedious.
Use !=0 instead of !=null in your while loop:
while((line = buffer.readLine()).length() !=0) {
output:
port 8332 reading: POST / HTTP/1.1content-type:
application/x-www-form-urlencodedContent-Length: 18Host:
localhost:8332Connection: Keep-AliveUser-Agent:
Apache-HttpClient/4.5.3 (Java/1.8.0_171)Accept-Encoding:
gzip,deflateorg.apache.http.NoHttpResponseException: localhost:8332
failed to respond
I am seeing this exception:
java.net.SocketTimeoutException: Timeout attempting to read data from the socket
Here's the code generating it:
public static String extractBody(HttpServletRequest request) {
StringBuffer sb = new StringBuffer();
String line = null;
try {
//BufferedReader reader = request.getReader();
BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream()));
while ((line = reader.readLine()) != null) {
sb.append(line);
}
} catch (Exception e) {
logger.fatal("Failed to read from socket with content-length: {}", request.getHeader("Content-Length"));
e.printStackTrace();
}
return sb.toString();
}
When it happens, that content-length that's written is non-zero. It's like this
Failed to read from socket with content-length: 279645
What is causing this timeout? Is it that the socket was left unclosed by the client? Is there something else I am missing? Is there a different way I should be reading the body data from a servlet request? Most of the requests work fine, I only see this error sometimes but it may be a certain client version or platform or something.
The content-length header didn't match the actual content length and it was waiting for more data.
I have a java program that is trying to make an HTTP request over a socket. For some reason slashes in the string are messing it up.
I have a try/catch and it gets caught as soon as the socket is created with a string that has a slash.
Socket socket = new Socket("www.google.ca", port);
Response
HTTP/1.1 400 Bad Request
Content-Length: 54
Content-Type: text/html; charset=UTF-8
Date: Fri, 14 Oct 2016 06:05:43 GMT
Connection: close
<html><title>Error 400 (Bad Request)!!1</title></html>
Now with a slash
Socket socket = new Socket("www.google.ca/", port);
Gets caught.
My request.
outputStream.println("GET / HTTP/1.1");
outputStream.println("");
outputStream.flush();
I'm trying to access a specific site with the hostname and path which has slashes. What is happening?
The first error HTTP/1.1 400 Bad Request happens because of wrong request path. It is hard to find the reason without knowing your code.
The second error happens like Andy Turner already said, because the host name is wrong. InetAddress can not resolve the host names with slashes.
This example works for me:
public static void main(String[] args) throws Exception {
Socket s = new Socket(InetAddress.getByName("google.com"), 80);
PrintWriter pw = new PrintWriter(s.getOutputStream());
pw.println("GET /about/ HTTP/1.1"); // here comes the path
pw.println("f-Modified-Since: Wed, 1 Oct 2017 07:00:00 GMT");
pw.println("");
pw.flush();
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
String line;
while((line = br.readLine()) != null){
System.out.println(line);
}
br.close();
}
You just have to set the path in this line:
pw.println("GET /about HTTP/1.1");
More specifically than IOException, you are getting an UnknownHostException (a subclass of IOException), because host names can't contain a slash.
You should print/log the exception's stack trace in the catch block; this problem would then be a lot more apparent.
Good evening to all of you
I want to fetch a webpage using the socket class in java and i have done this as
import java.net.*;
import java.io.*;
class htmlPageFetch{
public static void main(String[] args){
try{
Socket s = new Socket("127.0.0.1", 80);
DataInputStream dIn = new DataInputStream(s.getInputStream());
DataOutputStream dOut = new DataOutputStream(s.getOutputStream());
dOut.write("GET /index.php HTTP/1.0\n\n".getBytes());
boolean more_data = true;
String str;
while(more_data){
str = dIn.readLine();
if(str==null)
more_data = false;
System.out.println(str);
}
}catch(IOException e){
}
}
}
But it is just giving the null's.
Output
HTTP/1.1 302 Found
Date: Wed, 01 Dec 2010 13:49:02 GMT
Server: Apache/2.2.11 (Unix) DAV/2 mod_ssl/2.2.11 OpenSSL/0.9.8k PHP/5.2.9 mod_apreq2-20051231/2.6.0 mod_perl/2.0.4 Perl/v5.10.0
X-Powered-By: PHP/5.2.9
Location: http://localhost/xampp/
Content-Length: 0
Content-Type: text/html
null
I'm not sure if this is causing your problem, but HTTP expects carriage return and line feed for a newline:
dOut.write("GET /index.php HTTP/1.0\r\n\r\n".getBytes());
Also, it wouldn't hurt to flush and close your DataOutputStream:
dOut.flush();
dOut.close();
If you plan on doing anything more with this code than just connecting to simple test cases, I'd recommending using HttpURLConnection for this instead of implenting HTTP in a socket yourself. Otherwise, the result will contain more than just the web page. It will also contain the HTTP response including status codes and headers. Your code would need to parse that.
Update:
Looking at the response you added, that 302 response along with the Location: header indicate that the page you are looking for moved to http://localhost/xampp/ (see HTTP 302) and there is no longer any content at the original URL. This is something that is can be set to be handled automatically by HttpURLConnection or another library like Apache HttpClient. You will need to parse the status code, parse the headers, open a new socket to the response Location and get the page. Depending upon the exact requirements of your assignment, you will probably want to familiarize yourself with the HTTP 1.0 Specification, and the HTTP 1.1 Specification as well.
I think the code is working, except maybe that you don't see the output because it's swamped by all the nulls you print. You should stop the while after the first null.
More generally, DataInputStream and DataOutputStream are not the right classes for this job. Try this code.
public static void main(String[] args) throws IOException {
Socket s = new Socket("127.0.0.1", 80);
BufferedReader dIn = new BufferedReader(new InputStreamReader(s.getInputStream()));
PrintStream dOut = new PrintStream(s.getOutputStream());
dOut.println("GET /index.php HTTP/1.0");
dOut.println();
String str = null;
do {
str = dIn.readLine();
System.out.println(str);
} while (str != null);
}
Why are you using socket directly to perform HTTP connection? This is fine exercise but it requires deep knowledge of internals of HTTP protocol. Why not just to use classes URL, and URLConnection?
BufferedReader dIn = new BufferedReader(new URL("http://127.0.0.1:80").openConnection().getInputStream());
do {
str = dIn.readLine();
System.out.println(str);
} while (str != null);
}