4040
4141import com .sun .net .httpserver .*;
4242
43- import robaho .net .httpserver .websockets .WebSocketHandler ;
44-
4543class ExchangeImpl {
4644
4745 Headers reqHdrs , rspHdrs ;
@@ -69,7 +67,8 @@ class ExchangeImpl {
6967
7068 private static final String HEAD = "HEAD" ;
7169 private static final String CONNECT = "CONNECT" ;
72-
70+ private static final String HEADER_CONNECTION = "Connection" ;
71+ private static final String HEADER_CONNECTION_UPGRADE = "Upgrade" ;
7372 /*
7473 * streams which take care of the HTTP protocol framing
7574 * and are passed up to higher layers
@@ -85,7 +84,7 @@ class ExchangeImpl {
8584 Map <String , Object > attributes ;
8685 int rcode = -1 ;
8786 HttpPrincipal principal ;
88- final boolean websocket ;
87+ boolean connectionUpgraded = false ;
8988
9089 ExchangeImpl (
9190 String m , URI u , Request req , long len , HttpConnection connection ) throws IOException {
@@ -97,11 +96,6 @@ class ExchangeImpl {
9796 this .method = m ;
9897 this .uri = u ;
9998 this .connection = connection ;
100- this .websocket = WebSocketHandler .isWebsocketRequested (this .reqHdrs );
101- if (this .websocket ) {
102- // length is indeterminate
103- len = -1 ;
104- }
10599 this .reqContentLen = len ;
106100 /* ros only used for headers, body written directly to stream */
107101 this .ros = req .outputStream ();
@@ -135,6 +129,9 @@ private boolean isHeadRequest() {
135129 private boolean isConnectRequest () {
136130 return CONNECT .equals (getRequestMethod ());
137131 }
132+ private boolean isUpgradeRequest () {
133+ return HEADER_CONNECTION_UPGRADE .equalsIgnoreCase (reqHdrs .getFirst (HEADER_CONNECTION ));
134+ }
138135
139136 public void close () {
140137 if (closed ) {
@@ -170,7 +167,7 @@ public InputStream getRequestBody() {
170167 if (uis != null ) {
171168 return uis ;
172169 }
173- if (websocket || isConnectRequest ()) {
170+ if (connectionUpgraded || isConnectRequest () || isUpgradeRequest ()) {
174171 // connection cannot be re-used
175172 uis = ris ;
176173 } else if (reqContentLen == -1L ) {
@@ -232,7 +229,6 @@ public void sendResponseHeaders(int rCode, long contentLen)
232229 ros .write (statusLine .getBytes (ISO_CHARSET ));
233230 boolean noContentToSend = false ; // assume there is content
234231 boolean noContentLengthHeader = false ; // must not send Content-length is set
235- rspHdrs .set ("Date" , ActivityTimer .dateAndTime ());
236232
237233 Integer bufferSize = (Integer )this .getAttribute (Attributes .SOCKET_WRITE_BUFFER );
238234 if (bufferSize !=null ) {
@@ -242,19 +238,21 @@ public void sendResponseHeaders(int rCode, long contentLen)
242238 boolean flush = false ;
243239
244240 /* check for response type that is not allowed to send a body */
245- if (rCode == 101 ) {
246- logger .log (Level .DEBUG , () -> "switching protocols" );
247-
248- if (contentLen != 0 ) {
249- String msg = "sendResponseHeaders: rCode = " + rCode
250- + ": forcing contentLen = 0" ;
251- logger .log (Level .WARNING , msg );
252- }
253- contentLen = 0 ;
254- flush = true ;
255-
256- } else if ((rCode >= 100 && rCode < 200 ) /* informational */
257- || (rCode == 204 ) /* no content */
241+ var informational = rCode >= 100 && rCode < 200 ;
242+
243+ if (informational ) {
244+ if (rCode == 101 ) {
245+ logger .log (Level .DEBUG , () -> "switching protocols" );
246+ if (contentLen != 0 ) {
247+ String msg = "sendResponseHeaders: rCode = " + rCode
248+ + ": forcing contentLen = 0" ;
249+ logger .log (Level .WARNING , msg );
250+ contentLen = 0 ;
251+ }
252+ connectionUpgraded = true ;
253+ }
254+ noContentLengthHeader = true ; // the Content-length header must not be set for interim responses as they cannot have a body
255+ } else if ((rCode == 204 ) /* no content */
258256 || (rCode == 304 )) /* not modified */
259257 {
260258 if (contentLen != -1 ) {
@@ -266,6 +264,10 @@ public void sendResponseHeaders(int rCode, long contentLen)
266264 noContentLengthHeader = (rCode != 304 );
267265 }
268266
267+ if (!informational ) {
268+ rspHdrs .set ("Date" , ActivityTimer .dateAndTime ());
269+ }
270+
269271 if (isHeadRequest () || rCode == 304 ) {
270272 /*
271273 * HEAD requests or 304 responses should not set a content length by passing it
@@ -278,14 +280,16 @@ public void sendResponseHeaders(int rCode, long contentLen)
278280 noContentToSend = true ;
279281 contentLen = 0 ;
280282 o .setWrappedStream (new FixedLengthOutputStream (this , ros , contentLen ));
281- } else { /* not a HEAD request or 304 response */
283+ } else if (informational && !connectionUpgraded ) {
284+ // don't want to set the stream for 1xx responses, except 101, the handler must call sendResponseHeaders again with the final code
285+ flush = true ;
286+ } else if (connectionUpgraded || isConnectRequest ()) {
287+ o .setWrappedStream (ros );
288+ close = true ;
289+ flush = true ;
290+ } else { /* standard response with possible response data */
282291 if (contentLen == 0 ) {
283- if (websocket || isConnectRequest ()) {
284- o .setWrappedStream (ros );
285- close = true ;
286- flush = true ;
287- }
288- else if (http10 ) {
292+ if (http10 ) {
289293 o .setWrappedStream (new UndefLengthOutputStream (this , ros ));
290294 close = true ;
291295 } else {
@@ -323,9 +327,9 @@ else if (http10) {
323327
324328 writeHeaders (rspHdrs , ros );
325329 this .rspContentLen = contentLen ;
326- sentHeaders = true ;
330+ sentHeaders = ! informational ;
327331 if (logger .isLoggable (Level .TRACE )) {
328- logger .log (Level .TRACE , "Sent headers: noContentToSend=" + noContentToSend );
332+ logger .log (Level .TRACE , "sendResponseHeaders(), code=" + rCode + ", noContentToSend=" + noContentToSend + ", contentLen=" + contentLen );
329333 }
330334 if (flush ) {
331335 ros .flush ();
0 commit comments