java-http代理服务

java版本的http代理服务器

https://gitee.com/lixl/proxyee.git

基于socket实现

  • 使用socket转发http请求

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    //连接到目标服务器
    proxySocket = new Socket(host, port);
    proxyInput = proxySocket.getInputStream();
    proxyOutput = proxySocket.getOutputStream();
    //根据HTTP method来判断是https还是http请求
    if ("CONNECT".equalsIgnoreCase(type)) {//https先建立隧道
    clientOutput.write("HTTP/1.1 200 Connection Established\r\n\r\n".getBytes());
    clientOutput.flush();
    } else {//http直接将请求头转发
    proxyOutput.write(headStr.toString().getBytes());
    }
    //新开线程转发客户端请求至目标服务器
    new ProxyHandle(Thread.currentThread().getName(),clientInput, proxyOutput).start();
    //转发目标服务器响应至客户端
    while (true) {
    clientOutput.write(proxyInput.read());
    }
  • 访问https地址

  • 使用socket转发https请求
    创建SocketServer监听端口,根据http请求头方法如果是CONNECT就是HTTPS请求否则都为HTTP请求,
    接着根据HOST头建立代理服务器与目标服务器的连接,然后转发数据。
    HTTPS请求需要特殊处理,因为CONNECT请求并不需要转发,
    要返回一个HTTP 200的响应建立隧道,之后才进行转发。

    • http请求头

      1
      2
      3
      4
      5
      6
      7
      8
      GET /wiki/show HTTP/1.1
      Host: localhost:8080
      User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:88.0) Gecko/20100101 Firefox/88.0
      Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
      Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
      Accept-Encoding: gzip, deflate
      Connection: keep-alive
      Upgrade-Insecure-Requests: 1
    • https请求头

基于nio实现

基于netty实现

客户端配置示例

java客户端通过代理服务器访问网络

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
 package com.lixl.socket;

import org.junit.Test;

import javax.net.ssl.HttpsURLConnection;
import java.io.InputStreamReader;
import java.net.URL;

public class HttpClientApp {


/**
* 使用http代理服务器(192.168.5.41 9999)访问网站<br/>
* 测试场景:
* 当前机器不能直接访问 my.oschina.net,需要通过5.41机器才能访问
* @throws Exception
*/
@Test
public void doHttpClientByProxy() throws Exception {
URL url = new URL("https://my.oschina.net/u/553266/blog/387722/");

/**
* 1.配置代理服务器
*/
// 方式1
System.setProperty("proxyPort", "9999");
System.setProperty("proxyHost", "192.168.5.41");
HttpsURLConnection httpsConn = (HttpsURLConnection) url.openConnection();

// 方式2
// Proxy proxy1 = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("192.168.5.41", 9999));
// HttpsURLConnection httpsConn = (HttpsURLConnection) url.openConnection(proxy1);

//某些网站会验证user-agent
httpsConn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");

InputStreamReader insr = new InputStreamReader(
httpsConn.getInputStream());

// 读取服务器的响应内容并显示
int respInt = insr.read();
while (respInt != -1) {
System.out.print((char) respInt);
respInt = insr.read();
}
insr.close();

// 断开连接
httpsConn.disconnect();
}

}

socket基于实现http请求

jmeter通过代理服务器访问网络

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
package org.shirdrn.java.communications.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
import java.util.logging.Logger;

