Java Proxy is not sending anything back to browser - java

I have created a proxy server in Java, (See code below), the thing is, I'm getting response back from the web-server however my client-side of the proxy which handles the connections between clients(browser) and the web-server after a socket has been created with the server side of the proxy. The server side creates a client and sends the request and socket and this is then handled in a new thread.
I have a few questions:
First Code is of the Client-side of the proxy second code part is of the Server-side of the proxy
What type of streams should I use when sending data between browser/proxy/webserver?
Is it fine to use a String or should I use some type of byte array when sending and receiving from streams?
Why is the browser not receiving anything from the proxy? Since I can print it out from the console but when writing to the stream nothing happens in the browser.
Also, why do I need to click "enter" twice in the browser for the proxy to react?
public class Client implements Runnable {
private String request;
private String response;
private Socket browserSocket;
public Client(String request, Socket browserSocket) {
this.request = request;
this.response = "";
this.browserSocket = browserSocket;
}
#Override
public void run() {
/* Send request to web server and get the response. */
this.request = Client.modifyHttpHeader("Connection", "close", this.request);
String hostName = Client.getHttpHeader("Host", this.request);
if (!hostName.isEmpty()) {
try {
/* Send request to the web-server. */
Socket socket = new Socket(hostName, 80);
OutputStreamWriter osw = new OutputStreamWriter(socket.getOutputStream());
PrintWriter pw = new PrintWriter(osw);
pw.write(this.request);
pw.flush();
System.out.println("---S:REQUEST---");
System.out.println(this.request);
System.out.println("---S:REQUEST---");
/* Receive the response from the web-server. */
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String response = "";
int tmpData;
while ((tmpData = br.read()) != -1) {
response += (char)tmpData;
}
this.response = response;
socket.close(); /* Close the socket between client-side and web-server. */
/* Send the response back to the browser. */
OutputStreamWriter oswbrowser = new OutputStreamWriter(this.browserSocket.getOutputStream());
PrintWriter pwBrowser = new PrintWriter(oswbrowser);
pwBrowser.write(this.response);
pwBrowser.flush();
pwBrowser.close();
this.browserSocket.close(); /* Close the socket between client-side and browser. */
System.out.println("---C:RESPONSE---");
System.out.println(this.response);
System.out.println("---C:RESPONSE---");
} catch (Exception e) {
System.err.println(e.getMessage());
e.printStackTrace();
}
}
}
public String getHttpResponse() {
return this.response;
}
/**
*
* #param header
* The name of the HTTP header. Example: "GET".
* Note: Header name is case sensitive.
* #param request
* The HTTP message.
* #return
* On success: The value following the HTTP header ": " (colon and whitespace).
* On failure: Empty string.
*/
public static String getHttpHeader(String header, String request) {
int startHeaderIndex = request.indexOf(header) + header.length();
int endHeaderIndex = request.indexOf('\n', startHeaderIndex);
/* Could not find the searched header. */
if (startHeaderIndex == -1 || endHeaderIndex == -1)
return "";
/* Add 2 to remove ':' and ' '(white space). Decrement 1 to exclude '\r' and '\n' */
return request.substring(startHeaderIndex + 2, endHeaderIndex - 1);
}
/**
*
* #param header
* The name of the HTTP header. Example: "Connection"
* Note: The header is case sensitive.
* #param value
* The new value you want to put. Example: "Close"
* #param request
* The HTTP message.
* #return
* On success: A new HTTP request with the modified header value.
* On failure: Empty string.
*
*/
public static String modifyHttpHeader(String header, String value, String request) {
int startHeaderIndex = request.indexOf(header) + header.length();
int endHeaderIndex = request.indexOf('\n', startHeaderIndex);
/* Could not find the searched header. */
if (startHeaderIndex == -1 || endHeaderIndex == -1)
return "";
String newRequest = "";
/* Copy all characters including ':' and ' ' (whitespace) */
for (int i = 0; i < startHeaderIndex + 2; i++) {
newRequest += request.charAt(i);
}
newRequest += value;
newRequest += "\r\n";
/* Add the rest of the request. */
for (int i = endHeaderIndex + 1; i < request.length(); i++) {
newRequest += request.charAt(i);
}
return newRequest;
}
}
public class Server {
public static void main(String[] args) throws Exception{
/* Receiving and parsing port number from command line arguments. */
int ssPort = 0;
if (args.length > 1 || args.length == 0){
System.err.println("Only one argument allowed; port number (int).");
System.exit(1);
} else {
try {
ssPort = Integer.parseInt(args[0]);
} catch(NumberFormatException exception) {
System.err.println("Argument \"" + args[0] + "\" must be a number.");
System.exit(1);
}
}
ServerSocket serverSocket = new ServerSocket(ssPort); /* Creating the server socket. */
System.out.println("Proxy running on port: " + ssPort);
while(true) {
System.out.println("Waiting for client...");
Socket clientSocket = serverSocket.accept(); /* Listening for connections. */
BufferedReader bReader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
String request = "";
int tmpData;
while ((tmpData = bReader.read()) != -1) {
request += (char)tmpData;
}
Client clientObject = new Client(request, clientSocket);
Thread clientThread = new Thread(clientObject);
clientThread.start(); /* Start the client thread. */
}
}
}

