Skip to content

Commit 9f57b7c

Browse files
committed
Structure optimized. Writing docs.
1 parent a3e113d commit 9f57b7c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+585
-356
lines changed

README.md

Lines changed: 191 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,197 @@
11
# HTTP Client/Server for Java
22

3-
由Java Socket API实现的简单HTTP Client与Http Server
3+
![JDK](https://img.shields.io/badge/-JDK_17-FC801D?style=flat&logo=java&logoColor=white)
4+
![Platform](https://img.shields.io/badge/Platform-macOS_|_Windows_|_Linux-white)
5+
6+
由 Java Socket API 实现的简单 HTTP Client 与 Http Server
7+
8+
### TODO
9+
10+
- [ ] 语言
11+
- [ ] 程序运行方式
12+
- [ ] 功能点
13+
- [ ] 说明、运行截图
14+
- [ ] 关键数据结构、类说明
15+
- [ ] 名字与对应功能
416

517
## 1. 概览
618

19+
### 1.1 环境
20+
21+
本项目基于 JDK 17.0 编写,由 Maven 构造。
22+
23+
### 1.2 依赖
24+
25+
| Scope | GroupId | ArtifactId | Version |
26+
| ----- | ------------------- | ---------------------- | ---------- |
27+
| Build | `org.projectlombok` | `lombok` | `1.18.22` |
28+
| Build | `org.json` | `json` | `20220320` |
29+
| Test | `org.junit.jupiter` | `junit-jupiter-engine` | `5.8.2` |
30+
31+
### 1.3 运行方式
32+
33+
最终产品为jar文件,通过 `java -jar [NAME].jar` 运行。
34+
35+
## 2. 使用说明
36+
### 2.1 外部文件
37+
[Data](./Data/) 及其子目录会在 HttpClient 或 HttpServer 启动时被自动创建于 jar 文件所在目录,结构如下:
38+
39+
```
40+
Data
41+
├── Client
42+
│ └── Cache // Client的缓存目录
43+
└── Server
44+
├── Cache // Server的缓存目录(未使用)
45+
└── Static // Server的静态文件存放目录
46+
```
47+
48+
### 2.2 HttpServer
49+
50+
#### 执行指令
51+
52+
`java -jar /path/to/HttpServer.jar [OPTIONS]...`
53+
54+
#### 具体语法
55+
56+
```
57+
SYNOPSIS
58+
~ [-p <PORT>] [--keep-alive]
59+
[-t <TIMEOUT>]
60+
61+
OPTIONS
62+
-p <PORT> Set up the server with the specified port number.
63+
The default value is 8080
64+
65+
--keep-alive Enable keep-alive.
66+
67+
-t <TIMEOUT> Socket timeout.
68+
The default value is 10000
69+
```
70+
71+
#### 启动信息
72+
73+
若运行正常,则会显示启动信息,包含:
74+
- Server 运行日志
75+
- 预设 URL Mapping 信息
76+
- 静态文件读取目录
77+
78+
如下所示:
79+
80+
```
81+
SERVER: Preset mappings:
82+
/register, methods: [POST]
83+
/status, methods: [GET]
84+
/login, methods: [GET]
85+
/logout, methods: [GET]
86+
/test, methods: [GET]
87+
/missing, methods: [GET,POST]
88+
/moved, methods: [GET]
89+
SERVER: Reading static files from: [file:///.../Data/Server/Static]
90+
SERVER: The server is now running
91+
```
92+
93+
94+
### 2.3 HttpClient
95+
96+
#### 执行指令
97+
98+
`java -jar /path/to/HttpClient.jar <URL> [OPTIONS]...`
99+
100+
#### 具体语法
101+
102+
```
103+
SYNOPSIS
104+
~ <URL>
105+
[-m <METHOD>] [--keep-alive] [-b <text>]
106+
[-h <headers>...]
107+
108+
URL
109+
Using the generic URI syntax of:
110+
http://<HOSTNAME>[:PORT][/PATH][?QUERY]
111+
112+
The default value of the port number is 80.
113+
Only support HTTP protocol (not HTTPS).
114+
115+
OPTIONS
116+
-m <METHOD> Send with the specified web method.
117+
Only supports GET and POST.
118+
The default value is GET.
119+
120+
--keep-alive Enable keep-alive.ß
121+
122+
-b <text> Plain text body.
123+
124+
-h <header>... Send with the specified headers.
125+
Syntax: <key>:<value>
126+
e.g.: User-Agent:AbaAba/0.1
127+
```
128+
129+
#### 执行信息
130+
131+
若执行正常,则会显示
132+
- Client 执行日志
133+
- HTTP Request Message 原信息
134+
- HTTP Response Message 原信息
135+
136+
其中原信息包含:
137+
- Startline
138+
- Headers
139+
- Body
140+
- 若为 `text/plain` 则直接显示
141+
- 否则存储在[缓存目录](./Data/Client/Cache/)中,显示文件路径
142+
143+
如下所示:
144+
145+
```
146+
CLIENT: Client has connect to the host
147+
SOCKET[localhost127.0.0.1:8080]: Message sent 0.097656 KB
148+
149+
>> ==================== HTTP Request Message ==================== <<
150+
>> GET /test HTTP/1.1
151+
>> Accept: */*
152+
>> User-Agent: Wget/1.21.3
153+
>> Host: localhost
154+
>> Accept-Encoding: gzip
155+
>>
156+
CLIENT: Status code received: 200
157+
CLIENT: Handle returned directly...
158+
CLIENT: Request complete
159+
160+
<< ==================== HTTP Response Message ==================== >>
161+
<< HTTP/1.1 200 OK
162+
<< date: Tue, 24 May 2022 03:54:16 GMT
163+
<< server: nju_ic
164+
<< content-encoding: gzip
165+
<< connection: keep-alive
166+
<< content-type: text/plain; charset=UTF-8
167+
<<
168+
<< You got the place!!!
169+
```
170+
*直接显示 Body 内容*
171+
172+
```
173+
>> ==================== HTTP Request Message ==================== <<
174+
>> GET /OS/2022/ HTTP/1.1
175+
>> Accept: */*
176+
>> User-Agent: Wget/1.21.3
177+
>> If-Modified-Since: Sun, 22 May 2022 11:13:51 GMT
178+
>> Host: jyywiki.cn
179+
>> Accept-Encoding: gzip
180+
>>
181+
CLIENT: Status code received: 200
182+
CLIENT: Handle returned directly...
183+
CLIENT: Request complete
7184
185+
<< ==================== HTTP Response Message ==================== >>
186+
<< HTTP/1.1 200 OK
187+
<< date: Tue, 24 May 2022 03:56:37 GMT
188+
<< server: nginx/1.18.0 (Ubuntu)
189+
<< content-encoding: gzip
190+
<< content-type: text/html; charset=utf-8
191+
<< connection: keep-alive
192+
<< Content-Length: 16876
193+
<<
194+
<< Body saved at:
195+
<< file:///.../Data/Client/Cache/jyywiki.cn/OS/2022/index/cache.html
196+
```
197+
*显示 Body 存储路径*

src/main/java/client/ClientDriver.java renamed to src/main/java/edu/nju/http/client/ClientDriver.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
package client;
1+
package edu.nju.http.client;
22

3-
import exception.InvalidCommandException;
4-
import message.consts.WebMethods;
5-
import util.ArgIterator;
3+
import edu.nju.http.exception.InvalidCommandException;
4+
import edu.nju.http.message.consts.WebMethods;
5+
import edu.nju.http.util.ArgIterator;
66

77
import java.net.URI;
8-
import java.util.ArrayList;
98
import java.util.Arrays;
109
import java.util.Locale;
1110

src/main/java/client/HttpClient.java renamed to src/main/java/edu/nju/http/client/HttpClient.java

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,29 @@
1-
package client;
1+
package edu.nju.http.client;
22

3-
import exception.InvalidMessageException;
3+
import edu.nju.http.exception.InvalidMessageException;
44
import lombok.Getter;
5-
import message.HttpRequestMessage;
6-
import message.HttpResponseMessage;
7-
import util.Config;
8-
import message.HttpMessage;
9-
import util.Log;
10-
import message.consts.Headers;
11-
import message.consts.WebMethods;
12-
import message.packer.MessagePacker;
13-
import message.parser.MessageParser;
5+
import edu.nju.http.message.HttpRequestMessage;
6+
import edu.nju.http.message.HttpResponseMessage;
7+
import edu.nju.http.util.Config;
8+
import edu.nju.http.message.HttpMessage;
9+
import edu.nju.http.util.Log;
10+
import edu.nju.http.message.consts.Headers;
11+
import edu.nju.http.message.consts.WebMethods;
12+
import edu.nju.http.message.packer.MessagePacker;
13+
import edu.nju.http.message.parser.MessageParser;
1414

1515
import java.io.IOException;
1616
import java.net.InetSocketAddress;
1717
import java.net.URI;
1818
import java.net.URISyntaxException;
1919
import java.nio.channels.AsynchronousSocketChannel;
2020
import java.nio.file.Path;
21+
import java.util.Arrays;
2122
import java.util.Objects;
2223
import java.util.concurrent.ExecutionException;
2324
import java.util.concurrent.Future;
2425
import java.util.concurrent.TimeoutException;
26+
import java.util.stream.Collectors;
2527

2628
public class HttpClient {
2729
private final
@@ -35,7 +37,7 @@ public class HttpClient {
3537
@Getter private final
3638
boolean keepAlive;
3739

38-
private
40+
private final
3941
Future<Void> connectedFuture;
4042
private
4143
boolean connected;
@@ -123,7 +125,11 @@ HttpResponseMessage request(String method, String rawUri, String body, String ..
123125
this.body = body;
124126
this.headers = headers;
125127

128+
if (this.rawPath == null || this.rawPath.length() == 0)
129+
this.rawPath = "/";
130+
126131
String pathNQuery = this.rawPath;
132+
127133
if (query != null
128134
&& (WebMethods.GET.equals(method)
129135
| (WebMethods.POST.equals(method) && body != null)))
@@ -233,21 +239,27 @@ public String present(HttpMessage hrm) {
233239
Config.CLIENT_CACHE, getHostName() + this.getRawPath()))
234240
.toString();
235241

236-
sb.append("file:///").append(p);
242+
sb.append("file://").append(p);
237243
}
238244
sb.append("\n");
239245
}
240246

241247
return sb.toString();
242248
}
243249

250+
public String decorate(String s, String mark) {
251+
return Arrays.stream(s.split("\n"))
252+
.map(ss -> mark + ss)
253+
.collect(Collectors.joining("\n"));
254+
}
255+
244256
public String present(HttpRequestMessage hrm) {
245257
return "\n>> ==================== HTTP Request Message ==================== <<\n" +
246-
present((HttpMessage) hrm);
258+
decorate(present((HttpMessage) hrm), ">> ");
247259
}
248260

249261
public String present(HttpResponseMessage hrm) {
250262
return "\n<< ==================== HTTP Response Message ==================== >>\n" +
251-
present((HttpMessage) hrm);
263+
decorate(present((HttpMessage) hrm), "<< ");
252264
}
253265
}

