Remove clashing error prefix; Use better name for RatufaCoat ROMs.
[SquirrelJME.git] / buildSrc / src / main / java / cc / squirreljme / plugin / util / SimpleHTTPRequest.java
blob4cb7098e37eb8720fa352ff809984f6868b43043
1 // -*- Mode: Java; indent-tabs-mode: t; tab-width: 4 -*-
2 // ---------------------------------------------------------------------------
3 // SquirrelJME
4 // Copyright (C) Stephanie Gawroriski <xer@multiphasicapps.net>
5 // ---------------------------------------------------------------------------
6 // SquirrelJME is under the GNU General Public License v3+, or later.
7 // See license.mkd for licensing and copyright information.
8 // ---------------------------------------------------------------------------
10 package cc.squirreljme.plugin.util;
12 import java.io.ByteArrayOutputStream;
13 import java.io.IOException;
14 import java.io.InputStream;
15 import java.net.URI;
16 import java.nio.charset.StandardCharsets;
17 import java.util.Collections;
18 import java.util.Map;
19 import java.util.TreeMap;
20 import java.util.regex.Pattern;
22 /**
23 * Represents a HTTP request.
25 * @since 2020/06/26
27 public final class SimpleHTTPRequest
28 implements SimpleHTTPData
30 /** The HTTP method. */
31 public final SimpleHTTPMethod method;
33 /** The requested path. */
34 public final URI path;
36 /** HTTP headers. */
37 public final Map<String, String> headers;
39 /** The body of the request. */
40 private final byte[] _body;
42 /**
43 * Initializes the HTTP request.
45 * @param __method The method.
46 * @param __path The path.
47 * @param __headers The headers.
48 * @param __body The body, this is optional.
49 * @throws NullPointerException On null arguments.
50 * @since 2020/06/26
52 public SimpleHTTPRequest(SimpleHTTPMethod __method, URI __path,
53 Map<String, String> __headers, byte[] __body)
54 throws NullPointerException
56 if (__method == null || __path == null || __headers == null)
57 throw new NullPointerException("NARG");
59 // Copy everything in the map
60 Map<String, String> map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
61 map.putAll(__headers);
63 // Store all the request details
64 this.method = __method;
65 this.path = __path;
66 this.headers = Collections.<String, String>unmodifiableMap(map);
67 this._body = (__body == null ? new byte[0] : __body.clone());
70 /**
71 * {@inheritDoc}
72 * @since 2020/06/26
74 @Override
75 public final String toString()
77 return String.format(
78 "{method=%s, path=%s, headers=%s, body=%d bytes}",
79 this.method, this.path, this.headers, this._body.length);
82 /**
83 * Parses the HTTP request.
85 * This does not strictly conform to HTTP but is close enough for it to
86 * work fine.
88 * @param __in The stream to read from.
89 * @return The request.
90 * @throws IOException On read errors.
91 * @throws SimpleHTTPProtocolException If the protocol is not correct.
92 * @throws NullPointerException On null arguments.
93 * @since 2020/06/26
95 @SuppressWarnings("resource")
96 public static SimpleHTTPRequest parse(InputStream __in)
97 throws IOException, NullPointerException, SimpleHTTPProtocolException
99 if (__in == null)
100 throw new NullPointerException("NARG");
102 // HTTP requests are in the following form:
103 // GET / HTTP/1.1
104 // Host: www.example.com
105 SimpleHTTPMethod httpMethod;
106 URI httpPath;
107 Map<String, String> headers =
108 new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
109 ByteArrayOutputStream body = new ByteArrayOutputStream();
111 // Read the request line
112 String requestLine = SimpleHTTPRequest.__readLine(__in);
114 // The request line must be in 3 parts
115 String[] requestSplice = requestLine.split(Pattern.quote(" "));
116 if (requestSplice.length < 3)
117 throw new SimpleHTTPProtocolException(
118 "Invalid HTTP request: " + requestLine);
120 // Use these for later
121 httpMethod = SimpleHTTPMethod.valueOf(
122 requestSplice[0].toUpperCase());
123 httpPath = URI.create(requestSplice[1]);
125 // The following is the header data
126 for (;;)
128 String headerLine = SimpleHTTPRequest.__readLine(__in);
130 // No more headers to send
131 if (headerLine.isEmpty())
132 break;
134 // This must exist
135 int col = headerLine.indexOf(':');
136 if (col < 0)
137 throw new SimpleHTTPProtocolException(
138 "Expected colon in header: " + headerLine);
140 // Store header for later
141 headers.put(headerLine.substring(0, col).trim(),
142 headerLine.substring(col + 1).trim());
145 // The rest is just character data for the body, if there is one
146 if (httpMethod.hasRequestBody)
148 // Need to know the content length
149 String contentLengthStr = headers.get("Content-Length");
150 if (contentLengthStr == null)
151 throw new SimpleHTTPProtocolException("No Content-Length");
153 // Parse content length as value
154 int contentLength;
157 contentLength = Integer.parseInt(contentLengthStr, 10);
159 catch (NumberFormatException e)
161 throw new SimpleHTTPProtocolException(
162 "Invalid Content-Length: " + contentLengthStr, e);
165 // Read in all the data
166 for (int i = 0; i < contentLength; i++)
168 int rc = __in.read();
170 if (rc < 0)
171 throw new SimpleHTTPProtocolException("Got EOF in body");
173 body.write(rc);
177 // Build request
178 return new SimpleHTTPRequest(httpMethod, httpPath, headers,
179 body.toByteArray());
183 * Reads an input line from the stream.
185 * @param __in The stream to read from.
186 * @return The read line or {@code null} on end of file.
187 * @throws IOException On read errors.
188 * @throws SimpleHTTPProtocolException If the protocol is not correct.
189 * @throws NullPointerException On null arguments.
190 * @since 2020/06/27
192 private static String __readLine(InputStream __in)
193 throws IOException, NullPointerException, SimpleHTTPProtocolException
195 if (__in == null)
196 throw new NullPointerException("NARG");
198 // Write to the buffer
199 try (ByteArrayOutputStream baos = new ByteArrayOutputStream())
201 // Read single line
202 for (;;)
204 int rc = __in.read();
206 // For EOF just treat as the end of the data
207 if (rc < 0)
208 break;
210 // New line?
211 if (rc == '\n')
212 break;
214 // Ignore CR
215 else if (rc == '\r')
216 continue;
218 // Append to the buffer
219 baos.write(rc);
222 // Encode to UTF-8
223 return new String(baos.toByteArray(), StandardCharsets.UTF_8);