/**
* NIO服务端
*
* @author shirdrn
*/
public class NioTcpServer extends Thread {

private static final Logger log = Logger.getLogger(NioTcpServer.class.getName());
private InetSocketAddress inetSocketAddress;
private Handler handler = new ServerHandler();

public NioTcpServer(String hostname, int port) {
inetSocketAddress = new InetSocketAddress(hostname, port);
}

@Override
public void run() {
try {
Selector selector = Selector.open(); // 打开选择器
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); // 打开通道
serverSocketChannel.configureBlocking(false); // 非阻塞
serverSocketChannel.socket().bind(inetSocketAddress);
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); // 向通道注册选择器和对应事件标识
log.info("Server: socket server started.");
while(true) { // 轮询
int nKeys = selector.select();
if(nKeys>0) {
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> it = selectedKeys.iterator();
while(it.hasNext()) {
SelectionKey key = it.next();
if(key.isAcceptable()) {
log.info("Server: SelectionKey is acceptable.");
handler.handleAccept(key);
} else if(key.isReadable()) {
log.info("Server: SelectionKey is readable.");
handler.handleRead(key);
} else if(key.isWritable()) {
log.info("Server: SelectionKey is writable.");
handler.handleWrite(key);
}
it.remove();
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}

/**
* 简单处理器接口
*
* @author shirdrn
*/
interface Handler {
/**
* 处理{@link SelectionKey#OP_ACCEPT}事件
* @param key
* @throws IOException
*/
void handleAccept(SelectionKey key) throws IOException;
/**
* 处理{@link SelectionKey#OP_READ}事件
* @param key
* @throws IOException
*/
void handleRead(SelectionKey key) throws IOException;
/**
* 处理{@link SelectionKey#OP_WRITE}事件
* @param key
* @throws IOException
*/
void handleWrite(SelectionKey key) throws IOException;
}

/**
* 服务端事件处理实现类
*
* @author shirdrn
*/
class ServerHandler implements Handler {

@Override
public void handleAccept(SelectionKey key) throws IOException {
ServerSocketChannel serverSocketChannel = (ServerSocketChannel)key.channel();
SocketChannel socketChannel = serverSocketChannel.accept();
log.info("Server: accept client socket " + socketChannel);
socketChannel.configureBlocking(false);
socketChannel.register(key.selector(), SelectionKey.OP_READ);
}

@Override
public void handleRead(SelectionKey key) throws IOException {
ByteBuffer byteBuffer = ByteBuffer.allocate(512);
SocketChannel socketChannel = (SocketChannel)key.channel();
while(true) {
int readBytes = socketChannel.read(byteBuffer);
if(readBytes>0) {
log.info("Server: readBytes = " + readBytes);
log.info("Server: data = " + new String(byteBuffer.array(), 0, readBytes));
byteBuffer.flip();
socketChannel.write(byteBuffer);
break;
}
}
socketChannel.close();
}

@Override
public void handleWrite(SelectionKey key) throws IOException {
ByteBuffer byteBuffer = (ByteBuffer) key.attachment();
byteBuffer.flip();
SocketChannel socketChannel = (SocketChannel)key.channel();
socketChannel.write(byteBuffer);
if(byteBuffer.hasRemaining()) {
key.interestOps(SelectionKey.OP_READ);
}
byteBuffer.compact();
}
}

public static void main(String[] args) {
NioTcpServer server = new NioTcpServer("localhost", 1000);
server.start();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
package org.shirdrn.java.communications.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.logging.Logger;

/**
* NIO客户端
*
* @author shirdrn
*/
public class NioTcpClient {

private static final Logger log = Logger.getLogger(NioTcpClient.class.getName());
private InetSocketAddress inetSocketAddress;

public NioTcpClient(String hostname, int port) {
inetSocketAddress = new InetSocketAddress(hostname, port);
}

/**
* 发送请求数据
* @param requestData
*/
public void send(String requestData) {
try {
SocketChannel socketChannel = SocketChannel.open(inetSocketAddress);
socketChannel.configureBlocking(false);
ByteBuffer byteBuffer = ByteBuffer.allocate(512);
socketChannel.write(ByteBuffer.wrap(requestData.getBytes()));
while (true) {
byteBuffer.clear();
int readBytes = socketChannel.read(byteBuffer);
if (readBytes > 0) {
byteBuffer.flip();
log.info("Client: readBytes = " + readBytes);
log.info("Client: data = " + new String(byteBuffer.array(), 0, readBytes));
socketChannel.close();
break;
}
}

} catch (IOException e) {
e.printStackTrace();
}
}

public static void main(String[] args) {
String hostname = "localhost";
String requestData = "Actions speak louder than words!";
int port = 1000;
new NioTcpClient(hostname, port).send(requestData);
}
}