I'm coding web-server based on sockets. So I can get HTTP request headers:
import java.net.ServerSocket;
import java.net.Socket;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;
public class HttpServer {
public static void main(String[] args) throws Throwable {
//http://localhost:3000
ServerSocket ss = new ServerSocket(3000);
while (true) {
//Waiting for socket
Socket s = ss.accept();
System.out.println("Client accepted");
//The main process
new SocketProcessor(s,ss).start();
}
}
private static class SocketProcessor implements Runnable {
private Thread t;
private Socket s;
private InputStream is;
private OutputStream os;
private SocketProcessor(Socket s,ServerSocket ss) throws Throwable {
t = new Thread(this, "Server Thread");
this.s = s;
this.is = s.getInputStream();
this.os = s.getOutputStream();
}
public void run() {
try {
readInputHeaders();
writeResponse("<html><body><h1>Hello</h1></body></html>");
} catch (Throwable t) {
/*do nothing*/
} finally {
try {
s.close();
} catch (Throwable t) {
}
}
System.out.println("Client processing finished");
}
public void start()
{
t.start();
}
private void writeResponse(String s) throws Throwable {
String response = "HTTP/1.1 200 OK\r\n" +
"Server: Server\r\n" +
"Content-Type: text/html\r\n" +
"Content-Length: " + s.length() + "\r\n" +
"Connection: close\r\n\r\n";
String result = response + s;
os.write(result.getBytes());
os.flush();
}
private void readInputHeaders() throws Throwable {
BufferedReader br = new BufferedReader(new InputStreamReader(is));
while(true) {
String s = br.readLine();
System.out.println(s);
if(s == null || s.trim().length() == 0) {
break;
}
}
}
}
}
Input:
http://localhost:3000/?page=1
Output:
GET /?page=1 HTTP/1.1
Host: localhost:3000
Connection: keep-alive
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.154 Safari/537.36
Accept-Encoding: gzip,deflate,sdch
Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.6,en;q=0.4,bg;q=0.2
But now I need to get query string params:
page=1
I know that GET-request is not the best example, because I can get params currently from URI, but this will not work with POST.
So how can I get query string params from socket? I have no idea to try.
Of course I've found a solution. POST parameters land after a new line. So checking if line is empty doesn't help. We could check Content-Length and read request char by char.
Related
I tried to make a simple java class that runs a server on localhost and just makes a page with the word test as an HTML title and the word test in the body. For some reason, when I run it and try to connect, it refuses to connect. Here's my code:
package Server.core;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerListenerThread extends Thread{
private int port;
private ServerSocket serverSocket;
public ServerListenerThread(int port) throws IOException {
this.port = port;
this.serverSocket = new ServerSocket(port);
}
#Override
public void run() {
try {
ServerSocket serverSocket = new ServerSocket(port);
Socket socket = serverSocket.accept();
InputStream inputStream = socket.getInputStream();
OutputStream outputStream = socket.getOutputStream();
String html = "<html><head><title>Test</title></head><body><h1>test</h1></body></html>";
final String CRLF = "\r\n"; // 13, 10
String response = "HTTP/1.1 200 OK" + CRLF + "Content-Length: " + html.getBytes().length + CRLF + CRLF + html+ CRLF + CRLF;
outputStream.write(response.getBytes());
inputStream.close();
outputStream.close();
socket.close();
serverSocket.close();
} catch(Exception e) {
}
}
}
In my main method, I start a new ServerListenerThread on port 276.
you need to keep your thread alive. you can use while for that.
below is the basic modification i did. you can refactor more.
Also create ServerSocket once only.
public class ServerListenerThread extends Thread{
public static void main(String args[]) throws IOException {
ServerListenerThread s1= new ServerListenerThread(276);
s1.start();
}
private int port;
private ServerSocket serverSocket;
public ServerListenerThread(int port) throws IOException {
this.port = port;
this.serverSocket = new ServerSocket(port);
}
#Override
public void run() {
while (true) {
// spin forever
try {
// ServerSocket serverSocket = new ServerSocket(port);
Socket socket = serverSocket.accept();
InputStream inputStream = socket.getInputStream();
OutputStream outputStream = socket.getOutputStream();
String html = "<html><head><title>Test</title></head><body><h1>test</h1></body></html>";
final String CRLF = "\r\n"; // 13, 10
String response =
"HTTP/1.1 200 OK" + CRLF + "Content-Length: " + html.getBytes().length + CRLF + CRLF + html
+ CRLF + CRLF;
outputStream.write(response.getBytes());
inputStream.close();
outputStream.close();
socket.close();
serverSocket.close();
} catch (Exception e) {
}
}
}
}
I've got strange issue with my http communication. I wrote following very simple http server and client.
Http server:
public class SimpleHttpServer {
public void run() throws IOException {
final ServerSocket serverSocket = new ServerSocket(8080);
while(true)
{
final Socket connectionSocket = serverSocket.accept();
new Thread(new Runnable() {
#Override
public void run() {
try {
try(BufferedReader in = new BufferedReader(new InputStreamReader(connectionSocket.getInputStream()));
DataOutputStream out = new DataOutputStream(connectionSocket.getOutputStream())){
System.out.println(in.readLine());
String line = null;
while((line = in.readLine()) != null && !line.trim().isEmpty()){
System.out.println(line);
}
byte[] message = loadFile(); //load some gzipped jpg image from disk
out.write(ascii("HTTP/1.1 200 OK\n"));
out.write(ascii("Content-Type: image/jpg\n"));
out.write(ascii(String.format("Content-Length: %s\n", message.length)));
out.write(ascii("content-encoding: gzip\n"));
out.write(ascii("\n"));
out.write(message);
}
}catch(Exception e){
e.printStackTrace();
}
}
}).start();
}
}
}
Http client:
public class SimpleHttpClient {
public void run() throws IOException {
String host = "localhost";
int port = 8080;
Socket socket = new Socket(host, port);
try (OutputStream out = socket.getOutputStream()) {
out.write(ascii("GET / HTTP/1.1\n"));
out.write(ascii("User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36\n"));
out.write(ascii("Accept-Encoding: gzip, deflate, sdch\n"));
out.write(ascii("Accept-Language: pl-PL,pl;q=0.8,en-US;q=0.6,en;q=0.4\n"));
out.write(ascii("\n"));
try (BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()))) {
String line = null;
while ((line = in.readLine()) != null && !line.trim().isEmpty()) {
System.out.println(line);
}
}
}
}
}
So, when I remove line with user agent header, I get result as I expect:
HTTP/1.1 200 OK
Content-Type: image/jpg
Content-Length: 378632
content-encoding: gzip
And this is clear and obvious.
But when this header will appear, the answer is different:
HTTP/1.1 200 OK
Content-Type: image/jpg
Transfer-Encoding: chunked
I didn't change anything on server side. On every request I send the same response. The user agent was copied from chrome browser.
Both implementations are in plain java socket, not on third party libriares.
So my question is: how it happend? Something was cached? But where?
Please don't look at code quality. This is just an example. I don't want to use it on production :)
EDIT 1:
private byte[] ascii(String value) throws UnsupportedEncodingException {
return value.getBytes(StandardCharsets.US_ASCII);
}
EDIT 2:
I've done some research. It happens when user agent contains "AppleWebKit", protocol name is HTTP and port is 80 or 8080.
Moreover it happens on Windows 10 (I checked on two different computers). On Linux (Ubuntu 16.04 and Debian 8) everything is fine.
Maybe I should change the title of question? But how can I explain the problem in one short sentence?
I'm coding a little web server, which is returning Hello using 3000 port:
import java.net.ServerSocket;
import java.net.Socket;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;
public class HttpServer {
public static void main(String[] args) throws Throwable {
//http://localhost:3000
ServerSocket ss = new ServerSocket(3000);
while (true) {
//Waiting for socket
Socket s = ss.accept();
System.out.println("Client accepted");
//The main process
new SocketProcessor(s,ss).start();
}
}
private static class SocketProcessor implements Runnable {
private Thread t;
private Socket s;
private ServerSocket ss;
private InputStream is;
private OutputStream os;
private SocketProcessor(Socket s,ServerSocket ss) throws Throwable {
t = new Thread(this, "Server Thread");
this.ss=ss;
this.s = s;
this.is = s.getInputStream();
this.os = s.getOutputStream();
}
public void run() {
try {
readInputHeaders();
writeResponse("<html><body><h1>Hello</h1></body></html>");
} catch (Throwable t) {
/*do nothing*/
} finally {
try {
s.close();
} catch (Throwable t) {
}
}
System.out.println("Client processing finished");
}
public void start()
{
t.start();
}
private void writeResponse(String s) throws Throwable {
String response = "HTTP/1.1 200 OK\r\n" +
"Server: Server\r\n" +
"Content-Type: text/html\r\n" +
"Content-Length: " + s.length() + "\r\n" +
"Connection: close\r\n\r\n";
String result = response + s;
os.write(result.getBytes());
os.flush();
}
private void readInputHeaders() throws Throwable {
BufferedReader br = new BufferedReader(new InputStreamReader(is));
while(true) {
String s = br.readLine();
System.out.println(s);
if(s == null || s.trim().length() == 0) {
break;
}
}
}
}
}
I want to make my server wait for second before response. I was trying to use something like this:
readInputHeaders();
t.wait(1000);
writeResponse("<html><body><h1>Hello</h1></body></html>");
But my browser says ERR_EMPTY_RESPONSE
Call Thread.sleep(1000) in the run method of the SocketProcessor.
The method wait is not the right one to call for your purpose.
See also:
Thread.sleep JavaDoc
The methods wait, notify, notifyAll are defined on java.lang.Object
and are used for synchronization between threads. They are not generally
for the purpose of making a thread sleep for a certain amount of time.
I was wrote fowling code then set HTTP proxy on 127.0.0.1:9090 and try to reach google.com but it print following output and nothing happend.
Output:
Waiting for clients on port 9090
Got connection from /127.0.0.1:11827
Active Connections = 1
Waiting for clients on port 9090
connect started
connect finished
***Got connection from /74.125.232.131:80
writed
GET http://google.com/ HTTP/1.1
Host: google.com
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:19.0) Gecko/20100101 Firefox/19.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Cookie: ****
Connection: keep-alive
null
read line startes
Code:
import java.net.*;
import java.io.*;
import java.util.*;
public class SocketGoogler
{
public static void main(String[] args)
{
int port = 9090;
try {
ServerSocket server = new ServerSocket(port);
//System.out.println(google());
while(true) {
System.out.println("Waiting for clients on port " + port);
Socket client = server.accept();
ConnectionHandler handler = new ConnectionHandler(client);
handler.start();
}
} catch(Exception ex) {
System.out.println("Connection error: "+ex);
}
}
}
class ConnectionHandler extends Thread {
private Socket client;
BufferedReader reader;
PrintWriter writer;
static int count;
public ConnectionHandler(Socket client) {
this.client = client;
System.out.println("Got connection from "+client.getInetAddress()
+":"+client.getPort());
count++;
System.out.println("Active Connections = " + count);
}
public void run() {
String message;
String totalMessage;
try {
reader = new BufferedReader(new
InputStreamReader(client.getInputStream()));
writer = new PrintWriter(client.getOutputStream());
writer.flush();
message = reader.readLine();
totalMessage=message+"\n";
while (message != null) {
message = reader.readLine();
totalMessage+=message+"\n";
}
client.close();
//System.out.println(totalMessage);
google(totalMessage);
count--;
System.out.println("Active Connections = " + count);
} catch (Exception ex) {
count--;
System.out.println("Active Connections = " + count);
}
}
public String google(String w) throws IOException
{
String message , totalMessage;
int port = 80;
//ServerSocket server = new ServerSocket(port,10,InetAddress.getByName("google.com"));
//Socket googler=server.accept();
Socket googler=new Socket();
InetSocketAddress endpoint=new InetSocketAddress(InetAddress.getByName("google.com").getHostAddress(), port);
System.out.println("connect started");
googler.connect(endpoint);
System.out.println("connect finished");
System.out.println("***Got connection from "+googler.getInetAddress()+":"+googler.getPort());
BufferedReader reader = new BufferedReader(new
InputStreamReader(googler.getInputStream()));
PrintWriter writer=new PrintWriter(googler.getOutputStream());
//String w="GET http://google.com/ HTTP/1.1\nHost: google.com\nUser-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:19.0) Gecko/20100101 Firefox/19.0\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\nAccept-Language: en-US,en;q=0.5\nAccept-Encoding: gzip, deflate\nConnection: keep-alive"+null;
writer.print(w);
System.out.println("writed\n"+w);
System.out.println("read line startes");
message = reader.readLine();
System.out.println("read line finished");
totalMessage=message+"\n";
while (message != null) {
message = reader.readLine();
System.out.println("*");
totalMessage+=message+"\n";
}
googler.close();
System.out.println("close");
return totalMessage;
}
}
Why this problems happens?I already connected too google.com and send request but nothing respond from this host.
Use writer.flush(); It will send your request.
:)
This is my code on the client side:
import java.io.*;
import java.net.*;
public class httpClient {
public void TcpSocket()
{
String sentence;
String modifiedSentence;
StringBuffer contents= null;
//open a socket connection on port 80
try{
Socket clientSocket = new Socket("localhost", 8080);
//send message to the server
DataOutputStream outToServer = new DataOutputStream(clientSocket.getOutputStream());
//read message from the server
BufferedReader inFromServer = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
//read http request message from a file
File file = new File("/home/x/Desktop/test.txt");
contents = new StringBuffer();
BufferedReader reader = null;
reader = new BufferedReader(new FileReader(file));
String text = null;
// repeat until all lines is read
while ((text = reader.readLine()) != null)
{
contents.append(text).append(System.getProperty("line.separator"));
}
//end reading file
//Send message
sentence = contents.toString();
outToServer.writeBytes(sentence + '\n');
modifiedSentence = inFromServer.readLine();
System.out.println("FROM SERVER: " + modifiedSentence);
clientSocket.close();
}
catch (UnknownHostException e) {
System.err.println("Don't know about host");
System.exit(1);
} catch (IOException e) {
System.err.println("Couldn't get I/O for the connection");
System.exit(1);
}
}
public static void main(String args[])
{
httpClient cl = new httpClient();
cl.TcpSocket();
}
}
and my http server:
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Executors;
import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
public class HttpServerDemo {
public static void main(String[] args) throws IOException {
InetSocketAddress addr = new InetSocketAddress(8080);
HttpServer server = HttpServer.create(addr, 0);
server.createContext("/", new MyHandler());
server.setExecutor(Executors.newCachedThreadPool());
server.start();
System.out.println("Server is listening on port 8080" );
}
}
class MyHandler implements HttpHandler {
public void handle(HttpExchange exchange) throws IOException {
System.out.println("I am in Server request Handler"); <---------------Request is not coming here
String requestMethod = exchange.getRequestMethod();
if (requestMethod.equalsIgnoreCase("GET")) {
Headers responseHeaders = exchange.getResponseHeaders();
responseHeaders.set("Content-Type", "text/plain");
exchange.sendResponseHeaders(200, 0);
OutputStream responseBody = exchange.getResponseBody();
Headers requestHeaders = exchange.getRequestHeaders();
Set<String> keySet = requestHeaders.keySet();
Iterator<String> iter = keySet.iterator();
while (iter.hasNext()) {
String key = iter.next();
List values = requestHeaders.get(key);
String s = key + " = " + values.toString() + "\n";
responseBody.write(s.getBytes());
}
responseBody.close();
}
}
}
Request I am sending:
GET /index.html HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.3) Gecko/20091020 Ubuntu/9.10 (karmic) Firefox/3.5.3
Accept: /
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
But my request is not coming in to HttpServerDemo.java's request handler.
Update
I am not able to debug my code since it's not hitting the request handler on the server and it works fine in a real browser. This is the response from the real browser when I open http://localhost:8080
Host = [localhost:8080]
Accept-charset = [ISO-8859-1,utf-8;q=0.7,*;q=0.7]
Accept-encoding = [gzip,deflate]
Connection = [keep-alive]
Keep-alive = [300]
Accept-language = [en-us,en;q=0.5]
User-agent = [Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.3) Gecko/20091020 Ubuntu/9.10 (karmic) Firefox/3.5.3]
Accept = [text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8]
You forgot a blank newline between request headers and request body. The server now thinks that there are more headers to come and is waiting with handling the response. You must always insert a blank newline (CRLF) after the request headers. See also Wikipedia: HTTP.
Why use a Socket for HTTP at all? All this stuff is already solved for you with URL.openConnection().