View Javadoc
1   /*
2     (C) Copyright IBM Corp. 2005, 2013
3   
4     THIS FILE IS PROVIDED UNDER THE TERMS OF THE ECLIPSE PUBLIC LICENSE
5     ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THIS FILE
6     CONSTITUTES RECIPIENTS ACCEPTANCE OF THE AGREEMENT.
7   
8     You can obtain a current copy of the Eclipse Public License from
9     http://www.opensource.org/licenses/eclipse-1.0.php
10  
11    @author : Roberto Pineiro, IBM, roberto.pineiro@us.ibm.com
12   * @author : Chung-hao Tan, IBM, chungtan@us.ibm.com
13   * 
14   * 
15   * Change History
16   * Flag     Date        Prog         Description
17   *------------------------------------------------------------------------------- 
18   * 1535756    2006-08-07  lupusalex    Make code warning free
19   * 1565892    2006-11-28  lupusalex    Make SBLIM client JSR48 compliant
20   * 2003590    2008-06-30  blaschke-oss Change licensing from CPL to EPL
21   * 2524131    2009-01-21  raman_arora  Upgrade client to JDK 1.5 (Phase 1)
22   * 3027479    2010-07-09  blaschke-oss Dead store to local variable
23   * 3601894    2013-01-23  blaschke-oss Enhance HTTP and CIM-XML tracing
24   *    2621    2013-02-23  blaschke-oss Not all chunked input has trailers
25   *    2635    2013-05-16  blaschke-oss Slowloris DoS attack for CIM indication listener port
26   *    2655    2013-08-14  blaschke-oss Content-length must be ignored when Transfer-encoding present
27   */
28  
29  package org.metricshub.wbem.sblim.cimclient.internal.http;
30  
31  /*-
32   * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲
33   * WBEM Java Client
34   * ჻჻჻჻჻჻
35   * Copyright 2023 - 2025 MetricsHub
36   * ჻჻჻჻჻჻
37   * Licensed under the Apache License, Version 2.0 (the "License");
38   * you may not use this file except in compliance with the License.
39   * You may obtain a copy of the License at
40   *
41   *      http://www.apache.org/licenses/LICENSE-2.0
42   *
43   * Unless required by applicable law or agreed to in writing, software
44   * distributed under the License is distributed on an "AS IS" BASIS,
45   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
46   * See the License for the specific language governing permissions and
47   * limitations under the License.
48   * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱
49   */
50  
51  import java.io.IOException;
52  import java.io.InputStream;
53  import java.util.logging.Level;
54  import org.metricshub.wbem.sblim.cimclient.internal.http.io.BoundedInputStream;
55  import org.metricshub.wbem.sblim.cimclient.internal.http.io.ChunkedInputStream;
56  import org.metricshub.wbem.sblim.cimclient.internal.http.io.PersistentInputStream;
57  import org.metricshub.wbem.sblim.cimclient.internal.logging.LogAndTraceBroker;
58  
59  /**
60   * Class MessageReader is responsible for reading http messages
61   *
62   */
63  public class MessageReader {
64  	HttpHeader iHeader;
65  
66  	HttpServerMethod iMethod;
67  
68  	private boolean iChunked = false;
69  
70  	private String iEncoding = "UTF-8";
71  
72  	InputStream iContent;
73  
74  	/**
75  	 * Ctor.
76  	 *
77  	 * @param pStream
78  	 *            The input stream
79  	 * @param pTimeout
80  	 *            The timeout for reading in entire header
81  	 * @throws IOException
82  	 * @throws HttpException
83  	 */
84  	public MessageReader(InputStream pStream, int pTimeout) throws IOException, HttpException {
85  		this.iMethod = new HttpServerMethod(pStream);
86  		this.iHeader = new HttpHeader(pStream, pTimeout);
87  
88  		String encoding = this.iHeader.getField("Transfer-Encoding");
89  		if ((encoding != null) && (encoding.toLowerCase().endsWith("chunked"))) {
90  			this.iChunked = true;
91  		}
92  		String length = this.iHeader.getField("Content-Length");
93  		int contentLength = -1;
94  		if (length != null && length.length() > 0) {
95  			try {
96  				contentLength = Integer.parseInt(length);
97  			} catch (Exception e) {
98  				LogAndTraceBroker.getBroker().trace(Level.FINER, "Exception while parsing http content-length", e);
99  			}
100 		}
101 		String contentType = this.iHeader.getField("Content-Type");
102 		if (contentType != null) {
103 			try {
104 				HttpHeaderParser contentTypeHeader = new HttpHeaderParser(contentType);
105 				encoding = contentTypeHeader.getValue("charset");
106 			} catch (Exception e) {
107 				encoding = "UTF-8"; // TODO is this the default character
108 				// encoding?
109 				LogAndTraceBroker.getBroker().trace(Level.FINER, "Exception while parsing http content-type", e);
110 			}
111 			this.iEncoding = encoding;
112 		}
113 
114 		this.iContent = new PersistentInputStream(pStream, isPersistentConnectionSupported());
115 		if (this.iChunked) {
116 			this.iContent = new ChunkedInputStream(this.iContent, this.iHeader.getField("Trailer"), "Indication Request");
117 		} else if (contentLength >= 0) {
118 			this.iContent = new BoundedInputStream(this.iContent, contentLength);
119 		}
120 	}
121 
122 	/**
123 	 * Returns the character encoding
124 	 *
125 	 * @return The character encoding
126 	 */
127 	public String getCharacterEncoding() {
128 		return this.iEncoding;
129 	}
130 
131 	/**
132 	 * Returns the http header
133 	 *
134 	 * @return The http header
135 	 */
136 	public HttpHeader getHeader() {
137 		return this.iHeader;
138 	}
139 
140 	/**
141 	 * Returns the http server method
142 	 *
143 	 * @return The http server method
144 	 */
145 	public HttpServerMethod getMethod() {
146 		return this.iMethod;
147 	}
148 
149 	/**
150 	 * Returns the input stream
151 	 *
152 	 * @return The input stream
153 	 */
154 	public InputStream getInputStream() {
155 		return this.iContent;
156 	}
157 
158 	/**
159 	 * Returns the persistent connection support state
160 	 *
161 	 * @return <code>true</code> if persistent connection is supported
162 	 */
163 	public boolean isPersistentConnectionSupported() {
164 		String conn = this.iHeader.getField("Connection");
165 		if (conn != null) {
166 			if (conn.equalsIgnoreCase("close")) return false;
167 			if (conn.equalsIgnoreCase("Keep-Alive")) return true;
168 		}
169 
170 		return ((this.iMethod.getMajorVersion() >= 1) && (this.iMethod.getMinorVersion() >= 1));
171 	}
172 
173 	/**
174 	 * Returns the chunking support state
175 	 *
176 	 * @return <code>true</code> if chunking is supported
177 	 */
178 	public boolean isChunkSupported() {
179 		// TODO: make sure this is the correct way to test for chunk support
180 		if ((this.iMethod.getMajorVersion() >= 1) && (this.iMethod.getMinorVersion() >= 1)) {
181 			String TE;
182 			if ((TE = this.iHeader.getField("TE")) != null && (TE.equalsIgnoreCase("trailers"))) {
183 				return true;
184 			}
185 		}
186 		return false;
187 	}
188 
189 	/**
190 	 * Closes the stream
191 	 *
192 	 * @throws IOException
193 	 */
194 	public void close() throws IOException {
195 		this.iContent.close();
196 	}
197 }