src/main/java/client/StatusHandler.java renamed to src/main/java/edu/nju/http/client/StatusHandler.java

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
package client;
1+
package edu.nju.http.client;
22

3-
import exception.InvalidMessageException;
4-
import message.HttpResponseMessage;
5-
import util.Config;
6-
import util.Log;
7-
import message.consts.Headers;
3+
import edu.nju.http.exception.InvalidMessageException;
4+
import edu.nju.http.message.HttpResponseMessage;
5+
import edu.nju.http.message.consts.Headers;
6+
import edu.nju.http.util.Config;
7+
import edu.nju.http.util.Log;
88

99
import java.io.IOException;
1010
import java.lang.reflect.InvocationTargetException;
@@ -16,14 +16,16 @@
1616
public class StatusHandler {
1717
private static final StatusHandler instance = new StatusHandler();
1818

19-
private StatusHandler() {}
19+
private StatusHandler() {
20+
}
2021

2122
public static StatusHandler getInstance() {
2223
return instance;
2324
}
2425

2526
/**
2627
* Perform the operation according to the status code
28+
*
2729
* @return New response message after the operation, or the original one.
2830
*/
2931
public HttpResponseMessage handle(HttpClient client, HttpResponseMessage msg) throws IOException {
@@ -66,7 +68,7 @@ private HttpResponseMessage redirect(HttpClient client, HttpResponseMessage msg)
6668
} catch (IOException e) {
6769
Log.logClient("fall back to long connection disabled");
6870
nextClient = new HttpClient(client.getHostName(), client.getHostPort(), client.isKeepAlive());
69-
return redirect(nextUri, client, nextClient,msg);
71+
return redirect(nextUri, client, nextClient, msg);
7072
}
7173
}
7274

src/main/java/exception/InvalidCommandException.java renamed to src/main/java/edu/nju/http/exception/InvalidCommandException.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package exception;
1+
package edu.nju.http.exception;
22

33
public class InvalidCommandException extends Exception {
44
public InvalidCommandException(String message) {

src/main/java/exception/InvalidMessageException.java renamed to src/main/java/edu/nju/http/exception/InvalidMessageException.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
package exception;
1+
package edu.nju.http.exception;
22

3-
import util.Log;
3+
import edu.nju.http.util.Log;
44

55
import java.nio.channels.AsynchronousSocketChannel;
66
import java.util.Arrays;

0 commit comments

Comments
 (0)