Throw it all away. This isn't how proxies are written. An HTTP proxy should:
Read one line from the downstream client, to determine whom to connect to upstream, without buffering.
Connect upstream.
If (2) fails, send an appropriate HTTP response downstream and close the connection.
Otherwise, start copying bytes, in both directions, simultaneously.
You should not attempt to assemble the entire request, or even the headers, and the words 'bytes' and 'simultaneously' above are critical. Note that you don't have to do anything about HTTP keep-alive, Connection headers, HTTPS, etc.

Related

Http client receives no response when server reads only headers from request

I'm messing around with HTTP and sockets in Java and was hoping you could shed some light on this:
When my HTTP server written in Java SE 11 does not read the entire request and then responds, the client does not get it or gets an error. Why is that? Is the client unable to read the response before the server has read the entire request? If the call to readBody is executed in the snippet below, this works fine. It also works fine if the response has the Content-Length header and a text body. That is actually more puzzling to me.
My example request is a POST with the data fds. Postman says "Could not get any request" and curl says "curl: (56) Recv failure: Connection reset by peer".
import java.io.*;
import java.net.Socket;
import java.util.*;
class Handler {
public synchronized void read(Socket incoming) {
try (incoming;
OutputStream outputStream = incoming.getOutputStream();
InputStream inputStream = incoming.getInputStream();
PrintWriter pw = new PrintWriter(outputStream)) {
writeRequest(inputStream);
pw.print("HTTP/1.1 200 OK\r\n");
pw.print("\r\n");
pw.flush();
} catch (IOException e) {
System.out.println("ERROR: " + e.getMessage());
e.printStackTrace();
}
}
private void writeRequest(InputStream inputStream) throws IOException {
String verbLine = readLine(inputStream);
Map<String, String> headers = readHeaders(inputStream);
//readBody(inputStream, headers);
}
private void readBody(InputStream inputStream, Map<String, String> headers) throws IOException {
Optional<String> optKey = headers.keySet().stream()
.filter(k -> k.equalsIgnoreCase("Content-Length"))
.findFirst();
if (optKey.isPresent()) {
int contentLength = Integer.parseInt(headers.get(optKey.get()));
byte[] bytes = inputStream.readNBytes(contentLength);
}
}
private Map<String, String> readHeaders(InputStream inputStream) throws IOException {
Map<String, String> headers = new HashMap<>();
while (true) {
String line = readLine(inputStream);
if (line == null || line.isEmpty()) {
return headers;
}
String key = line.split(":")[0].trim();
String value = line.split(":")[1].trim();
headers.put(key, value);
}
}
private String readLine(InputStream inputStream) throws IOException {
byte[] buf = new byte[200];
int offset = 0;
while (true) {
int read = inputStream.read();
if (read == -1) {
return null;
}
buf[offset] = (byte) read;
if (buf[0] == '\n' || (buf[0] == '\r' && buf[1] == '\n')) {
return "";
}
if (buf[offset] == 0x0A) {
int endOfLine = buf[offset - 1] == 0x0D ? offset - 1 : offset;
return new String(buf, 0, endOfLine);
} else {
offset++;
}
}
}
}
If you close a socket at the server while there are still unread data it will result in a connection reset error at the client. This happens here since you don't read the full request. This error will be exposed to the user if the full response from the server was not read yet.
If you send the response with a content-length and then the full body then the client will have read the full response and thus the error will be ignored. If instead you send neither content-length nor use chunked encoding the client will expect the response to end with a proper close of the TCP connection. In this case the connection reset will be propagated to the user since the full response from the server was not (properly) read yet.
Your response needs to have either a Content-Length header or a Transfer-Encoding header - which tells the client how the response will be transmitted and allows it to figure out when all the bytes have been received. Without that it will need to wait for EOF and assume that the response body is terminated by EOF (this for compatibility with HTTP/1.0). It is possible that your client doesn't support that.
It might help to know which client you are using.

Read and write on single socket in java

