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 package org.apache.hc.core5.http2.impl;
29
30 import java.net.URISyntaxException;
31 import java.util.ArrayList;
32 import java.util.Iterator;
33 import java.util.List;
34 import java.util.Locale;
35
36 import org.apache.hc.core5.http.Header;
37 import org.apache.hc.core5.http.HttpException;
38 import org.apache.hc.core5.http.HttpHeaders;
39 import org.apache.hc.core5.http.HttpRequest;
40 import org.apache.hc.core5.http.HttpVersion;
41 import org.apache.hc.core5.http.Method;
42 import org.apache.hc.core5.http.ProtocolException;
43 import org.apache.hc.core5.http.message.BasicHeader;
44 import org.apache.hc.core5.http.message.BasicHttpRequest;
45 import org.apache.hc.core5.http2.H2MessageConverter;
46 import org.apache.hc.core5.http2.H2PseudoRequestHeaders;
47 import org.apache.hc.core5.net.URIAuthority;
48 import org.apache.hc.core5.util.TextUtils;
49
50
51
52
53
54
55 public final class DefaultH2RequestConverter implements H2MessageConverter<HttpRequest> {
56
57 public final static DefaultH2RequestConverteruestConverter.html#DefaultH2RequestConverter">DefaultH2RequestConverter INSTANCE = new DefaultH2RequestConverter();
58
59 @Override
60 public HttpRequest convert(final List<Header> headers) throws HttpException {
61 String method = null;
62 String scheme = null;
63 String authority = null;
64 String path = null;
65 final List<Header> messageHeaders = new ArrayList<>();
66
67 for (int i = 0; i < headers.size(); i++) {
68 final Header header = headers.get(i);
69 final String name = header.getName();
70 final String value = header.getValue();
71
72 for (int n = 0; n < name.length(); n++) {
73 final char ch = name.charAt(n);
74 if (Character.isAlphabetic(ch) && !Character.isLowerCase(ch)) {
75 throw new ProtocolException("Header name '%s' is invalid (header name contains uppercase characters)", name);
76 }
77 }
78
79 if (name.startsWith(":")) {
80 if (!messageHeaders.isEmpty()) {
81 throw new ProtocolException("Invalid sequence of headers (pseudo-headers must precede message headers)");
82 }
83
84 if (name.equals(H2PseudoRequestHeaders.METHOD)) {
85 if (method != null) {
86 throw new ProtocolException("Multiple '%s' request headers are illegal", name);
87 }
88 method = value;
89 } else if (name.equals(H2PseudoRequestHeaders.SCHEME)) {
90 if (scheme != null) {
91 throw new ProtocolException("Multiple '%s' request headers are illegal", name);
92 }
93 scheme = value;
94 } else if (name.equals(H2PseudoRequestHeaders.PATH)) {
95 if (path != null) {
96 throw new ProtocolException("Multiple '%s' request headers are illegal", name);
97 }
98 path = value;
99 } else if (name.equals(H2PseudoRequestHeaders.AUTHORITY)) {
100 authority = value;
101 } else {
102 throw new ProtocolException("Unsupported request header '%s'", name);
103 }
104 } else {
105 if (name.equalsIgnoreCase(HttpHeaders.CONNECTION)) {
106 throw new ProtocolException("Header '%s: %s' is illegal for HTTP/2 messages", header.getName(), header.getValue());
107 }
108 messageHeaders.add(header);
109 }
110 }
111 if (method == null) {
112 throw new ProtocolException("Mandatory request header '%s' not found", H2PseudoRequestHeaders.METHOD);
113 }
114 if (Method.CONNECT.isSame(method)) {
115 if (authority == null) {
116 throw new ProtocolException("Header '%s' is mandatory for CONNECT request", H2PseudoRequestHeaders.AUTHORITY);
117 }
118 if (scheme != null) {
119 throw new ProtocolException("Header '%s' must not be set for CONNECT request", H2PseudoRequestHeaders.SCHEME);
120 }
121 if (path != null) {
122 throw new ProtocolException("Header '%s' must not be set for CONNECT request", H2PseudoRequestHeaders.PATH);
123 }
124 } else {
125 if (scheme == null) {
126 throw new ProtocolException("Mandatory request header '%s' not found", H2PseudoRequestHeaders.SCHEME);
127 }
128 if (path == null) {
129 throw new ProtocolException("Mandatory request header '%s' not found", H2PseudoRequestHeaders.PATH);
130 }
131 }
132
133 final HttpRequest httpRequest = new BasicHttpRequest(method, path);
134 httpRequest.setVersion(HttpVersion.HTTP_2);
135 httpRequest.setScheme(scheme);
136 try {
137 httpRequest.setAuthority(URIAuthority.create(authority));
138 } catch (final URISyntaxException ex) {
139 throw new ProtocolException(ex.getMessage(), ex);
140 }
141 httpRequest.setPath(path);
142 for (int i = 0; i < messageHeaders.size(); i++) {
143 httpRequest.addHeader(messageHeaders.get(i));
144 }
145 return httpRequest;
146 }
147
148 @Override
149 public List<Header> convert(final HttpRequest message) throws HttpException {
150 if (TextUtils.isBlank(message.getMethod())) {
151 throw new ProtocolException("Request method is empty");
152 }
153 final boolean optionMethod = Method.CONNECT.name().equalsIgnoreCase(message.getMethod());
154 if (optionMethod) {
155 if (message.getAuthority() == null) {
156 throw new ProtocolException("CONNECT request authority is not set");
157 }
158 if (message.getPath() != null) {
159 throw new ProtocolException("CONNECT request path must be null");
160 }
161 } else {
162 if (TextUtils.isBlank(message.getScheme())) {
163 throw new ProtocolException("Request scheme is not set");
164 }
165 if (TextUtils.isBlank(message.getPath())) {
166 throw new ProtocolException("Request path is not set");
167 }
168 }
169 final List<Header> headers = new ArrayList<>();
170 headers.add(new BasicHeader(H2PseudoRequestHeaders.METHOD, message.getMethod(), false));
171 if (optionMethod) {
172 headers.add(new BasicHeader(H2PseudoRequestHeaders.AUTHORITY, message.getAuthority(), false));
173 } else {
174 headers.add(new BasicHeader(H2PseudoRequestHeaders.SCHEME, message.getScheme(), false));
175 if (message.getAuthority() != null) {
176 headers.add(new BasicHeader(H2PseudoRequestHeaders.AUTHORITY, message.getAuthority(), false));
177 }
178 headers.add(new BasicHeader(H2PseudoRequestHeaders.PATH, message.getPath(), false));
179 }
180
181 for (final Iterator<Header> it = message.headerIterator(); it.hasNext(); ) {
182 final Header header = it.next();
183 final String name = header.getName();
184 final String value = header.getValue();
185 if (name.startsWith(":")) {
186 throw new ProtocolException("Header name '%s' is invalid", name);
187 }
188 if (name.equalsIgnoreCase(HttpHeaders.CONNECTION)) {
189 throw new ProtocolException("Header '%s: %s' is illegal for HTTP/2 messages", name, value);
190 }
191 headers.add(new BasicHeader(name.toLowerCase(Locale.ROOT), value));
192 }
193
194 return headers;
195 }
196
197 }