-
Notifications
You must be signed in to change notification settings - Fork 2
http parser #4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
http parser #4
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,56 @@ | ||||||||||||||||||||||||||||||||||
| package org.example.httpparser; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| import java.io.BufferedReader; | ||||||||||||||||||||||||||||||||||
| import java.io.IOException; | ||||||||||||||||||||||||||||||||||
| import java.io.InputStream; | ||||||||||||||||||||||||||||||||||
| import java.io.InputStreamReader; | ||||||||||||||||||||||||||||||||||
| import java.util.HashMap; | ||||||||||||||||||||||||||||||||||
| import java.util.Map; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| public class HttpParser { | ||||||||||||||||||||||||||||||||||
| public HttpRequest parse(InputStream in) throws IOException { | ||||||||||||||||||||||||||||||||||
| BufferedReader reader = new BufferedReader(new InputStreamReader(in)); | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| // 1. Request Line | ||||||||||||||||||||||||||||||||||
| String requestLine = reader.readLine(); | ||||||||||||||||||||||||||||||||||
| if (requestLine == null || requestLine.isEmpty()) { | ||||||||||||||||||||||||||||||||||
| throw new IOException("The request is empty"); | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| String[] parts = requestLine.split(" "); | ||||||||||||||||||||||||||||||||||
| String method = parts[0]; | ||||||||||||||||||||||||||||||||||
| String fullPath = parts[1]; | ||||||||||||||||||||||||||||||||||
| String version = parts[2]; | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+20
to
+23
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No validation on request-line parts —
🛡️ Proposed fix String[] parts = requestLine.split(" ");
+ if (parts.length < 3) {
+ throw new IOException("Malformed request line: " + requestLine);
+ }
String method = parts[0];
String fullPath = parts[1];
String version = parts[2];📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| String path; | ||||||||||||||||||||||||||||||||||
| String query = null; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| int qIndex = fullPath.indexOf('?'); | ||||||||||||||||||||||||||||||||||
| if (qIndex >= 0) { | ||||||||||||||||||||||||||||||||||
| path = fullPath.substring(0, qIndex); | ||||||||||||||||||||||||||||||||||
| query = fullPath.substring(qIndex + 1); | ||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||
| path = fullPath; | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| // 2. Headers | ||||||||||||||||||||||||||||||||||
| Map<String, String> headers = new HashMap<>(); | ||||||||||||||||||||||||||||||||||
| String line; | ||||||||||||||||||||||||||||||||||
| while (!(line = reader.readLine()).isEmpty()) { | ||||||||||||||||||||||||||||||||||
| int colon = line.indexOf(':'); | ||||||||||||||||||||||||||||||||||
| String key = line.substring(0, colon).trim(); | ||||||||||||||||||||||||||||||||||
| String value = line.substring(colon + 1).trim(); | ||||||||||||||||||||||||||||||||||
| headers.put(key, value); | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+40
to
+43
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing colon guard — malformed header line crashes the parser. If a header line contains no 🛡️ Proposed fix int colon = line.indexOf(':');
+ if (colon < 0) {
+ throw new IOException("Malformed header: " + line);
+ }
String key = line.substring(0, colon).trim();🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+37
to
+44
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion | 🟠 Major HTTP headers are case-insensitive — Per RFC 7230, header field names are case-insensitive. A client sending ♻️ Suggested change- Map<String, String> headers = new HashMap<>();
+ Map<String, String> headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
Comment on lines
+39
to
+44
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
If the client disconnects before sending the blank line that terminates headers, 🐛 Proposed fix- while (!(line = reader.readLine()).isEmpty()) {
+ while ((line = reader.readLine()) != null && !line.isEmpty()) {🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| // 3. Body | ||||||||||||||||||||||||||||||||||
| byte[] body = new byte[0]; | ||||||||||||||||||||||||||||||||||
| if (headers.containsKey("Content-Length")) { | ||||||||||||||||||||||||||||||||||
| int length = Integer.parseInt(headers.get("Content-Lenght")); | ||||||||||||||||||||||||||||||||||
| body = in.readNBytes(length); | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+48
to
+50
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Typo: Line 48 correctly checks 🐛 Fix the typo if (headers.containsKey("Content-Length")) {
- int length = Integer.parseInt(headers.get("Content-Lenght"));
+ int length = Integer.parseInt(headers.get("Content-Length"));
body = in.readNBytes(length);
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| return new HttpRequest(method, path, query, version, headers, body); | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| package org.example.httpparser; | ||
|
|
||
| import java.util.Map; | ||
|
|
||
| public record HttpRequest( | ||
| String method, | ||
| String path, | ||
| String queryString, | ||
| String httpVersion, | ||
| Map<String, String> headers, | ||
| byte[] body | ||
| ) {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mixing
BufferedReaderand rawInputStreamreads corrupts the body.BufferedReaderreads ahead into an internal buffer (default 8 KiB). When you later callin.readNBytes(length)on the underlyingInputStream(line 50), those bytes have already been consumed by theBufferedReader, so the body read will either block, return wrong data, or return fewer bytes than expected.Read the body through the same
BufferedReader, or avoidBufferedReaderentirely and parse the request line / headers byte-by-byte from theInputStream.Also applies to: 46-51
🤖 Prompt for AI Agents