I have a socket server which accepts only one client socket connection. It will remember the connection and send response on the same. If client opens one more connection to the server with same host and port, then server will accept it but send response on last remembered socket connection. I don't know how server is able to do it as it is not in my control.
Below is code i tried. In my code, I am first creating Socket connection with server and storing it in Spring application context. For every request, I am getting it and using Input and Output Stream.
I am able to send one request and receive one response. But when i send second request, I do not get any response from server.
Please make a note that, server sends message length in first two bytes and appends the rest of the response.
For second response, I get header as 0000 and response as null.
Can somebody point me to example where client is creating only one socket with server and use its input and output stream for every request. Below is code i tried.
SocketConnector.java
public class SocketConnector {
LoggerUtil log = LoggerUtil.getInstance();
final String className = "SocketConnector";
#Value("${socket.host}")
String socketHost;
#Value("${socket.port}")
int socketPort;
#Value("${socket.connection.timeout}")
int socketConnectionTimeout;
#Value("${socket.read.timeout}")
int socketReadTimeout;
#Bean
Socket socketSocket(){
Socket s = new Socket();
final String methodName = "socketSocket";
try {
s.connect(new InetSocketAddress(socketHost, socketPort), socketConnectionTimeout); // Connection timeout set to 5 seconds with socket.
s.setSoTimeout(socketReadTimeout); // Read timeout set to 2 seconds. This means socket should send response in maximum of 2 second for every request.
log.doLog(3, className, methodName, "Created Socket Connection");
} catch (IOException e) {
log.doLog(3, className, methodName, LoggerUtil.getExStackTrace(e));
}
return s;
};
}
LogonScheduler.java
#Configuration
#EnableScheduling
public class LogonScheduler {
#Autowired
#Qualifier("socketSocket")
private Socket socketSocket;
private DataInputStream socketInputStream;
private OutputStream socketOutputStream;
static LoggerUtil log = LoggerUtil.getInstance();
final static String className = "LogonScheduler";
String logonResponse = null;
byte[] reqMsg = null;
byte[] msgLen = null;
byte[] header = null;
byte[] buf = null;
byte[] response = null;
int messageLen = -1;
/*#Scheduled(fixedDelay = 17500)*/
#Scheduled(fixedDelay = 10000)
public void logonSender(){
final String methodName = "logonSender";
try {
socketInputStream = new DataInputStream(socketSocket.getInputStream());
socketOutputStream = socketSocket.getOutputStream();
/*
* Created a byte array named buf and writing to socketOutputStream
* buf = createRequest();
*/
socketOutputStream.write(buf);
header = new byte[2];
socketInputStream.read(header, 0, 2);
messageLen = Integer.parseInt(bytetoHex(header), 16);
log.doLog(4, className, methodName, "Message length received :" + messageLen);
logonResponse = bytetoHex(header);
response = new byte[messageLen];
socketInputStream.readFully(response, 0, messageLen);
logonResponse += bytetoHex(response);
log.doLog(4, className, methodName, "Response Message with header : " + logonResponse);
logonResponse = logonResponse.substring(4);
log.doLog(4, className, methodName, "Response Message without header : " + logonResponse);
} catch (Exception e) {
e.printStackTrace();
}finally{
Utils.closeResources(logonResponse, reqMsg, msgLen, header, buf, response, messageLen);
}
}
public String bytetoHex(byte[] b) {
if (isEmpty(b)) {
return null;
}
String hex = "";
for (int i = 0; i < b.length; i++) {
hex += byteToHex(b[i]);
}
return hex;
}
// Returns hex String representation of byte b
public String byteToHex(byte b) {
char[] array = {hexDigit[(b >> 4) & 0x0f], hexDigit[b & 0x0f]};
return new String(array);
}
}

Unexpected character ('%' (code 37)): expected a valid value (number, String, array, object, 'true', 'false' or 'null')

For several day I trying to figure out what this error means.
I getting the error when i try to download file from web service.
The full error is:
java.lang.RuntimeException: org.codehaus.jackson.JsonParseException: Unexpected character ('%' (code 37)): expected a valid value (number, String, array, object, 'true', 'false' or 'null')
at [Source: java.io.StringReader#39494c1; line: 1, column: 2]".
The class that do the job of Serialization and Deserialization from web service:
public class WSClass{
public String authenticationToken;
public enum HTTPMethod {
GET, PUT, POST, DELETE
}
// Constructor.
public WSClass() {
}
/**
* Calls a REST endpoint in the specified URL and returns
* the return value. Optionally deserializes it from JSON.
*
* #param <T> The type of the return value
* #param //strUrl URL relative to the applet root
* #param method HTTP Method used in the request
* #param body Body serialized for the JSON
* #param output The type of the return value
* #return The REST response as an instance of type T
*/
public <T> T doMethod(String strUrl, HTTPMethod method, Object body,
Class<T> output) throws IOException {
return doMethod(strUrl, method, body, output, null);
}
/**
* Calls a REST endpoint in the specified URL and returns
* the return value. Optionally deserializes it from JSON.
*
* #param <T> The type of the return value
* #param //strUrl URL relative to the applet root
* #param method HTTP Method used in the request
* #param body Body serialized for the JSON
* #param output The type of the return value
* #param headers Key-Value list of additional headers.
* #return The REST response as an instance of type T
*/
#SuppressWarnings("unchecked")
public <T> T doMethod(String strUrl, HTTPMethod method, Object body,
Class<T> output, Map<String, String> headers) throws IOException {
// Strip the first '/' away if it exists.
if (strUrl.startsWith("/")) {
strUrl = strUrl.substring(1);
}
// Calculate the real url based on method. IIS supports only the
// GET and POST in default mode so we'll use the _method parameter
// that MFWS understands.
if (method != HTTPMethod.GET && method != HTTPMethod.POST) {
String methodParam;
if (strUrl.contains("?")) {
methodParam = "&_method=";
} else {
methodParam = "?_method=";
}
strUrl += methodParam + method.name();
method = HTTPMethod.POST;
}
// Initialize JSON (de)serializer.
ObjectMapper om = new ObjectMapper();
om.configure(org.codehaus.jackson.map.SerializationConfig.Feature.CAN_OVERRIDE_ACCESS_MODIFIERS, false);
om.configure(org.codehaus.jackson.map.DeserializationConfig.Feature.CAN_OVERRIDE_ACCESS_MODIFIERS, false);
om.configure(org.codehaus.jackson.map.DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
// Get URL to REST interface.
URL u = new URL(strUrl);
// Perform the request.
HttpURLConnection conn = null;
OutputStream os = null;
InputStream is = null;
BufferedReader in = null;
try {
// Open connection.
conn = (HttpURLConnection) u.openConnection();
// Prevent the use of cache.
// The applet does not seem to respect the cache control flags it receives from the server.
// For example it won't necessarily make a new request to the server even if the requested
// resources has expired. See issue: #9234.
conn.setUseCaches(false);
// Set the request properties.
conn.setRequestMethod(method.name());
if (body != null)
conn.setDoOutput(true);
if (!output.equals(void.class))
conn.setDoInput(true);
conn.setRequestProperty("Accept", "application/json");
conn.setRequestProperty("X-Authentication", authenticationToken);
if (headers != null) {
for (Map.Entry<String, String> header : headers.entrySet()) {
System.out.println("Setting header " + header.getKey());
conn.setRequestProperty(header.getKey(), header.getValue());
}
}
// If there is a body, serialize it to the output stream.
if (body != null) {
os = conn.getOutputStream();
om.writeValue(os, body);
} else if (method != HTTPMethod.GET) {
// No body available.
conn.setRequestProperty("Content-Length", "0");
}
// Check if the caller wanted the connection as the return value.
if (output.equals(HttpURLConnection.class)) {
// Change ownership so we don't disconnect the connection in
// finalize block.
HttpURLConnection connDetached = conn;
conn = null;
return (T) connDetached;
}
// Write the output if we had output.
if (os != null) {
os.flush();
os.close();
}
os = null;
// Get response to input stream.
conn.connect();
is = conn.getInputStream();
int contentLength = conn.getContentLength();
if (output.equals(InputStream.class)) {
// If the output type is input stream, just return it
// as it is.
InputStream isReturn = is;
is = null; // Change ownership.
return (T) isReturn;
}
else {
// Deserialize from JSON object.
String response = readStringFromStream(is, contentLength);
// Read the return value from the response.
if (output.equals(void.class) || response.length() == 0)
return null;
else
return om.readValue(response, output);
} // end-if (output.equals(InputStream.class))
} catch (IOException e3) {
throw new RuntimeException(e3);
} finally {
// Close streams.
closeStream(os);
closeStream(is);
closeStream(in);
if (conn != null)
conn.disconnect();
}
}
/**
* Reads an UTF-8 encoded string from the specified stream.
*
* #param is
* #param totalLengthInBytes
* #return
* #throws IOException
*/
private String readStringFromStream(InputStream is, int totalLengthInBytes) throws IOException {
// Return empty string if the requested number of bytes is zero.
if (totalLengthInBytes == 0)
return "";
// It seems that Opera 10 may pass -1 as the total length if the actual Content-Length header
// indicates zero body length.
// Because -1 indicates unspecified content length we attempt to read as much as possible in this case.
if (totalLengthInBytes == -1)
totalLengthInBytes = Integer.MAX_VALUE;
// Read the data from the stream as bytes and pipe it through piped stream
// that converts the byte stream to UTF-8 char stream.
PipedOutputStream poutput = null;
PipedInputStream pinput = null;
StringBuilder result = new StringBuilder();
try {
// Start reading the stream.
boolean continueRead = true;
poutput = new PipedOutputStream();
pinput = new PipedInputStream(poutput);
InputStreamReader r = new InputStreamReader(pinput, "UTF-8");
int bytesReadTotal = 0;
int byteBufferSize = 500; // Buffer size used in the conversion.
CharBuffer cb = CharBuffer.allocate(byteBufferSize);
byte[] buffer = new byte[byteBufferSize];
while (continueRead) {
// Read correct number of bytes from the input stream and write the to the output buffer.
int readByteCount = Math.min(buffer.length, totalLengthInBytes - bytesReadTotal);
int bytesRead = is.read(buffer, 0, readByteCount);
// Convert the bytes to a string.
if (bytesRead > 0) {
// Write to the piped stream.
poutput.write(buffer, 0, bytesRead);
// Read the bytes as string.
cb.clear();
r.read(cb);
int charactersRead = cb.position();
// Collect the string read to the buffer.
cb.rewind();
String currentBatch = cb.subSequence(0, charactersRead).toString();
result.append(currentBatch);
} // end if
// Stop reading if EOF was encountered.
if (bytesRead == -1)
continueRead = false;
else
bytesReadTotal += bytesRead;
// Stop reading the stream after we have read all the available bytes.
if (bytesReadTotal == totalLengthInBytes)
continueRead = false;
} // end while
} finally {
// Close the middleware streams.
closeStream(poutput);
closeStream(pinput);
}
// Return the result.
return result.toString();
}
/**
* Closes the specified stream
*
* #param stream
*/
private static void closeStream(Closeable stream) {
// Try closing only if the stream was specified.
if (stream != null) {
try {
stream.close();
} catch (IOException e) {
// Ignore error.
e.printStackTrace();
}
}
}
}
The line the I used to download the file is:
try {
FileOutputStream outputStream = new FileOutputStream(file);
outputStream = WSClass.doMethod(Url, WSClass.HTTPMethod.GET, null,
FileOutputStream.class);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}finally {
if (outputStream != null) {
outputStream.flush();
outputStream.close();
}
}
Someone knows what the problem?
UPDATE: I success to download the file.
I change the function doMethod with #Ganesh Karewad suggested and added another way to write to fileoutputstream.
public <T> T doMethod(String strUrl, HTTPMethod method, Object body,
Class<T> output, File file) throws IOException {
return doMethod(strUrl, method, body, output, null, file);
}
/**
* Calls a REST endpoint in the specified URL and returns
* the return value. Optionally deserializes it from JSON.
*
* #param <T> The type of the return value
* #param //strUrl URL relative to the applet root
* #param method HTTP Method used in the request
* #param body Body serialized for the JSON
* #param output The type of the return value
* #param headers Key-Value list of additional headers.
* #return The REST response as an instance of type T
*/
#SuppressWarnings("unchecked")
public <T> T doMethod(String strUrl, HTTPMethod method, Object body,
Class<T> output, Map<String, String> headers, File file) throws IOException {
// Strip the first '/' away if it exists.
if (strUrl.startsWith("/")) {
strUrl = strUrl.substring(1);
}
// Calculate the real url based on method. IIS supports only the
// GET and POST in default mode so we'll use the _method parameter
// that MFWS understands.
if (method != HTTPMethod.GET && method != HTTPMethod.POST) {
String methodParam;
if (strUrl.contains("?")) {
methodParam = "&_method=";
} else {
methodParam = "?_method=";
}
strUrl += methodParam + method.name();
method = HTTPMethod.POST;
}
// Initialize JSON (de)serializer.
ObjectMapper om = new ObjectMapper();
om.configure(org.codehaus.jackson.map.SerializationConfig.Feature.CAN_OVERRIDE_ACCESS_MODIFIERS, false);
om.configure(org.codehaus.jackson.map.DeserializationConfig.Feature.CAN_OVERRIDE_ACCESS_MODIFIERS, false);
om.configure(org.codehaus.jackson.map.DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
// Get URL to REST interface.
URL u = new URL(strUrl);
// Perform the request.
HttpURLConnection conn = null;
OutputStream os = null;
InputStream is = null;
BufferedReader in = null;
try {
// Open connection.
conn = (HttpURLConnection) u.openConnection();
// Prevent the use of cache.
// The applet does not seem to respect the cache control flags it receives from the server.
// For example it won't necessarily make a new request to the server even if the requested
// resources has expired. See issue: #9234.
conn.setUseCaches(false);
// Set the request properties.
conn.setRequestMethod(method.name());
if (body != null)
conn.setDoOutput(true);
if (!output.equals(void.class))
conn.setDoInput(true);
if(file != null)
{
conn.setRequestProperty("Accept", "application/octet-stream");
}
else
{
conn.setRequestProperty("Accept", "application/json");
}
conn.setRequestProperty("X-Authentication", authenticationToken);
if (headers != null) {
for (Map.Entry<String, String> header : headers.entrySet()) {
System.out.println("Setting header " + header.getKey());
conn.setRequestProperty(header.getKey(), header.getValue());
}
}
// If there is a body, serialize it to the output stream.
if (body != null) {
os = conn.getOutputStream();
om.writeValue(os, body);
} else if (method != HTTPMethod.GET) {
// No body available.
conn.setRequestProperty("Content-Length", "0");
}
// Check if the caller wanted the connection as the return value.
if (output.equals(HttpURLConnection.class)) {
// Change ownership so we don't disconnect the connection in
// finalize block.
HttpURLConnection connDetached = conn;
conn = null;
return (T) connDetached;
}
// Write the output if we had output.
if (os != null) {
os.flush();
os.close();
}
os = null;
// Get response to input stream.
conn.connect();
is = conn.getInputStream();
String response = null;
int contentLength = conn.getContentLength();
if (output.equals(InputStream.class)) {
// If the output type is input stream, just return it
// as it is.
InputStream isReturn = is;
is = null; // Change ownership.
return (T) isReturn;
}
else {
if(file != null)
{
FileOutputStream fileOut = new FileOutputStream(file);
IOUtils.copy(is, fileOut);
return om.readValue("true", output);
}
else
{
// Deserialize from JSON object.
response = readStringFromStream(is, contentLength);
}
// Read the return value from the response.
if (output.equals(void.class) || response.length() == 0)
return null;
else
return om.readValue(response, output);
} // end-if (output.equals(InputStream.class))
} catch (IOException e3) {
throw new RuntimeException(e3);
} finally {
// Close streams.
closeStream(os);
closeStream(is);
closeStream(in);
if (conn != null)
conn.disconnect();
}
}
If i need file:
I set to connection as "conn.setRequestProperty("Accept", "application/octet-stream");" and read the input to fileoutputstream.
Thanks,
Tal
you are trying to download file as a json by setting value as
conn.setRequestProperty("Accept", "application/json");
so java is trying to read response as a json and thats why its throwing error
now if you dont' know what type of file you will get in response use
conn.setRequestProperty("Accept", "application/octet-stream");
if you know file like its a png file then use
conn.setRequestProperty("Accept", "image/png");

Generating Http 301 Response Message

I'm trying to create a little web server and have be able to generate Http response messages 200, 301, and 404.
I am able to get 200 and 404 to work, but I am having problems with 301.
When I try to access a page that has "permanently moved" my browser doesn't get redirected and I get a java.lang.NullPointerException from java.
The way I have it determine if the code should be a 301 is it checks a list of strings for the file the client is trying to access, and if the original file they're trying to access has been moved, it will be in the list, along with it's new name/location. So if the original file is "index5.html" and it's been moved to "index.html" then they will be in an array and "index5.html" will be in an index 1 before "index.html"
I'm also just testing this on my own machine so I'm using localhost for the URL and using port 9012.
Here is my code:
import java.io.*;
import java.net.*;
import java.util.*;
public final class HttpRequest implements Runnable {
final static String CarrLine = "\r\n";
Socket clientSocket;
// A list of files that have been moved.
// Even indexes (0, 2, 4, ...) are the original file names.
// Odd indexes (1, 3, 5, ...) are where the files of previous indexes moved to.
static String movedFiles[] = {"index5.html", "index.html", "page.html", "homepage.html"};
// This sets the Httprequest object socket equal to
// the socket the client comes in through
public HttpRequest(Socket socket) throws Exception {
this.clientSocket = socket;
}
// Here we define a new method that overwrites the
// previous method in the Runnables class. This is done
// so that when an Http request is attempted, and
// something goes wrong, our whole web server will
// not fail and crash.
#Override
public void run(){
try {
// This is where the method to actually start the Http request starts.
requestProcessing();
} catch (Exception ex) { System.out.print(ex); }
}
// This is our main processing method to take in out Http request
// and spit out a reponse header along with the requested data,
// if there is any.
void requestProcessing() throws Exception {
Boolean fileExists = false;
String CarrLine = "\r\n";
String statusCode = null;
String responseHeader = "HTTP/1.1 ";
String fileName, line = null;
String clientSentence = null;
ArrayList<String> records = new ArrayList<String>();
FileInputStream requestedFileStream = null;
File requestedFile;
// Starts input from client and establishes filters
BufferedReader inFromClient = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
// Starts output stream for output to client through socket
DataOutputStream outToClient = new DataOutputStream(clientSocket.getOutputStream());
/*
// Reads in GET from client BufferedReader
while ( (line = inFromClient.readLine()) != null){
records.add(line);
break;
}*/
clientSentence = inFromClient.readLine();
// Parses and stores file name the client wants in a string
fileName = parseGET(clientSentence);
if (!existingFile(fileName)){
// Here is where the 301 response message is generated and
// retrieve the correct filename.
if (hasMoved(fileName) != -1){
statusCode = "301";
responseHeader = responseHeader + statusCode + " Moved Permanently\n";
responseHeader = responseHeader + "Location: localhost:9012/"
+ movedFiles[hasMoved(fileName)] + CarrLine;
}
// This generates the response header for the client
// if the file the client is looking for is not there (404).
else {
statusCode = "404";
responseHeader = responseHeader + statusCode + " Not Found: \n";
responseHeader = responseHeader + "Content-Type: text/html" + CarrLine;
}
}
// This generates the 200 status code response header
// to send to the client saying the file was found.
if (existingFile(fileName)) {
statusCode = "200";
responseHeader = responseHeader + statusCode + " OK: \n";
responseHeader = responseHeader + "Content-Type: " + fileType(fileName) + CarrLine;
requestedFileStream = openFileStream(fileName);
}
// Outputs the response message to the client through a data stream
outToClient.writeBytes(responseHeader);
outToClient.writeBytes(CarrLine);
// If the file the client is requesting exists,
// begin writing file out to client.
if (existingFile(fileName)){
fileWriteOut(requestedFileStream, outToClient);
requestedFileStream.close();
}
else if(hasMoved(fileName) != -1){
outToClient.writeBytes("File Moved");
}
// If the file the client is requesting does not exist,
// return a 404 message.
else {
outToClient.writeBytes("404: File not found!");
}
// Closes all open streams and sockets to the client.
inFromClient.close();
outToClient.close();
clientSocket.close();
}
// This parses the GET line from the client to get the filename the client is requesting
String parseGET(String clientString){
String temp[] = clientString.split(" /");
temp = temp[1].split(" ");
return temp[0];
}
// This is used to find the file the client is requesting.
// It will return null if no file was found/opened.
FileInputStream openFileStream(String file){
FileInputStream fileStream = null;
// Opening the file stream is in a try catch statment so that
// incase there was no file, the program doesn't crash
// and it'll alert the user on the console.
try {
fileStream = new FileInputStream(file);
} catch (FileNotFoundException e) {
System.out.println(e);
return null;
}
return fileStream;
}
// Determines the file type that is being sent to the client
// and returns the appropriate string
String fileType(String clientRequestFile){
// If the file ends in .html or .htm, it will return "text/html"
// so that it can be added to the response message.
if (clientRequestFile.endsWith(".html") || clientRequestFile.endsWith(".htm")){
return "text/html";
}
// If the file ends in .jpg, it will return "text/jpeg"
// so that it can be added to the response message.
if (clientRequestFile.endsWith(".jpg")){
return "text/jpg";
}
// If the file ends in .css, it will return "text/css"
// so that it can be added to the response message.
if (clientRequestFile.endsWith(".css")){
return "text/css";
}
// Returns this by default, if none of the above.
return "application/octet-stream";
}
// This creates a 2k buffer and writes out
// requested filed to the client.
static void fileWriteOut(FileInputStream clientStream, OutputStream toClient) throws Exception{
byte[] buffer = new byte[2048];
int bytes = 0;
while ((bytes = clientStream.read(buffer)) != -1){
toClient.write(buffer, 0, bytes);
}
}
// This determines whether or not a file that
// the client has requested exists or not.
// Returns a Boolean value.
static Boolean existingFile(String fileName){
File file = new File(fileName);
if (file.exists() && !file.isDirectory()){
return true;
}
return false;
}
// Determines if a file has been moved and if so,
// returns the index of the NEW file. Else it
// returns -1.
static int hasMoved(String fileName){
int i = 0;
for (i = 0; i < movedFiles.length; i=i+2){
if (movedFiles[i].equals(fileName)){
return i+1;
}
}
return -1;
}
}
Could someone point me in the right direction to doing this correctly?
Thank you!
Okay, I figured it out.
It was because I was trying to define the entire "URL" in the 301 response messages.
So it should have been:
responseHeader = responseHeader + "Location: /" + movedFiles[hasMoved(fileName)] + CarrLine;
Instead of:
responseHeader = responseHeader + "Location: localhost:9012/" + movedFiles[hasMoved(fileName)] + CarrLine;

HTTP Proxy Server Java error

As an assignment, I am allowed to use ServerSocket and Socket class only. Also it should be single-threaded as well.
I'm implementing a HTTP proxy server in Java, first it fetches request from client and then pushes to server, and then pushes the response back to the client.
The problem
The problem is, I have successfully get the request, send it to the end-server and get the proper HTTP response. I also can do print out the response in console. But it got stuck when I send the response to clientServer.outputstream. Firefox (requested to use, HTTP 1.0, no keep-alive requested) seems to load forever and nothing shows, and no response Firefox received from my program as well.
What I inspect when debug
Everytime a page start to load (FF request), there are always 2 client sockets. First socket contains null request, and second socket contains proper request. What I expect was that only one proper HTTP request from Firefox. Is that a weird behavior?
example:
/0:0:0:0:0:0:0:1:65194
[null request]
/0:0:0:0:0:0:0:1:65195
GET http://www.microsoft.com/ HTTP/1.0
Host: www.microsoft.com
User-Agent: Mozilla/5.0 (Windows NT 6.2; WOW64; rv:15.0) Gecko/20100101 Firefox/15.0.1
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
Connection: close
Proxy-Connection: close
Cookie: viewkey=lightweight; WT_FPC=id=269eb0e7618962f93a81347585923074:lv=1349229942007:ss=1349229580158; WT_NVR_RU=0=technet|msdn:1=:2=; omniID=c736269c_f430_4e9b_a42a_23a0c965c60a; MUID=212A1766CFE761423CD014BDCBE76158&TUID=1; MC1=GUID=08600fba7f5c5f409e67980d8a027593&HASH=ba0f&LV=20129&V=4&LU=1347643534618; A=I&I=AxUFAAAAAADGBwAA8ezRtqBBHjk3++mP1Bwj9w!!&V=4&CS=119EQ5002j10100; msdn=L=en-US
Code
ServerSocket serverSocket;
try {
serverSocket = new ServerSocket(60000);
while (true) {
clientSocket = serverSocket.accept();
[...]
// Extract request, and push to end-server
// Fetch response from end-server to client, using flush() already
// Close all input, output
// Close all sockets
} catch {[...]}
Any help is welcomed, thank you!
Full code as requested, I use PrintWriter, but before that using Byte makes no difference (not care efficiency)
import java.io.*;
import java.net.*;
import java.util.*;
public class Proxy {
static String separator = System.getProperty("line.separator");
public static void main(String args[]) {
//int port = Integer.parseInt(args[0]);
start(60000);
}
public static void start(int port) {
ServerSocket serverSocket;
try {
serverSocket = new ServerSocket(port);
Socket clientSocket = null;
while (true) {
clientSocket = serverSocket.accept();
System.out.println(clientSocket.getRemoteSocketAddress() + "\n" + clientSocket.getLocalSocketAddress() + "\n" + clientSocket.getInetAddress());
BufferedReader inStreamFromClient = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
String inLine;
Vector<String> clientRequestHeader = new Vector<String>();
String rawRequest = "";
while ((inLine = inStreamFromClient.readLine()) != null) {
if (!inLine.isEmpty()) {
clientRequestHeader.add(inLine);
rawRequest = rawRequest.concat(inLine + separator);
} else break;
}
while ((inLine = inStreamFromClient.readLine()) != null)
rawRequest = rawRequest.concat(inLine + separator);
System.out.println(rawRequest);
if (!rawRequest.isEmpty()) {
handleRequest(clientSocket, clientRequestHeader, rawRequest);
} else {
//clientSocket.close();
// Not sure how to handle null request
}
}
} catch (Exception e) {e.printStackTrace();}
}
public static void handleRequest(Socket clientSocket, Vector<String> clientRequestHeader, String rawRequest) {
HTTPRequest request = new HTTPRequest(clientRequestHeader, rawRequest);
try {
//System.out.println(rawRequest);
// Send request to end-server
Socket endServerSocket = new Socket(request.getHost(), 80);
PrintWriter outStreamToEndServer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(endServerSocket.getOutputStream())));
BufferedReader stringReader = new BufferedReader(new StringReader(rawRequest));
String inLine;
while ((inLine = stringReader.readLine())!= null) {
outStreamToEndServer.println(inLine);
}
outStreamToEndServer.println();
outStreamToEndServer.flush();
// Read response header from end-server
String responseHeader = "";
BufferedReader inStreamFromEndServer = new BufferedReader(new InputStreamReader(endServerSocket.getInputStream()));
while (!(inLine = inStreamFromEndServer.readLine()).isEmpty()) {
responseHeader = responseHeader.concat(inLine + separator);
}
// Send response header to client
PrintWriter outStreamToClient = new PrintWriter(new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream())));
outStreamToClient.println(responseHeader);
outStreamToClient.flush();
// Send response body to client
String responseBody = "";
while ((inLine = inStreamFromEndServer.readLine()) != null) {
responseBody = responseBody.concat(inLine + separator);
}
outStreamToClient.println(responseBody);
outStreamToClient.flush();
endServerSocket.shutdownInput();
clientSocket.shutdownOutput();
clientSocket.close();
endServerSocket.close();
//endServerSocket = null;
} catch (Exception e) {
e.printStackTrace();
}
}
}
first you should not use PrintWriter to transfer the Data, because the HTTP protocol isn't a pure text protocol the body can contain some raw data like images.
Replace your response transfer code with the code below.
InputStream in = endServerSocket.getInputStream();
OutputStream out = clientSocket.getOutputStream();
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = in.read(buffer)) != -1)
{
out.write(buffer, 0, bytesRead);
}
in.close();
out.close();
Second point, you add always as line break the
static String separator = System.getProperty("line.separator");
This is the System specific line seperator. HTTP defines for the HTTP header and for the http header and body separation the ctrl line break charaters, so change this.
static String separator = "\r\n";
With this changes you will get your response to your browser.
Last Point you should change your client request read code also, because it will not always work if you want POST some data. Sometimes this data will transfered as raw data, by example file uploads.
Good Luck

Categories